Skip to content

Conversation

@irbekrm
Copy link
Contributor

@irbekrm irbekrm commented Dec 1, 2023

Generate static kube manifests for the Tailscale operator from the Helm chart to ensure a single source of truth.

The workflow for adding new functionality for the manifests will now be:

  • add the changes to ./cmd/k8s-operator/deploy/charts
  • run go generate tailscale.com/cmd/k8s-operator to ensure the change is propagated to the static manifests
  • commit all changes

This PR also adds a CI test to ensure that the static manifests are up to date with regards to the chart.

I have tested the generated manifest by setting the client id and client secret fields of the Secret data and applying it to a cluster.

@irbekrm irbekrm changed the title cmd/k8s-operator: generate static kube manifests from Helm charts. cmd/k8s-operator: generate static kube manifests from the Helm chart. Dec 1, 2023
finalString, _ := strings.CutSuffix(final.String(), "---\n")
if err := os.WriteFile(filepath.Join(repoRoot, "cmd/k8s-operator/deploy/manifests/operator.yaml"), []byte(finalString), 0664); err != nil {
log.Fatalf("error writing new file: %v", err)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was looking for some way how to validate that the generated manifest is a valid Kubernetes manifest, however it looks like kubectl apply --dry-run still requires a running cluster and marshaling the yamls into kube types in Go would be an overkill so I think this is fine for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Would a sensible alternative be running something like kubectl-validate or kubeconform as a Github action when operator.yaml gets changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was considering adding a validating kubectl plugin like this, yes. But I was a bit reluctant to add two more tools (kubectl and the validating plugin) that are kube-specific to ./tools (more dependencies, running another third party tool in our test env). We will though need kubectl anyway for the e2e tests. Perhaps, we could first get the kubectl in in a separate PR to make it more explicit in case folks have objections to adding more tools and then look at whether we want to add more plugins. I am not particularly concerned about generating invalid yaml right now- we are not planning to make big changes to the manifests and unlike the helm templates, the static manifests are quite readable so should be easy to evaluate a change with 👀

@irbekrm irbekrm force-pushed the irbekrm/generatestaticmanifests branch from be5024e to 31a0775 Compare December 1, 2023 12:23
This is done to ensure that there is a single source of truth
for the operator kube manifests.
Also adds linux node selector to the static manifests as
this was added as a default to the Helm chart.

Static manifests can now be generated by running
go generate tailscale.com/cmd/k8s-operator.

Updates #9222

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Also ensure that the check is only ran when files in ./cmd/k8s-operator or the test have changed.

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
@irbekrm irbekrm force-pushed the irbekrm/generatestaticmanifests branch from 31a0775 to ec9faa5 Compare December 1, 2023 12:36
value: "false"
- name: PROXY_FIREWALL_MODE
value: auto
image: tailscale/k8s-operator:unstable
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably at some point we will want to hardcode this to 'latest' tag instead, but not in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And/or we might publish the manifest at a specific tag with operator and proxy tags set accordingly to Github releases

name: tailscale
---
apiVersion: v1
kind: ServiceAccount
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changes to this file are:

  • resource definitions being swapped around due to the order of the Helm templates
  • things actually being brought up to date from changes in the Helm templates:
    • node selector set to 'linux'
    • image pull policy set to 'Always'

@@ -0,0 +1,12 @@
# Tailscale Kubernetes operator deployment manifests

./cmd/k8s-operator/deploy contain various Tailscale Kubernetes operator deployment manifests.
Copy link
Contributor

Choose a reason for hiding this comment

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

Any thoughts on whether cmd/k8s-operator is the best place for these? It might not be too late to move them to a top-level directory.

Copy link
Contributor Author

@irbekrm irbekrm Dec 4, 2023

Choose a reason for hiding this comment

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

It might not be too late to move them to a top-level directory

I would first publish them to a Github releases and tell people to consume it from there, after that we can move them around in the repo as we see fit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But I want to get the charts work in first.

cmd := exec.Command("./tool/helm", "template", "operator", "./cmd/k8s-operator/deploy/chart",
"--namespace=tailscale")
cmd.Dir = repoRoot
out := bytes.NewBuffer([]byte{})
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason to create a buffer this way as opposed to declaring a variable, e.g. var out bytes.Buffer?

Copy link
Contributor Author

@irbekrm irbekrm Dec 4, 2023

Choose a reason for hiding this comment

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

It is *bytes.Buffer that implements io.Writer and var out *bytes.Buffer would get initialized to nil so would need some value assigned to it anyway, so I think out := bytes.NewBuffer([]byte{}) is preferable here
I need more coffee. Yeah could do var out bytes.Buffer

finalString, _ := strings.CutSuffix(final.String(), "---\n")
if err := os.WriteFile(filepath.Join(repoRoot, "cmd/k8s-operator/deploy/manifests/operator.yaml"), []byte(finalString), 0664); err != nil {
log.Fatalf("error writing new file: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Would a sensible alternative be running something like kubectl-validate or kubeconform as a Github action when operator.yaml gets changed?

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
metadata:
name: operator
namespace: tailscale
name: operator
Copy link
Contributor Author

Choose a reason for hiding this comment

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

gopkg.in/yaml.v3 has opinions wrt tabs vs spaces

@irbekrm irbekrm merged commit 49fd0a6 into main Dec 4, 2023
@irbekrm irbekrm deleted the irbekrm/generatestaticmanifests branch December 4, 2023 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants