-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: cleanup tekton operands #641
Conversation
Skipping CI for Draft Pull Request. |
@@ -307,3 +276,21 @@ func getTektonTasksNamespace(request *common.Request) string { | |||
} | |||
return request.Instance.Namespace | |||
} | |||
|
|||
func filterResources[PT 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.
Appreciate use of generics! Can this be simplified to just use the metav1.Object
interface? It is working right now but looks very complicated.
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, I agree that's very complicated, also there is no Go docs for it, but it works. I tried to do it with just metav1.Object
, I couldn't make it work. @akrejcir suggested to do that way yesterday #532 (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.
@akrejcir's yesterday's explanation for it:
If you would define simply [T metav1.Object] it means that parameter T is a type that implements interface metav1.Object . Which means it is a pointer to a struct, for example T = *v1.Pod.
When you have : []T it is in fact []*v1.Pod. So when a method accepts []T parameter, it does not accept []v1.Pod to it.
The PT interface {*T ; meta1.Object } is a trick. It means that type PT satisfies an anonymous typeset defined directly in the function signature. This typeset interface { *T; metav1.Object } means that it is a pointer to T and implements interface metav1.Object. Which means that for example PT = *v1.Pod .
It would be nice if golang compiler was smarter and we didn't need PT. :)
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.
Better explanation: https://stackoverflow.com/a/71444968
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.
IMO we should not merge this just because it works.
I see the pain in changing the slices to hold pointers instead. Is there another maybe less 'generic' but still prettier solution?
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 about casting to keep this simple?
func filterResources[T any](items []T, trimSuffix bool) []T {
var results []T
for _, obj := range items {
metaObj, _ := any(obj).(metav1.ObjectMeta)
name := metaObj.GetName()
if trimSuffix {
name = strings.TrimSuffix(name, "-task")
}
if _, ok := AllowedTasks[name]; ok {
results = append(results, obj)
}
}
return results
}
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.
Ah sweet, thanks @lyarwood. Let's use it then.
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.
@@ -307,3 +276,21 @@ func getTektonTasksNamespace(request *common.Request) string { | |||
} | |||
return request.Instance.Namespace | |||
} | |||
|
|||
func filterResources[PT 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.
IMO we should not merge this just because it works.
I see the pain in changing the slices to hold pointers instead. Is there another maybe less 'generic' but still prettier solution?
func filterResources[T any](items []T, trimSuffix bool) []T { | ||
var results []T | ||
for _, item := range items { | ||
object, _ := any(item).(metav1.ObjectMeta) |
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 still an unchecked cast. Please return early with empty list at least if that happens.
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've reverted back to @akrejcir's function at the moment, because this new suggested function is failing unit tests.
@0xFelix I didn't see another way, I spent too much time on it. It seems like this generics feature is kind of new, and there is not enough documentation IMO, all examples based on simple things like "int/float/string" whatever. Simpler solution would be not to use generics at all:
And just use separated functions for it. |
I still appreciate the use of generics, but I'm afraid this is making it more complicated than it needs to be. Golang can be repetitive, but at least it is easy to grok what happens? |
I'm not particularly against the use of generics, thanks for experimenting generics and pushing forward the use of them. However, I agree with @0xFelix that it may make the code a bit more hard to read. Nevertheless, I guess that's something that a good function documentation can solve. |
@@ -17,7 +17,7 @@ import ( | |||
) | |||
|
|||
// +kubebuilder:rbac:groups=tekton.dev,resources=pipelines,verbs=list;watch;create;update;delete | |||
// +kubebuilder:rbac:groups=*,resources=configmaps,verbs=list;watch;create;delete | |||
// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=list;watch;create;delete |
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.
Just out of curiosity, is there any difference between using *
or core
? AFAIK, it will refer to the same apiGroup
, isn't 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.
Yes, *
would grant permission to any API group in your cluster. Imagine that some custom API group, e.g. kubevirt.io
would contain a custom ConfigMap resource that may be accessible (e.g. some ConfigMap that contains secrets).
I see that it's also possible to just specify an empty API group, as ""
(groups=""
) and it would also refer to the core objects. Anyways, by using core
we embrace least privilege principle and readability.
From the beginning I think that It was specified as *
to speed up the development process (also some verbs) - because to have a specific value requires some effort.
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 love the explanation of that topic here [1]. Very good explanation and examples.
[1] https://learnk8s.io/rbac-kubernetes#modelling-access-to-resources
bundle := &Bundle{} | ||
for _, file := range files { | ||
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(file), 1024) | ||
defer file.Close() |
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.
An unintuitive behavior of golang is that defer
statements are executed when returning from a function, and not when a scope ends with }
, like for example a destructor in C++ would be executed. When you call defer
in a loop, all the defers will be remembered and only called on return. In this case, all the files will be closed only on return.
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.
Ack, thanks for the explanation. Good to know.
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. I'll leave that discussion open so others can learn as well.
@codingben can you please rebase this PR? |
@ksimon1 Ack, doing it. |
/cc sradco |
I think it's wrong comment, I don't think we need more reviews in this PR. |
Hi @lyarwood @0xFelix, I see a few tests failed because of:
Can you please check? it seems like it wasn't found, I'm not familiar enough with InstanceTypes resources. |
It seems like there is no CentOS 9 folder in |
|
The URL looks to be incorrect in general, because it is only referring to Preferences but not Instancetypes? |
Yeah this looks more like a race between the CR being updated and the test trying to assert that things have been reconciled?
This was intentional, we don't have a unified cluster wide resources bundle so I just went with one, IMHO the test doesn't need it either. |
Thanks for looking on it, the fact that this happened only in this PR, let's re-run the tests then? |
/retest |
/retest-required |
#693 for the common-instancetypes failure, smells like a race to me. |
Cleanup Tekton operands to improve code quality and readability. Also set specific RBAC groups to core instead of the wildcard. Signed-off-by: Ben Oukhanov <[email protected]>
/cc sradco |
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
switch kind { | ||
case tasksString: | ||
task := pipeline.Task{} | ||
err = getObject(obj, &task) | ||
if err != nil { | ||
return err | ||
} | ||
bundle.Tasks = append(bundle.Tasks, task) | ||
case pipelineKindString: | ||
p := pipeline.Pipeline{} | ||
err = getObject(obj, &p) | ||
if err != nil { | ||
return err | ||
} | ||
bundle.Pipelines = append(bundle.Pipelines, p) | ||
case serviceAccountKind: | ||
sa := v1.ServiceAccount{} | ||
err = getObject(obj, &sa) | ||
if err != nil { | ||
return err | ||
} | ||
bundle.ServiceAccounts = append(bundle.ServiceAccounts, sa) | ||
case roleBindingKind: | ||
rb := rbac.RoleBinding{} | ||
err = getObject(obj, &rb) | ||
if err != nil { | ||
return err | ||
} | ||
bundle.RoleBindings = append(bundle.RoleBindings, rb) | ||
case clusterRoleKind: | ||
cr := rbac.ClusterRole{} | ||
err = getObject(obj, &cr) | ||
if err != nil { | ||
return err | ||
} | ||
bundle.ClusterRoles = append(bundle.ClusterRoles, cr) | ||
case configMapKind: | ||
cm := v1.ConfigMap{} | ||
err = getObject(obj, &cm) | ||
if err != nil { | ||
return err | ||
} | ||
bundle.ConfigMaps = append(bundle.ConfigMaps, cm) | ||
default: | ||
continue | ||
} |
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.
Can we move the error checking to the end of the switch statement? Something like:
switch kind {
case tasksString:
task := pipeline.Task{}
err = getObject(obj, &task)
bundle.Tasks = append(bundle.Tasks, task)
[...]
}
if err != nil {
return 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.
Thanks, I'll look on it. I'd love to merge this PR firstly because it's open too much time already and too many CI issues there.
/retest |
@ksimon1 Seems like there is an issue with example Tekton pipelines again? |
/retest |
/lgtm |
What this PR does / why we need it:
Cleanup Tekton operands to improve code quality
and readability. Also set specific RBAC groups
to core instead of the wildcard.
Which issue(s) this PR fixes:
Fixes #548
Release note: