-
Notifications
You must be signed in to change notification settings - Fork 28
Adding bucketrequest controller (WIP) #7
Adding bucketrequest controller (WIP) #7
Conversation
c5ed674
to
e6dd28a
Compare
@wlan0 @jeffvance @rrati Please review. |
/test pull-container-object-storage-interface-controller-build |
2 similar comments
/test pull-container-object-storage-interface-controller-build |
/test pull-container-object-storage-interface-controller-build |
4f8836e
to
730de26
Compare
c6079f5
to
b9cf8f7
Compare
pkg/bucketrequest/bucketrequest.go
Outdated
b.bucketClient = bc | ||
} | ||
|
||
// Add creates a bucket in response to a buckerrequest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
pkg/bucketrequest/bucketrequest.go
Outdated
return "InvalidBucketClass", util.ErrStopProvision | ||
} | ||
|
||
glog.Info(logOperation("created", "bucket %q provisioned", bucket)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is success declared here? The provisioner hasn't even had a chance to see the new bucket instance yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Success is that the bucket is created but status is not available until provisioner acts on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good Question. @brahmaroutu
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't Created give the false impression that the bucket is created in the backend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Success is that the bucket is created but status is not available until provisioner acts on it.
But the log msg states that the bucket was provisioned and this is not the case.
pkg/bucketrequest/bucketrequest.go
Outdated
// Returns nil error only when the bucket was provisioned (in which case it also returns "Finished"), | ||
// a normal error when the bucket was not provisioned and provisioning should be retried (requeue the bucketRequest), | ||
// or the special errStopProvision when provisioning was impossible and no further attempts to provision should be tried. | ||
func (b *bucketRequestListener) provisionBucketRequestOperation(ctx context.Context, bucketRequest *v1alpha1.BucketRequest) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you consider the func returning (bool, error)
like we see for many of the wait funcs? Where the bool is true if the controller should not retry (sort of like done
)
b9cf8f7
to
d10e10c
Compare
/test pull-container-object-storage-interface-controller-build |
/test pull-container-object-storage-interface-controller-unit |
d10e10c
to
9bf6e28
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First batch of comments.
var cancel context.CancelFunc | ||
|
||
ctx, cancel = context.WithCancel(context.Background()) | ||
sigs := make(chan os.Signal, 1) | ||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGSEGV) | ||
|
||
go func() { | ||
s := <-sigs | ||
cancel() | ||
panic(fmt.Sprintf("%s %s", s.String(), "Signal received. Exiting")) | ||
}() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do this in init
instead of main
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved the code.
"github.com/golang/glog" | ||
) | ||
|
||
var ctx context.Context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this global I believe, see below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
SilenceErrors: true, | ||
SilenceUsage: true, | ||
RunE: func(c *cobra.Command, args []string) error { | ||
return run(args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use Command
s Context()
and pass it to run
instead: https://godoc.org/github.com/spf13/cobra#Command.Context
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Using cmd context now.
} | ||
|
||
func main() { | ||
if err := cmd.Execute(); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you move the context
setup out of init
(where IMHO it doesn't belong) into main
, you can use Command
s ExecuteContext
(https://godoc.org/github.com/spf13/cobra#Command.ExecuteContext)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
func run(args []string) error { | ||
ctrl, err := bucketcontroller.NewDefaultObjectStorageController("controller-manager", "leader-lock", 40) | ||
if err != nil { | ||
glog.Error(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both log
and glog
seem to be used throughout this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, using only blog
func run(args []string) error { | ||
ctrl, err := bucketcontroller.NewDefaultObjectStorageController("controller-manager", "leader-lock", 40) | ||
if err != nil { | ||
glog.Error(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either log the error and handle it, or don't log and return it (then, expect the upper layer to log it if appropriate).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Returning error
|
||
ctx, cancel = context.WithCancel(context.Background()) | ||
sigs := make(chan os.Signal, 1) | ||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGSEGV) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need/want to override the default handler for SIGSEGV
? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree no need to catch SIGSEGV. @wlan WDYT?
go func() { | ||
s := <-sigs | ||
cancel() | ||
panic(fmt.Sprintf("%s %s", s.String(), "Signal received. Exiting")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should this panic
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed panic, I think we just fall off the go routine. Should be fine as context is cancelled.
func (b *bucketRequestListener) InitializeKubeClient(k kubeclientset.Interface) { | ||
b.kubeClient = k | ||
} | ||
|
||
func (b *bucketRequestListener) InitializeBucketClient(bc bucketclientset.Interface) { | ||
b.bucketClient = bc | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears, on first sight, a bucketRequestListener
can't reasonably be used unless its kubeClient
and bucketClient
are set up. As such, instead of having these Initialize*
methods, how about requiring a kubeclientset.Interface
and bucketclientset.Interface
to be passed to the 'constructor' (NewListener
)? Less API, less possibility of 'doing something wrong'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the object require the client and some may not need. Controller listener interface is built generically. https://github.com/kubernetes-sigs/container-object-storage-interface-api/blob/43539346a903848b3f17aa0f4d41cd827d0070e1/controller/interfaces.go#L16
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remains an odd API which can easily be mis-used. All of a sudden a consumer needs to know what any method call requires to be set in order to 'sufficiently properly' initialize an instance.
// - the bucketRequest is already there if previous status was ProvisioningInBackground. | ||
// - the bucketRequest is not there if if previous status was "Finished". | ||
} | ||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if err != nil
and status != "Finished"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworked this code to return and process just error.
pkg/bucketrequest/bucketrequest.go
Outdated
glog.V(1).Infof("add called for bucket %s", obj.Name) | ||
bucketRequest := obj | ||
status, err := b.provisionBucketRequestOperation(ctx, bucketRequest) | ||
if err == nil || status == "Finished" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finished
should likely be a const
somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code reworked as explained above.
pkg/bucketrequest/bucketrequest.go
Outdated
Name: bucketRequest.Name, | ||
Namespace: bucketRequest.Namespace, | ||
UID: bucketRequest.ObjectMeta.UID} | ||
bucket.Spec.AllowedNamespaces = util.CopyStrings(bucketClass.AllowedNamespaces) //could use k8s util/slice |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of these could ...
comments, either do so (in final version of the code), or remove the comment 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the comments.
pkg/bucketrequest/bucketrequest.go
Outdated
} | ||
if err == nil || apierrs.IsAlreadyExists(err) { | ||
glog.V(5).Infof("Bucket %s saved", bucket.Name) | ||
return "exists", nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these should likely be const
s, or even a custom type and an enumeration of all possible values (also consts). Not plain string
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworked the code.
return client, kubeClient, ctrl | ||
} | ||
|
||
func GetBuckets(ctx context.Context, client bucketclientset.Interface, numExpected int) *types.BucketList { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is likely only used in tests? Could use a comment at least to explain what the function does, how it behaves.
return false | ||
} | ||
|
||
func GetBucketAccesses(ctx context.Context, client bucketclientset.Interface, numExpected int) *types.BucketAccessList { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
return false | ||
} | ||
|
||
func DeleteObjects(ctx context.Context, client bucketclientset.Interface, objs ...interface{}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd keep some more specific defer
s in the test code rather than a catch-all like this. Seems a bit error-prone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this way code is less verbose in the test and test is more readable. I agree user need to remember all objects to delete whether he choses to call this multiple time with multiple defer calls or at once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's 'magic'. Go code should be explicit and simple, no 'magic'. But fair enough, matter of taste so no real objection.
} | ||
|
||
func getCRDClient() (apiextensions.CustomResourceDefinitionInterface, error) { | ||
config, err := func() (*rest.Config, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documented that this is used by unit testing only.
return crdClientset.CustomResourceDefinitions(), err | ||
} | ||
|
||
func RegisterCRDs(ctx context.Context, client apiextensions.CustomResourceDefinitionInterface) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd argue these should be set up/managed in a test environment/cluster by the test automation, not by the test(s) themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, will be reworked once we have a proper setup mechanism.
9bf6e28
to
af06dd8
Compare
af06dd8
to
0078523
Compare
I have minor nits only. We can take this in and address minor nits in subsequent PRs |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: brahmaroutu, wlan0 The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
var cancel context.CancelFunc | ||
|
||
_, cancel = context.WithCancel(cmd.Context()) | ||
sigs := make(chan os.Signal, 1) | ||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) | ||
|
||
go func() { | ||
<-sigs | ||
cancel() | ||
}() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of this comes after we ran cmd.Execute
, so it seems unused whatsoever?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you are saying that this code should go before cmd.Execute then we will have to create the context like we did before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to create it, but it shouldn't be a global, can remain within main
😃 Something like the following (from memory):
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Just in case
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigs
cancel()
}()
if err := cmd.ExecuteContext(ctx); err != nil {
glog.Fatal(err.Error())
}
}
|
||
var cancel context.CancelFunc | ||
|
||
_, cancel = context.WithCancel(cmd.Context()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cmd
would not 'have' a Context
here since we never set it. beforehand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default cmd gets context.Background() right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, though that's a bit of a 'fallback' in cobra
. I think being explicit here (also see snippet above) is preferable.
This code adds basic bucket request Add functionality.
A controller, pkg util to support unit and functional testing.