Skip to content
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

Merged
merged 1 commit into from
Oct 10, 2023
Merged

refactor: cleanup tekton operands #641

merged 1 commit into from
Oct 10, 2023

Conversation

codingben
Copy link
Member

@codingben codingben commented Aug 1, 2023

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:

None

@kubevirt-bot kubevirt-bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. release-note-none Denotes a PR that doesn't merit a release note. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. labels Aug 1, 2023
@openshift-ci
Copy link

openshift-ci bot commented Aug 1, 2023

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@@ -307,3 +276,21 @@ func getTektonTasksNamespace(request *common.Request) string {
}
return request.Instance.Namespace
}

func filterResources[PT interface {
Copy link
Member

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.

Copy link
Member Author

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).

Copy link
Member Author

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. :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

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?

Copy link
Member

@lyarwood lyarwood Aug 1, 2023

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
}

Copy link
Member Author

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.

Copy link
Member Author

@codingben codingben Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @lyarwood, this function doesn't work, unit tests are failing because of it. Currently only @akrejcir's function works now, I'll keep it to proceed with other PR comments because it takes too much time.

internal/operands/tekton-tasks/reconcile.go Show resolved Hide resolved
@@ -307,3 +276,21 @@ func getTektonTasksNamespace(request *common.Request) string {
}
return request.Instance.Namespace
}

func filterResources[PT interface {
Copy link
Member

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)
Copy link
Member

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.

Copy link
Member Author

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.

@codingben
Copy link
Member Author

codingben commented Aug 2, 2023

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?

@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:

func New(bundle *tektonbundle.Bundle) operands.Operand {
	return &tektonTasks{
		tasks:           filterTasks(bundle.Tasks),
		serviceAccounts: filterServiceAccounts(bundle.ServiceAccounts),
		roleBindings:    filterRoleBindings(bundle.RoleBindings),
		clusterRoles:    filterClusterRoles(bundle.ClusterRoles),
	}
}

And just use separated functions for it.

@0xFelix
Copy link
Member

0xFelix commented Aug 2, 2023

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?

@jcanocan
Copy link
Contributor

jcanocan commented Aug 3, 2023

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.
Apologizes in advance for my ignorance, but we need to use workarounds to make it work (are we? Right?), I'm not sure if it's a good idea. I didn't have the chance to study in deep generics in go, so if we consider this is the intended way of go to use generics, please go ahead.
Also, can we do the same with the DeepCopy functions ?

@@ -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
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member Author

@codingben codingben Aug 7, 2023

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

@codingben codingben marked this pull request as ready for review August 8, 2023 14:06
@kubevirt-bot kubevirt-bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 8, 2023
@codingben codingben marked this pull request as draft August 8, 2023 14:22
@kubevirt-bot kubevirt-bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 8, 2023
@codingben codingben marked this pull request as ready for review August 8, 2023 14:39
@kubevirt-bot kubevirt-bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 8, 2023
@codingben
Copy link
Member Author

@akrejcir All comments in issue #548 were resolved. Please review if you have some free time.

bundle := &Bundle{}
for _, file := range files {
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(file), 1024)
defer file.Close()
Copy link
Collaborator

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.

Copy link
Member Author

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.

Copy link
Member Author

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 codingben marked this pull request as draft August 8, 2023 14:55
@kubevirt-bot kubevirt-bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. size/XL and removed size/L labels Aug 8, 2023
@kubevirt-bot kubevirt-bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 30, 2023
@akrejcir akrejcir added this to the v0.19.0 milestone Sep 18, 2023
@ksimon1
Copy link
Member

ksimon1 commented Sep 25, 2023

@codingben can you please rebase this PR?

@codingben
Copy link
Member Author

@codingben can you please rebase this PR?

@ksimon1 Ack, doing it.

@kubevirt-bot kubevirt-bot removed lgtm Indicates that a PR is ready to be merged. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Oct 2, 2023
@github-actions
Copy link

github-actions bot commented Oct 2, 2023

/cc sradco

@codingben
Copy link
Member Author

/cc sradco

I think it's wrong comment, I don't think we need more reviews in this PR.

@codingben
Copy link
Member Author

Hi @lyarwood @0xFelix, I see a few tests failed because of:

�[0mCommon Instance Types �[38;5;243moperand �[38;5;9m�[1m[It] should reconcile from URL when provided�[0m
�[38;5;243m/go/src/github.com/kubevirt/ssp-operator/tests/common_instancetypes_test.go:39�[0m

  �[38;5;9m[FAILED] Expected success, but got an error:
      <*errors.StatusError | 0xc00012e640>: 
      virtualmachineclusterpreferences.instancetype.kubevirt.io "centos.9.stream.desktop" not found
      {
          ErrStatus: {
              TypeMeta: {Kind: "", APIVersion: ""},
              ListMeta: {
                  SelfLink: "",
                  ResourceVersion: "",
                  Continue: "",
                  RemainingItemCount: nil,
              },
              Status: "Failure",
              Message: "virtualmachineclusterpreferences.instancetype.kubevirt.io \"centos.9.stream.desktop\" not found",
              Reason: "NotFound",
              Details: {
                  Name: "centos.9.stream.desktop",
                  Group: "instancetype.kubevirt.io",
                  Kind: "virtualmachineclusterpreferences",
                  UID: "",
                  Causes: nil,
                  RetryAfterSeconds: 0,
              },
              Code: 404,
          },
      }�[0m
  �[38;5;9mIn �[1m[It]�[0m�[38;5;9m at: �[1m/go/src/github.com/kubevirt/ssp-operator/tests/common_instancetypes_test.go:58�[0m �[38;5;243m@ 10/02/23 11:01:11.283�[0m

Can you please check? it seems like it wasn't found, I'm not familiar enough with InstanceTypes resources.

@codingben
Copy link
Member Author

It seems like there is no CentOS 9 folder in v0.1.0: https://github.com/kubevirt/common-instancetypes/tree/v0.1.0/preferences/centos/9_stream

@0xFelix
Copy link
Member

0xFelix commented Oct 2, 2023

centos.9.stream was renamed to centos.stream9 recently, but that should not be the issue. SSP should reconcile the preferences from the URL but it looks like the cluster was not in a clean state before running this test?

@0xFelix
Copy link
Member

0xFelix commented Oct 2, 2023

The URL looks to be incorrect in general, because it is only referring to Preferences but not Instancetypes?

@lyarwood
Copy link
Member

lyarwood commented Oct 2, 2023

centos.9.stream was renamed to centos.stream9 recently, but that should not be the issue. SSP should reconcile the preferences from the URL but it looks like the cluster was not in a clean state before running this test?

Yeah this looks more like a race between the CR being updated and the test trying to assert that things have been reconciled?

The URL looks to be incorrect in general, because it is only referring to Preferences but not Instancetypes?

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.

@codingben
Copy link
Member Author

Thanks for looking on it, the fact that this happened only in this PR, let's re-run the tests then?

@codingben
Copy link
Member Author

/retest

@codingben
Copy link
Member Author

/retest-required

@lyarwood
Copy link
Member

lyarwood commented Oct 2, 2023

#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 <boukhanov@redhat.com>
@github-actions
Copy link

github-actions bot commented Oct 4, 2023

/cc sradco

@sonarcloud
Copy link

sonarcloud bot commented Oct 4, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

Comment on lines +93 to 138
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
}
Copy link
Contributor

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
}

Copy link
Member Author

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.

@codingben
Copy link
Member Author

/retest

@codingben
Copy link
Member Author

@ksimon1 Seems like there is an issue with example Tekton pipelines again?

@ksimon1
Copy link
Member

ksimon1 commented Oct 6, 2023

/retest

@codingben
Copy link
Member Author

@akrejcir @ksimon1 @0xFelix Can you please approve and merge it?

@jcanocan
Copy link
Contributor

/lgtm

@kubevirt-bot kubevirt-bot added the lgtm Indicates that a PR is ready to be merged. label Oct 10, 2023
@kubevirt-bot kubevirt-bot merged commit ef4e613 into kubevirt:main Oct 10, 2023
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. lgtm Indicates that a PR is ready to be merged. release-note-none Denotes a PR that doesn't merit a release note. size/XL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

fix: Issues left for next PR from #532
7 participants