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

ADD reconciler for pipeline and task resources - step 6 on operator-pipelines docs #13

Merged
merged 15 commits into from
Nov 9, 2021

Conversation

acmenezes
Copy link
Contributor

In a nutshell this PR solves the bare minimum on issue #5.

Added on new the dependencies.go file in package controllers:

  1. Clones the repo operator-pipelines to the tmp directory in the container.
  2. Reads and applies both pipelines and tasks found on the paths described in the instructions;
  3. Removes the repo clone from the tmp directory in the container.

OBS:
On Step 2:

The apply part is done via dynamic client-go with unstructured objects "rest mapped" to known GVKs such as pipelines and tasks that belong to tekton.dev apigroup; That requires that the openshift-pipeline operator aka tekton be installed before. This task doesn't seem to require any other to complete therefore I put it right after the pipeline operator subscription one on the resources.go file.

I'm assuming those will be deployed on the same namespace as the CR here.

The image I used for testing this on my AWS OCP environment is on my own repo for now as I don't know yet what tagging strategy for testing images I should use on opdev. You can find it here: quay.io/acmenezes/operator-certification-operator:0.0.1-2.

We may need unit tests, comments and more logging on it (maybe on the next iteration). Glad to discuss what is the level of preference on each one of those that this team prefers.

This commit implements decoding of yaml manifests into unstructured objs
for client to apply. It doesn't use the controller-runtime client instead
uses a client-go based client.

For now it uses a local test.yaml as input to be replaced with operator-pipelines
yaml manifests.
The applyManifests function is used within the reconcilePipelineDependencies to
create both pipelines and tasks from the operator-pipeline project after executing
a plain clone from the project.
This commit includes:
1 - few adjustments and constants on func reconcilePipelineDependencies
2 - kubebuilder marker with tekton.dev api group rbac permissions
3 - other changes due to make manifests commands such as role.yaml
with tekton permissions.
Tekton pipelines and tasks are being created on the same
namespace as the namespace where the CR is created.
Just by catching the meta.Namespace on the specific func
for reconciling the pipeline dependencies.
// yaml manifests that need to be applied beforehand
// ref: https://github.com/redhat-openshift-ecosystem/certification-releases/blob/main/4.9/ga/ci-pipeline.md#step-6---install-the-certification-pipeline-and-dependencies-into-the-cluster

repo := "https://github.com/redhat-openshift-ecosystem/operator-pipelines.git"
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this be a constant as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair enough. My bad. I think I passed too fast my eyes on that. I just changed it.

controllers/dependencies.go Outdated Show resolved Hide resolved
}

// Removing cloned project
if err = os.RemoveAll(PIPELINE_MANIFESTS_PATH); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think removing the manifests should be a deferred function after git.PlainClone. Otherwise, if any error encounters, the program execution will never reach here.
My other question is why we don't remove the files under TASK_MANIFEST_PATH?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's fair. My mistake. It's not PIPELINE_MANIFESTS_PATH it's the whole REPO_CLONE_PATH. And the defer is a good suggestion already done.


func (r *OperatorPipelineReconciler) applyManifests(fileName string, Namespace string) error {

b, err := ioutil.ReadFile(fileName)
Copy link
Collaborator

Choose a reason for hiding this comment

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

ioutil is going to be deprecated soon (I think). It would be better to use os package instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure about ioutil. But no problem os is now the one on the code. They have exactly the same signature.

}

// Decoding yaml files to resource objects and apply to cluster
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader(b), 100)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I was having some fun today to resolve the same issue, and I think using yaml.Unmarshal is cleaner. This is the snippet of my code to read a csv file:

	yfile, err := os.ReadFile("csv.yaml")
	if err != nil {
		log.Error(err)
	}

	var data operatorv1alpha1.ClusterServiceVersion
	err = yaml.Unmarshal(yfile, &data)

I checked the pipeline and task folders and we need to handle two CRDs (Task and Pipeline). I think this could be simplified. Please take a look at how we manage a similar situation in preflight (but it is the opposite! we try to dump go struct into yaml manifest). https://github.com/redhat-openshift-ecosystem/openshift-preflight/blob/1a2ef8a3829ea911356cd41a0171130d568379cd/certification/internal/policy/operator/deployable_by_olm.go#L378

Please let me know if we need to discuss further.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As it is it's working. Are we aiming for refactoring here? Wouldn't it be interesting having another iteration with another issue to simplify it? I would suggest that we unblock other tasks that depends on this step first and take the code refactoring and new approaches on another issue. Glad to jump in and make the changes there. What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes refactoring.. I understand this is working, but I think it is too complex to follow and some of these lines could be removed. I'd personally like to push a code that is easy to maintain the first time. The refactoring is usually more complicated as you need to have/add unit tests to ensure the refactored code is not breaking, and we don't have unit tests yet.

BTW, have you seen this?
kubernetes/client-go#193 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @samira-barouti here, I'd rather see this clearly defined structs (or partial structs) and use yaml.Unmarshal to simplify the code and make it easier to read/maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi Samira, nope. I didn't see this one. But just realized that going the generic approach with client-go was not necessary there. So I went on the specific approach which is actually way easier. I just flipped to controller runtime client and that's much better now I think. Please check it out.

@samira-barouti samira-barouti mentioned this pull request Nov 3, 2021
Copy link
Contributor

@bcrochet bcrochet left a comment

Choose a reason for hiding this comment

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

Two things...

  1. Unit tests?
  2. Please collapse all the commits. Unless a commit can stand on its own, it shouldn't be a separate commit. I see some of the individual commits have a bunch of commented out code. That definitely doesn't need to be in the repo as a part of the history.


func (r *OperatorPipelineReconciler) applyManifests(fileName string, Namespace string) error {

b, err := ioutil.ReadFile(fileName)
Copy link
Contributor

Choose a reason for hiding this comment

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

+1

Copy link
Contributor

@bcrochet bcrochet left a comment

Choose a reason for hiding this comment

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

A couple more comments.

defer r.RemovePipelineDependencyFiles(REPO_CLONE_PATH)

// Reading pipeline manifests and applying to cluster
root := REPO_CLONE_PATH + PIPELINE_MANIFESTS_PATH
Copy link
Contributor

Choose a reason for hiding this comment

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

These two blocks could be a loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you clarify? What do you mean?

Copy link
Contributor

Choose a reason for hiding this comment

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

paths := []string{PIPELINE_MANIFESTS_PATH, TASKS_MANIFESTS_PATH}
for _, path := range paths {
...
}

The blocks are identical. The only difference is the two paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

return nil
}

func (r *OperatorPipelineReconciler) RemovePipelineDependencyFiles(filePath string) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seem like it needs to be exported.

removePipelineDependencyFiles

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

@acmenezes
Copy link
Contributor Author

Two things...

  1. Unit tests?
  2. Please collapse all the commits. Unless a commit can stand on its own, it shouldn't be a separate commit. I see some of the individual commits have a bunch of commented out code. That definitely doesn't need to be in the repo as a part of the history.

@bcrochet

  1. about the unit tests I also wrote on the PR note. We didn't discuss on our first meeting how it would be done and with what libraries on this project. I suggest we take it on another issue and have it defined with everyone's participation.

  2. About the multiple commits what do you suggest here? Do you mean git rebase -i head n? Or do you apply another method to accomplish this? Sorry about my ignorance here.

@bcrochet
Copy link
Contributor

bcrochet commented Nov 3, 2021

Two things...

  1. Unit tests?
  2. Please collapse all the commits. Unless a commit can stand on its own, it shouldn't be a separate commit. I see some of the individual commits have a bunch of commented out code. That definitely doesn't need to be in the repo as a part of the history.

@bcrochet

1. about the unit tests I also wrote on the PR note. We didn't discuss on our first meeting how it would be done and with what libraries on this project. I suggest we take it on another issue and have it defined with everyone's participation.

We've used ginkgo/gomega on preflight. But we've also accepted plain old Go tests. Use whatever you're comfortable with. It's a bigger debt to have to add a test later, than to possibly migrate a test to a different framework.

2. About the multiple commits what do you suggest here? Do you mean git rebase -i head n? Or do you apply another method to accomplish this? Sorry about my ignorance here.

My typical workflow when working on a PR like this is to git commit --amend whenever I'm fixing up a PR based on feedback. But yes, a git rebase -i HEAD~n and squash them all to a single commit. It's not completely forbidden to have multiple commits in a PR, but unless a commit adds a significant contribution to the git history, it doesn't need to be a part of main, i.e. we don't need to see your development iterations in the final product. :)

1 - fileWalk on loop block
2 - Simplify code on manifest apply
@acmenezes
Copy link
Contributor Author

acmenezes commented Nov 5, 2021

Two things...

  1. Unit tests?
  2. Please collapse all the commits. Unless a commit can stand on its own, it shouldn't be a separate commit. I see some of the individual commits have a bunch of commented out code. That definitely doesn't need to be in the repo as a part of the history.

@bcrochet

1. about the unit tests I also wrote on the PR note. We didn't discuss on our first meeting how it would be done and with what libraries on this project. I suggest we take it on another issue and have it defined with everyone's participation.

We've used ginkgo/gomega on preflight. But we've also accepted plain old Go tests. Use whatever you're comfortable with. It's a bigger debt to have to add a test later, than to possibly migrate a test to a different framework.

2. About the multiple commits what do you suggest here? Do you mean git rebase -i head n? Or do you apply another method to accomplish this? Sorry about my ignorance here.

My typical workflow when working on a PR like this is to git commit --amend whenever I'm fixing up a PR based on feedback. But yes, a git rebase -i HEAD~n and squash them all to a single commit. It's not completely forbidden to have multiple commits in a PR, but unless a commit adds a significant contribution to the git history, it doesn't need to be a part of main, i.e. we don't need to see your development iterations in the final product. :)

Hi @bcrochet , so on the unit test I believe after we talked yesterday you won't need them anymore now. About the commits agreed. I pass to that practice from now on. I would suggest to squash them on merge when this PR gets approved.

@acmenezes acmenezes changed the title Resources 5 ADD reconciler for pipeline and task resources - step 6 on operator-pipelines docs Nov 8, 2021
if !info.IsDir() {
var pipeline tekton.Pipeline
var task tekton.Task
fmt.Print(path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

this seems to be a leftover from the debugging. Could be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's also done.

controllers/dependencies.go Show resolved Hide resolved
// Progress: os.Stdout,
})
if err != nil {
log.Log.Info("Couldn't clone the repository for operator-pipelines: " + err.Error())
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be log.Log.Error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

return nil
})
if err != nil {
log.Log.Info("Couldn't iterate over operator-pipelines yaml manifest files: " + err.Error())
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


func (r *OperatorPipelineReconciler) removePipelineDependencyFiles(filePath string) error {
if err := os.RemoveAll(filePath); err != nil {
log.Log.Info("Couldn't remove operator-pipelines directory")
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


b, err := os.ReadFile(fileName)
if err != nil {
log.Log.Info("Couldn't read manifest file " + err.Error())
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

controllers/dependencies.go Show resolved Hide resolved
controllers/dependencies.go Show resolved Hide resolved
mapper := restmapper.NewDiscoveryRESTMapper(gr)
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
log.Log.Info("Couldn't the preferred resource mapping for given kind.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this log missing a word?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

}

// Decoding yaml files to resource objects and apply to cluster
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader(b), 100)
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @samira-barouti here, I'd rather see this clearly defined structs (or partial structs) and use yaml.Unmarshal to simplify the code and make it easier to read/maintain.

controllers/dependencies.go Outdated Show resolved Hide resolved
log.Error(err, "Couldn't clone the repository for operator-pipelines")
return err
}
defer r.removePipelineDependencyFiles(REPO_CLONE_PATH)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think (and I might be wrong), that this method call should be place before the if statement on line 33. If cloning the repo fails in the middle, the control flow will return the error but not delete the created dir.

I overaly recommend 1) creating a tmp folder programmatically for the operator-pipelines (please check https://pkg.go.dev/os#MkdirTemp) to avoid unwanted surprises, and 2) move the defer method to line 33, after the git.PlainClone and before the if err

Copy link
Contributor Author

@acmenezes acmenezes Nov 8, 2021

Choose a reason for hiding this comment

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

2- I'm not sure as well if the defer function would get triggered there so just in case I moved it to line 33 as requested.

1- On the other hand temporary files used by programs in Linux are historically stored in the /tmp directory that happens to have, in regular systems, garbage collection in place. This is a single Linux image deployment, single process container, single user with a single purpose. It's pretty straightforward. I don't see the need to add another package/method and more complexity to address this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

No problem.

Copy link
Collaborator

@samira-barouti samira-barouti left a comment

Choose a reason for hiding this comment

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

/lgtm

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Nov 8, 2021
@jomkz jomkz merged commit 0a911ee into redhat-openshift-ecosystem:main Nov 9, 2021
@acmenezes acmenezes deleted the resources-5 branch November 30, 2021 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lgtm Indicates that a PR is ready to be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants