From 02697de50bde6656fb298063d7108c724b18b637 Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Fri, 29 Dec 2023 16:05:29 +0100 Subject: [PATCH 01/10] hack: implement prowjob-generator prowjob-generator allows to generate the periodic and presubmit configuration files in test-infra from a configuration file which simplifies and automates introducing and chaning tests for branches. --- .github/ISSUE_TEMPLATE/kubernetes_bump.md | 36 ++--- Makefile | 17 +++ hack/boilerplate/boilerplate.py | 6 +- hack/tools/prowjob-gen/README.md | 68 +++++++++ hack/tools/prowjob-gen/config.go | 56 ++++++++ hack/tools/prowjob-gen/generator.go | 164 ++++++++++++++++++++++ hack/tools/prowjob-gen/main.go | 72 ++++++++++ hack/tools/prowjob-gen/main_test.go | 30 ++++ hack/tools/prowjob-gen/test/test.yaml.tpl | 0 9 files changed, 432 insertions(+), 17 deletions(-) create mode 100644 hack/tools/prowjob-gen/README.md create mode 100644 hack/tools/prowjob-gen/config.go create mode 100644 hack/tools/prowjob-gen/generator.go create mode 100644 hack/tools/prowjob-gen/main.go create mode 100644 hack/tools/prowjob-gen/main_test.go create mode 100644 hack/tools/prowjob-gen/test/test.yaml.tpl diff --git a/.github/ISSUE_TEMPLATE/kubernetes_bump.md b/.github/ISSUE_TEMPLATE/kubernetes_bump.md index 9b65aec8d365..a6ea6b646253 100644 --- a/.github/ISSUE_TEMPLATE/kubernetes_bump.md +++ b/.github/ISSUE_TEMPLATE/kubernetes_bump.md @@ -34,21 +34,25 @@ changes should be cherry-picked to all release series that will support the new * Note: Only bump for Cluster API versions that will support the new Kubernetes release. * Prior art: #9160 * [ ] Ensure the jobs are adjusted to provide test coverage according to our [support policy](https://cluster-api.sigs.k8s.io/reference/versions.html#supported-kubernetes-versions): - * For the main branch: - * periodics: - * Drop the oldest upgrade job as the oldest Kubernetes minor version is now out of support. - * Add new upgrade job which upgrades from the previous to the new Kubernetes version. - * periodics & presubmits: - * Bump `KUBERNETES_VERSION_MANAGEMENT` of the `e2e-mink8s` job to the new minimum supported management cluster version. - * Bump `KUBEBUILDER_ENVTEST_KUBERNETES_VERSION` of the `test-mink8s` jobs to the new minimum supported management cluster version. - * Adjust the `-latest` upgrade job to upgrade from the new Kubernetes to the next Kubernetes version. - * For the release branch of the latest supported Cluster API minor release: - * periodics & presubmits: - * Adust the `-latest` upgrade jobs to upgrade to the new Kubernetes version instead of latest. - * Note: Also check if `ETCD_VERSION_UPGRADE_TO` or `COREDNS_VERSION_UPGRADE_TO` needs to change for the upgrades jobs to the new or next Kubernetes version. - * For etcd, see the `DefaultEtcdVersion` kubeadm constant: [e.g. for v1.28.0](https://github.com/kubernetes/kubernetes/blob/v1.28.0/cmd/kubeadm/app/constants/constants.go#L308) - * For coredns, see the `CoreDNSVersion` kubeadm constant:[e.g. for v1.28.0](https://github.com/kubernetes/kubernetes/blob/v1.28.0/cmd/kubeadm/app/constants/constants.go#L344) - * Prior art: https://github.com/kubernetes/test-infra/pull/30347 https://github.com/kubernetes/test-infra/pull/30406 https://github.com/kubernetes/test-infra/pull/30407 + + * At the `.versions` section in the `cluster-api-prowjob-gen.yaml` file in [test-infra](https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes-sigs/cluster-api/): + * Add a new entry for the new Kubernetes version + * Adjust the released kKubernetes's version entry to refer `stable-1.` instead of `ci/latest-1.` + * Check and update the versions for the keys `etcd` and `coreDNS` if necessary: + * For etcd, see the `DefaultEtcdVersion` kubeadm constant: [e.g. for v1.28.0](https://github.com/kubernetes/kubernetes/blob/v1.28.0/cmd/kubeadm/app/constants/constants.go#L308) + * For coredns, see the `CoreDNSVersion` kubeadm constant:[e.g. for v1.28.0](https://github.com/kubernetes/kubernetes/blob/v1.28.0/cmd/kubeadm/app/constants/constants.go#L344) + * For the `.branches.main` section in the `cluster-api-prowjob-gen.yaml` file in [test-infra](https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes-sigs/cluster-api/): + * For the `.upgrades` section: + * Drop the oldest upgrade + * Add a new upgrade entry from the previous to the new Kubernetes version + * Bump the version set at `.kubernetesVersionManagement` to the new minimum supported management cluster version (This is the image version available as kind image). + * Bump the version set at `.kubebuilderEnvtestKubernetesVersion` to the new minimum supported management cluster version. + * Run `make generate-test-infra-prowjobs` to generate the resulting prowjob configuration: + + ```sh + TEST_INFRA_DIR=../../k8s.io/test-infra make generate-test-infra-prowjobs + ``` + * [ ] Update book: * Update supported versions in `versions.md` * Update job documentation in `jobs.md` @@ -65,7 +69,7 @@ need them in older releases as they are not necessary to manage workload cluster run the Cluster API controllers on the new Kubernetes version. * [ ] Ensure there is a new controller-runtime minor release which uses the new Kubernetes Go dependencies. -* [ ] Update our Prow jobs for the `main` branch to use the correct `kubekins-e2e` image +* [ ] Update our Prow jobs for the `main` branch to use the correct `kubekins-e2e` image via the configuration file and by running `make generate-test-infra-prowjobs`. * It is recommended to have one PR for presubmit and one for periodic jobs to reduce the risk of breaking the periodic jobs. * Prior art: presubmit jobs: https://github.com/kubernetes/test-infra/pull/27311 * Prior art: periodic jobs: https://github.com/kubernetes/test-infra/pull/27326 diff --git a/Makefile b/Makefile index cb0425f3e350..e830ea893d69 100644 --- a/Makefile +++ b/Makefile @@ -190,6 +190,9 @@ OPENAPI_GEN_BIN := openapi-gen OPENAPI_GEN := $(abspath $(TOOLS_BIN_DIR)/$(OPENAPI_GEN_BIN)) OPENAPI_GEN_PKG := k8s.io/kube-openapi/cmd/openapi-gen +PROWJOB_GEN_BIN := prowjob-gen +PROWJOB_GEN := $(abspath $(TOOLS_BIN_DIR)/$(PROWJOB_GEN_BIN)) + RUNTIME_OPENAPI_GEN_BIN := runtime-openapi-gen RUNTIME_OPENAPI_GEN := $(abspath $(TOOLS_BIN_DIR)/$(RUNTIME_OPENAPI_GEN_BIN)) @@ -600,6 +603,13 @@ generate-diagrams-book: ## Generate diagrams for *.plantuml files in book generate-diagrams-proposals: ## Generate diagrams for *.plantuml files in proposals docker run -v $(ROOT_DIR)/$(DOCS_DIR):/$(DOCS_DIR)$(DOCKER_VOL_OPTS) plantuml/plantuml:$(PLANTUML_VER) /$(DOCS_DIR)/proposals/**/*.plantuml +.PHONY: generate-test-infra-prowjobs +generate-test-infra-prowjobs: $(PROWJOB_GEN) ## Generates the prowjob configurations in test-infra + @if [ -z "${TEST_INFRA_DIR}" ]; then echo "TEST_INFRA_DIR is not set"; exit 1; fi + $(PROWJOB_GEN) \ + -config "$(TEST_INFRA_DIR)/config/jobs/kubernetes-sigs/cluster-api/cluster-api-prowjob-gen.yaml" \ + -templates-dir "$(TEST_INFRA_DIR)/config/jobs/kubernetes-sigs/cluster-api/templates" \ + -output-dir "$(TEST_INFRA_DIR)/config/jobs/kubernetes-sigs/cluster-api" ## -------------------------------------- ## Lint / Verify @@ -1307,6 +1317,9 @@ $(OPENAPI_GEN_BIN): $(OPENAPI_GEN) ## Build a local copy of openapi-gen. .PHONY: $(RUNTIME_OPENAPI_GEN_BIN) $(RUNTIME_OPENAPI_GEN_BIN): $(RUNTIME_OPENAPI_GEN) ## Build a local copy of runtime-openapi-gen. +.PHONY: $(PROWJOB_GEN_BIN) +$(PROWJOB_GEN_BIN): $(PROWJOB_GEN) ## Build a local copy of prowjob-gen. + .PHONY: $(CONVERSION_VERIFIER_BIN) $(CONVERSION_VERIFIER_BIN): $(CONVERSION_VERIFIER) ## Build a local copy of conversion-verifier. @@ -1367,6 +1380,10 @@ $(OPENAPI_GEN): # Build openapi-gen from tools folder. $(RUNTIME_OPENAPI_GEN): $(TOOLS_DIR)/go.mod # Build openapi-gen from tools folder. cd $(TOOLS_DIR); go build -tags=tools -o $(BIN_DIR)/$(RUNTIME_OPENAPI_GEN_BIN) sigs.k8s.io/cluster-api/hack/tools/runtime-openapi-gen +.PHONY: $(PROWJOB_GEN) +$(PROWJOB_GEN): $(TOOLS_DIR)/go.mod # Build prowjob-gen from tools folder. + cd $(TOOLS_DIR); go build -tags=tools -o $(BIN_DIR)/$(PROWJOB_GEN_BIN) sigs.k8s.io/cluster-api/hack/tools/prowjob-gen + $(GOTESTSUM): # Build gotestsum from tools folder. GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOTESTSUM_PKG) $(GOTESTSUM_BIN) $(GOTESTSUM_VER) diff --git a/hack/boilerplate/boilerplate.py b/hack/boilerplate/boilerplate.py index 830fcdd3e842..7a9fb6155645 100755 --- a/hack/boilerplate/boilerplate.py +++ b/hack/boilerplate/boilerplate.py @@ -143,6 +143,7 @@ def file_passes(filename, refs, regexs): for line in difflib.unified_diff(ref, data, 'reference', filename, lineterm=''): print(line, file=verbose_out) print(file=verbose_out) + return False return True @@ -154,7 +155,10 @@ def file_extension(filename): # list all the files contain 'DO NOT EDIT', but are not generated skipped_ungenerated_files = [ - 'hack/lib/swagger.sh', 'hack/boilerplate/boilerplate.py'] + 'hack/lib/swagger.sh', + 'hack/boilerplate/boilerplate.py', + '/hack/tools/prowjob-gen/generator.go', + ] def normalize_files(files): newfiles = [] diff --git a/hack/tools/prowjob-gen/README.md b/hack/tools/prowjob-gen/README.md new file mode 100644 index 000000000000..5333aa130616 --- /dev/null +++ b/hack/tools/prowjob-gen/README.md @@ -0,0 +1,68 @@ +# prowjob-gen + +Prowjob-gen is a tool which helps generating prowjob configuration. + +## Usage + +Flags: + +```txt + -config string + Path to the config file + -output-dir string + Path to the directory to create the files in + -templates-dir string + Path to the directory containing the template files referenced inside the config file +``` + +When running prowjob-gen, all flags need to be provided. +The tool then will iterate over all templates defined in the config file and execute them per configured branch. + +The configuration file is supposed to be in yaml format and to be stored inside the [test-infra](https://github.com/kubernetes/test-infra) +repository, we have to make sure it is not getting parsed as configuration for prow. +Because of that the top-level key for the configuration file is `prow-ignored:`. + +A sample configuration looks as follows: + +```yaml +prow_ignored: + branches: + main: # values below the branch here are available in the template + kubekinsImage: "gcr.io/k8s-staging-test-infra/kubekins-e2e:v20231208-8b9fd88e88-1.29" + interval: "2h" + kubernetesVersionManagement: "v1.26.6@sha256:6e2d8b28a5b601defe327b98bd1c2d1930b49e5d8c512e1895099e4504007adb" + kubebuilderEnvtestKubernetesVersion: "1.26.1" + upgrades: + - from: "1.29" + to: "1.30" + + templates: + - name: "cluster-api-periodics.yaml.tpl" + format: "cluster-api-periodics-%s.yaml" + + versions: + "1.29": + etcd: "3.5.10-0" + coreDNS: "v1.11.1" + k8sRelease: "stable-1.29" + "1.30": + etcd: "3.5.10-0" + coreDNS: "v1.11.1" + k8sRelease: "ci/latest-1.30" +``` + +With this configuration, the template `cluster-api-periodics.yaml.tpl` would get executed for each branch. +In this example we only configure the `main` branch which results in the output file `cluster-api-periodics-main.yaml`. + +When executing a template, the following functions are available as addition to the standard functions in go templates: + +- `TrimPrefix`: [strings.TrimPrefix](https://pkg.go.dev/strings#TrimPrefix) +- `TrimSuffix`: [strings.TrimSuffix](https://pkg.go.dev/strings#TrimSuffix) +- `ReplaceAll`: [strings.ReplaceAll](https://pkg.go.dev/strings#ReplaceAll) +- `last`: `func(any) any`: returns the last element of an array or slice. + +When executing a template, the following variables are available: + +- `branch`: The branch name the file get's templated for (The key in `.prow_ignored.branches`). +- `config`: The branch's configuration from `.prow_ignored.branches.`. +- `versions`: The versions mapper from `.prow_ignored.versions`. diff --git a/hack/tools/prowjob-gen/config.go b/hack/tools/prowjob-gen/config.go new file mode 100644 index 000000000000..6a083f425262 --- /dev/null +++ b/hack/tools/prowjob-gen/config.go @@ -0,0 +1,56 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +// ProwIgnoredConfig is the top-level configuration struct. Because we want to +// store the configuration in test-infra as yaml file, we have to prevent prow +// from trying to parse our configuration as prow configuration. Prow provides +// the well-known `prow_ignored` key which is not parsed further by Prow. +type ProwIgnoredConfig struct { + ProwIgnored Config `json:"prow_ignored"` +} + +// Config is the configuration file struct. +type Config struct { + Branches map[string]BranchConfig `json:"branches"` + Templates []Template `json:"templates"` + VersionsMapper VersionsMapper `json:"versions"` +} + +// BranchConfig is the branch-based configuration struct. +type BranchConfig struct { + Interval string `json:"interval"` + KubekinsImage string `json:"kubekinsImage"` + KubernetesVersionManagement string `json:"kubernetesVersionManagement"` + KubebuilderEnvtestKubernetesVersion string `json:"kubebuilderEnvtestKubernetesVersion"` + Upgrades []*Upgrade `json:"upgrades"` +} + +// Template refers a template file and defines the target file name format. +type Template struct { + Format string `json:"format"` + Name string `json:"name"` +} + +// Upgrade describes a kubernetes upgrade. +type Upgrade struct { + From string `json:"from"` + To string `json:"to"` +} + +// VersionsMapper provides key value pairs for a parent key. +type VersionsMapper map[string]map[string]string diff --git a/hack/tools/prowjob-gen/generator.go b/hack/tools/prowjob-gen/generator.go new file mode 100644 index 000000000000..ed2666db06ed --- /dev/null +++ b/hack/tools/prowjob-gen/generator.go @@ -0,0 +1,164 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "bytes" + "fmt" + "os" + "path" + "path/filepath" + "reflect" + "strings" + "text/template" + + "github.com/pkg/errors" + "k8s.io/klog/v2" +) + +const generatedFileHeader = "# Code generated by cluster-api's prowjob-gen. DO NOT EDIT.\n" + +// newGenerator initializes a generator which includes parsing the configured templates. +func newGenerator(config Config, templatesDir, outputDir string) (*generator, error) { + g := &generator{ + config: config, + outputDir: outputDir, + createdFiles: map[string]bool{}, + } + + var err error + g.templates, err = template.New(""). + Funcs(g.templateFunctions()). + ParseGlob(templatesDir + "/*.yaml.tpl") + if err != nil { + return nil, err + } + + return g, err +} + +type generator struct { + templates *template.Template + config Config + outputDir string + createdFiles map[string]bool +} + +// generate executes every template for every branch and writes the result to a +// file in outputDir. +func (g *generator) generate() error { + for _, tpl := range g.config.Templates { + for branch := range g.config.Branches { + out, err := g.executeTemplate(branch, tpl.Name) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return errors.Wrapf(err, "Generating prowjobs for template %s", tpl.Name) + } + + fileName := fmt.Sprintf(tpl.Format, strings.ReplaceAll(branch, ".", "-")) + filePath := filepath.Clean(path.Join(g.outputDir, fileName)) + if err := os.WriteFile(filePath, out.Bytes(), 0644); err != nil { //nolint:gosec + return errors.Wrapf(err, "Writing prowjob to %q", filePath) + } + + g.createdFiles[fileName] = true + } + } + return nil +} + +// cleanup deletes files which have the generatedFileHeader and had not been updated +// during generate. +func (g *generator) cleanup() error { + entries, err := os.ReadDir(g.outputDir) + if err != nil { + return err + } + + for _, entry := range entries { + if _, ok := g.createdFiles[entry.Name()]; ok { + continue + } + + if entry.IsDir() { + continue + } + + path := filepath.Clean(path.Join(g.outputDir, entry.Name())) + data, err := os.ReadFile(path) + if err != nil { + return err + } + + if strings.HasPrefix(string(data), generatedFileHeader) { + klog.Infof("Deleting file %s", entry.Name()) + if err := os.Remove(path); err != nil { + return err + } + } + } + + return nil +} + +// executeTemplate executes a previously parsed template with the data for a specific branch. +func (g *generator) executeTemplate(branch, templateName string) (*bytes.Buffer, error) { + klog.Infof("executing template %q for branch %q", templateName, branch) + + data := map[string]interface{}{ + "branch": branch, + "config": g.config.Branches[branch], + "versions": g.config.VersionsMapper, + } + + var out bytes.Buffer + + // Write yaml comment as header to indicate this file got generated. + out.WriteString(generatedFileHeader) + + if err := g.templates.ExecuteTemplate(&out, templateName, data); err != nil { + return nil, errors.Wrapf(err, "Executing template %q for branch %q", templateName, branch) + } + + return &out, nil +} + +// templateFunctions returns the functions available inside of templates. +func (g *generator) templateFunctions() template.FuncMap { + funcs := map[string]any{} + funcs["TrimPrefix"] = strings.TrimPrefix + funcs["TrimSuffix"] = strings.TrimSuffix + funcs["ReplaceAll"] = strings.ReplaceAll + funcs["last"] = last + return funcs +} + +func last(list any) any { + tp := reflect.TypeOf(list).Kind() + switch tp { + case reflect.Slice, reflect.Array: + l2 := reflect.ValueOf(list) + + l := l2.Len() + if l == 0 { + return nil + } + + return l2.Index(l - 1).Interface() + default: + panic(fmt.Sprintf("Cannot find last on type %s", tp)) + } +} diff --git a/hack/tools/prowjob-gen/main.go b/hack/tools/prowjob-gen/main.go new file mode 100644 index 000000000000..8f631538d27c --- /dev/null +++ b/hack/tools/prowjob-gen/main.go @@ -0,0 +1,72 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// main is the main package for prowjob-gen. +package main + +import ( + "flag" + "os" + + "k8s.io/klog/v2" + "sigs.k8s.io/yaml" +) + +var ( + configFile = flag.String("config", "", "Path to the config file") + outputDir = flag.String("output-dir", "", "Path to the directory to create the files in") + templatesDir = flag.String("templates-dir", "", "Path to the directory containing the template files referenced inside the config file") +) + +func main() { + // Parse flags and validate input. + flag.Parse() + if *configFile == "" { + klog.Fatal("Expected flag \"config\" to be set") + } + if *outputDir == "" { + klog.Fatal("Expected flag \"output-dir\" to be set") + } + if *templatesDir == "" { + klog.Fatal("Expected flag \"templates-dir\" to be set") + } + + // Read and Unmarshal the configuration file. + rawConfig, err := os.ReadFile(*configFile) + if err != nil { + klog.Fatalf("Failed to read config file %q: %v", *configFile, err) + } + prowIgnoredConfig := ProwIgnoredConfig{} + if err := yaml.Unmarshal(rawConfig, &prowIgnoredConfig); err != nil { + klog.Fatalf("Failed to parse config file %q: %v", *configFile, err) + } + + // Initialize a generator using the config data. + g, err := newGenerator(prowIgnoredConfig.ProwIgnored, *templatesDir, *outputDir) + if err != nil { + klog.Fatalf("Failed to initialize generator: %v", err) + } + + // Generate new files. + if err := g.generate(); err != nil { + klog.Fatalf("Failed to generate prowjobs: %v", err) + } + + // Cleanup old files which did not get updated. + if err := g.cleanup(); err != nil { + klog.Fatalf("Failed to cleanup old generated files: %v", err) + } +} diff --git a/hack/tools/prowjob-gen/main_test.go b/hack/tools/prowjob-gen/main_test.go new file mode 100644 index 000000000000..a1caaa0be601 --- /dev/null +++ b/hack/tools/prowjob-gen/main_test.go @@ -0,0 +1,30 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// main is the main package for prowjob-gen. +package main + +import ( + "testing" +) + +func Test_newGenerator(t *testing.T) { + _, err := newGenerator(Config{}, "test", "") + if err != nil { + t.Errorf("newGenerator() error = %v", err) + return + } +} diff --git a/hack/tools/prowjob-gen/test/test.yaml.tpl b/hack/tools/prowjob-gen/test/test.yaml.tpl new file mode 100644 index 000000000000..e69de29bb2d1 From e1ccaea314c08bd886b12f787e10f792a12f06ff Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Tue, 16 Jan 2024 19:11:43 +0100 Subject: [PATCH 02/10] fixes --- .github/ISSUE_TEMPLATE/kubernetes_bump.md | 2 +- docs/release/release-tasks.md | 23 +++----- hack/boilerplate/boilerplate.py | 4 +- hack/tools/prowjob-gen/README.md | 4 +- hack/tools/prowjob-gen/config.go | 15 +++--- hack/tools/prowjob-gen/generator.go | 66 +++++++++++++++-------- hack/tools/prowjob-gen/main.go | 10 ++-- 7 files changed, 72 insertions(+), 52 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/kubernetes_bump.md b/.github/ISSUE_TEMPLATE/kubernetes_bump.md index a6ea6b646253..9834458bef4c 100644 --- a/.github/ISSUE_TEMPLATE/kubernetes_bump.md +++ b/.github/ISSUE_TEMPLATE/kubernetes_bump.md @@ -37,7 +37,7 @@ changes should be cherry-picked to all release series that will support the new * At the `.versions` section in the `cluster-api-prowjob-gen.yaml` file in [test-infra](https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes-sigs/cluster-api/): * Add a new entry for the new Kubernetes version - * Adjust the released kKubernetes's version entry to refer `stable-1.` instead of `ci/latest-1.` + * Adjust the released Kubernetes's version entry to refer `stable-1.` instead of `ci/latest-1.` * Check and update the versions for the keys `etcd` and `coreDNS` if necessary: * For etcd, see the `DefaultEtcdVersion` kubeadm constant: [e.g. for v1.28.0](https://github.com/kubernetes/kubernetes/blob/v1.28.0/cmd/kubeadm/app/constants/constants.go#L308) * For coredns, see the `CoreDNSVersion` kubeadm constant:[e.g. for v1.28.0](https://github.com/kubernetes/kubernetes/blob/v1.28.0/cmd/kubeadm/app/constants/constants.go#L344) diff --git a/docs/release/release-tasks.md b/docs/release/release-tasks.md index 23436b13c96e..00f142b6c2a4 100644 --- a/docs/release/release-tasks.md +++ b/docs/release/release-tasks.md @@ -477,22 +477,15 @@ The goal of this task is to have test coverage for the new release branch and re While we add test coverage for the new release branch we will also drop the tests for old release branches if necessary. 1. Create new jobs based on the jobs running against our `main` branch: - 1. Copy `test-infra/config/jobs/kubernetes-sigs/cluster-api/cluster-api-periodics-main.yaml` to `config/jobs/kubernetes-sigs/cluster-api/cluster-api-periodics-release-1-6.yaml`. - 2. Copy `test-infra/config/jobs/kubernetes-sigs/cluster-api/cluster-api-periodics-main-upgrades.yaml` to `test-infra/config/jobs/kubernetes-sigs/cluster-api/cluster-api-periodics-release-1-6-upgrades.yaml`. - 3. Copy `test-infra/config/jobs/kubernetes-sigs/cluster-api/cluster-api-presubmits-main.yaml` to `test-infra/config/jobs/kubernetes-sigs/cluster-api/cluster-api-presubmits-release-1-6.yaml`. - 4. Modify the following: - 1. Rename the jobs, e.g.: `periodic-cluster-api-test-main` => `periodic-cluster-api-test-release-1-6`. - 2. Change `annotations.testgrid-dashboards` to `sig-cluster-lifecycle-cluster-api-1.6`. - 3. Change `annotations.testgrid-tab-name`, e.g. `capi-test-main` => `capi-test-release-1-6`. - 4. For periodics additionally: - * Change `extra_refs[].base_ref` to `release-1.6` (for repo: `cluster-api`). - * Change interval (let's use the same as for `1.5`). - 5. For presubmits additionally: Adjust branches: `^main$` => `^release-1.6$`. + 1. Copy the `main` branch entry as `release-1.6` in the `cluster-api-prowjob-gen.yaml` file in [test-infra](https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes-sigs/cluster-api/). + 2. Modify the following at the `release-1.6` branch entry: + * Change intervals (let's use the same as for `release-1.5`). 2. Create a new dashboard for the new branch in: `test-infra/config/testgrids/kubernetes/sig-cluster-lifecycle/config.yaml` (`dashboard_groups` and `dashboards`). -3. Remove tests from the [test-infra](https://github.com/kubernetes/test-infra) repository for old release branches according to our policy documented in [Support and guarantees](../../CONTRIBUTING.md#support-and-guarantees). For example, let's assume we just created tests for v1.6, then we can now drop test coverage for the release-1.3 branch. -4. Verify the jobs and dashboards a day later by taking a look at: `https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api-1.6` -5. Update `.github/workflows/weekly-security-scan.yaml` - to setup Trivy and govulncheck scanning - `.github/workflows/weekly-md-link-check.yaml` - to setup link checking in the CAPI book - and `.github/workflows/weekly-test-release.yaml` - to verify the release target is working - for the currently supported branches. -6. Update the [PR markdown link checker](https://github.com/kubernetes-sigs/cluster-api/blob/main/.github/workflows/pr-md-link-check.yaml) accordingly (e.g. `main` -> `release-1.6`). +3. Remove old release branches and unused versions from the `cluster-api-prowjob-gen.yaml` file in [test-infra](https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes-sigs/cluster-api/) according to our policy documented in [Support and guarantees](../../CONTRIBUTING.md#support-and-guarantees). For example, let's assume we just added `release-1.6`, then we can now drop test coverage for the `release-1.3` branch. +4. Regenerate the probjob configuration using `make generate-test-infra-prowjobs`. +5. Verify the jobs and dashboards a day later by taking a look at: `https://testgrid.k8s.io/sig-cluster-lifecycle-cluster-api-1.6` +6. Update `.github/workflows/weekly-security-scan.yaml` - to setup Trivy and govulncheck scanning - `.github/workflows/weekly-md-link-check.yaml` - to setup link checking in the CAPI book - and `.github/workflows/weekly-test-release.yaml` - to verify the release target is working - for the currently supported branches. +7. Update the [PR markdown link checker](https://github.com/kubernetes-sigs/cluster-api/blob/main/.github/workflows/pr-md-link-check.yaml) accordingly (e.g. `main` -> `release-1.6`).
Prior art: [Update branch for link checker](https://github.com/kubernetes-sigs/cluster-api/pull/9206) diff --git a/hack/boilerplate/boilerplate.py b/hack/boilerplate/boilerplate.py index 7a9fb6155645..8c62bd4895cd 100755 --- a/hack/boilerplate/boilerplate.py +++ b/hack/boilerplate/boilerplate.py @@ -157,7 +157,9 @@ def file_extension(filename): skipped_ungenerated_files = [ 'hack/lib/swagger.sh', 'hack/boilerplate/boilerplate.py', - '/hack/tools/prowjob-gen/generator.go', + # The generator injects `DO NOT EDIT` and thus needs to get excluded to not + # get detected as false positive. + 'hack/tools/prowjob-gen/generator.go', ] def normalize_files(files): diff --git a/hack/tools/prowjob-gen/README.md b/hack/tools/prowjob-gen/README.md index 5333aa130616..325753b19372 100644 --- a/hack/tools/prowjob-gen/README.md +++ b/hack/tools/prowjob-gen/README.md @@ -38,7 +38,7 @@ prow_ignored: templates: - name: "cluster-api-periodics.yaml.tpl" - format: "cluster-api-periodics-%s.yaml" + format: "cluster-api-periodics-{{ .branch }}.yaml" versions: "1.29": @@ -63,6 +63,6 @@ When executing a template, the following functions are available as addition to When executing a template, the following variables are available: -- `branch`: The branch name the file get's templated for (The key in `.prow_ignored.branches`). +- `branch`: The branch name the file gets templated for (The key in `.prow_ignored.branches`). - `config`: The branch's configuration from `.prow_ignored.branches.`. - `versions`: The versions mapper from `.prow_ignored.versions`. diff --git a/hack/tools/prowjob-gen/config.go b/hack/tools/prowjob-gen/config.go index 6a083f425262..a637412bfd02 100644 --- a/hack/tools/prowjob-gen/config.go +++ b/hack/tools/prowjob-gen/config.go @@ -26,24 +26,25 @@ type ProwIgnoredConfig struct { // Config is the configuration file struct. type Config struct { - Branches map[string]BranchConfig `json:"branches"` - Templates []Template `json:"templates"` - VersionsMapper VersionsMapper `json:"versions"` + Branches map[string]BranchConfig `json:"branches"` + Templates []Template `json:"templates"` + Versions VersionsMapper `json:"versions"` } // BranchConfig is the branch-based configuration struct. type BranchConfig struct { Interval string `json:"interval"` - KubekinsImage string `json:"kubekinsImage"` + UpdatesInterval string `json:"updatesInterval"` + TestImage string `json:"testImage"` KubernetesVersionManagement string `json:"kubernetesVersionManagement"` KubebuilderEnvtestKubernetesVersion string `json:"kubebuilderEnvtestKubernetesVersion"` Upgrades []*Upgrade `json:"upgrades"` } -// Template refers a template file and defines the target file name format. +// Template refers a template file and defines the target file name template. type Template struct { - Format string `json:"format"` - Name string `json:"name"` + Template string `json:"template"` + Name string `json:"name"` } // Upgrade describes a kubernetes upgrade. diff --git a/hack/tools/prowjob-gen/generator.go b/hack/tools/prowjob-gen/generator.go index ed2666db06ed..e93e0cc98cd5 100644 --- a/hack/tools/prowjob-gen/generator.go +++ b/hack/tools/prowjob-gen/generator.go @@ -35,9 +35,9 @@ const generatedFileHeader = "# Code generated by cluster-api's prowjob-gen. DO N // newGenerator initializes a generator which includes parsing the configured templates. func newGenerator(config Config, templatesDir, outputDir string) (*generator, error) { g := &generator{ - config: config, - outputDir: outputDir, - createdFiles: map[string]bool{}, + config: config, + outputDir: outputDir, + nameTemplates: map[string]*template.Template{}, } var err error @@ -48,14 +48,22 @@ func newGenerator(config Config, templatesDir, outputDir string) (*generator, er return nil, err } - return g, err + for _, tpl := range config.Templates { + nameTemplate, err := template.New("").Funcs(g.templateFunctions()).Parse(tpl.Template) + if err != nil { + return nil, errors.Wrapf(err, "parsing name template %q", tpl.Template) + } + g.nameTemplates[tpl.Name] = nameTemplate + } + + return g, nil } type generator struct { - templates *template.Template - config Config - outputDir string - createdFiles map[string]bool + templates *template.Template + nameTemplates map[string]*template.Template + config Config + outputDir string } // generate executes every template for every branch and writes the result to a @@ -63,25 +71,27 @@ type generator struct { func (g *generator) generate() error { for _, tpl := range g.config.Templates { for branch := range g.config.Branches { + klog.Infof("Executing and writing template %q for branch %q", tpl.Name, branch) out, err := g.executeTemplate(branch, tpl.Name) - if err != nil && !errors.Is(err, os.ErrNotExist) { + if err != nil { return errors.Wrapf(err, "Generating prowjobs for template %s", tpl.Name) } - fileName := fmt.Sprintf(tpl.Format, strings.ReplaceAll(branch, ".", "-")) + fileName, err := g.executeNameTemplate(branch, tpl.Name) + if err != nil { + return errors.Wrapf(err, "Generating name for template %s and branch %s", tpl.Name, branch) + } filePath := filepath.Clean(path.Join(g.outputDir, fileName)) if err := os.WriteFile(filePath, out.Bytes(), 0644); err != nil { //nolint:gosec return errors.Wrapf(err, "Writing prowjob to %q", filePath) } - g.createdFiles[fileName] = true } } return nil } -// cleanup deletes files which have the generatedFileHeader and had not been updated -// during generate. +// cleanup deletes files which have the generatedFileHeader. func (g *generator) cleanup() error { entries, err := os.ReadDir(g.outputDir) if err != nil { @@ -89,10 +99,6 @@ func (g *generator) cleanup() error { } for _, entry := range entries { - if _, ok := g.createdFiles[entry.Name()]; ok { - continue - } - if entry.IsDir() { continue } @@ -116,12 +122,10 @@ func (g *generator) cleanup() error { // executeTemplate executes a previously parsed template with the data for a specific branch. func (g *generator) executeTemplate(branch, templateName string) (*bytes.Buffer, error) { - klog.Infof("executing template %q for branch %q", templateName, branch) - data := map[string]interface{}{ "branch": branch, "config": g.config.Branches[branch], - "versions": g.config.VersionsMapper, + "versions": g.config.Versions, } var out bytes.Buffer @@ -130,12 +134,27 @@ func (g *generator) executeTemplate(branch, templateName string) (*bytes.Buffer, out.WriteString(generatedFileHeader) if err := g.templates.ExecuteTemplate(&out, templateName, data); err != nil { - return nil, errors.Wrapf(err, "Executing template %q for branch %q", templateName, branch) + return nil, errors.Wrapf(err, "executing template %q for branch %q", templateName, branch) } return &out, nil } +// executeNameTemplate executes a previously parsed name template with the branch as data. +func (g *generator) executeNameTemplate(branch, templateName string) (string, error) { + data := map[string]interface{}{ + "branch": branch, + } + + var out bytes.Buffer + + if err := g.nameTemplates[templateName].Execute(&out, data); err != nil { + return "", errors.Wrapf(err, "executing name template %q for branch %q", templateName, branch) + } + + return out.String(), nil +} + // templateFunctions returns the functions available inside of templates. func (g *generator) templateFunctions() template.FuncMap { funcs := map[string]any{} @@ -143,9 +162,14 @@ func (g *generator) templateFunctions() template.FuncMap { funcs["TrimSuffix"] = strings.TrimSuffix funcs["ReplaceAll"] = strings.ReplaceAll funcs["last"] = last + // funcs["lastGeneric"] = lastGeneric return funcs } +func lastGeneric[T any](l []T) T { + return l[len(l)-1] +} + func last(list any) any { tp := reflect.TypeOf(list).Kind() switch tp { diff --git a/hack/tools/prowjob-gen/main.go b/hack/tools/prowjob-gen/main.go index 8f631538d27c..5edd9e04913d 100644 --- a/hack/tools/prowjob-gen/main.go +++ b/hack/tools/prowjob-gen/main.go @@ -60,13 +60,13 @@ func main() { klog.Fatalf("Failed to initialize generator: %v", err) } + // Cleanup old files. + if err := g.cleanup(); err != nil { + klog.Fatalf("Failed to cleanup old generated files: %v", err) + } + // Generate new files. if err := g.generate(); err != nil { klog.Fatalf("Failed to generate prowjobs: %v", err) } - - // Cleanup old files which did not get updated. - if err := g.cleanup(); err != nil { - klog.Fatalf("Failed to cleanup old generated files: %v", err) - } } From a8b297f0c40bd178e7c196ae4673d23622892bb6 Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Tue, 16 Jan 2024 19:16:58 +0100 Subject: [PATCH 03/10] fix --- hack/tools/prowjob-gen/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/tools/prowjob-gen/config.go b/hack/tools/prowjob-gen/config.go index a637412bfd02..5960fe93fd16 100644 --- a/hack/tools/prowjob-gen/config.go +++ b/hack/tools/prowjob-gen/config.go @@ -34,7 +34,7 @@ type Config struct { // BranchConfig is the branch-based configuration struct. type BranchConfig struct { Interval string `json:"interval"` - UpdatesInterval string `json:"updatesInterval"` + UpgradesInterval string `json:"upgradesInterval"` TestImage string `json:"testImage"` KubernetesVersionManagement string `json:"kubernetesVersionManagement"` KubebuilderEnvtestKubernetesVersion string `json:"kubebuilderEnvtestKubernetesVersion"` From 2acb8dfbffc23f3f6d65aa4e98ed6f66538a4a4c Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Wed, 17 Jan 2024 09:18:13 +0100 Subject: [PATCH 04/10] fix --- hack/tools/prowjob-gen/generator.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hack/tools/prowjob-gen/generator.go b/hack/tools/prowjob-gen/generator.go index e93e0cc98cd5..40677b0a0fc6 100644 --- a/hack/tools/prowjob-gen/generator.go +++ b/hack/tools/prowjob-gen/generator.go @@ -162,14 +162,9 @@ func (g *generator) templateFunctions() template.FuncMap { funcs["TrimSuffix"] = strings.TrimSuffix funcs["ReplaceAll"] = strings.ReplaceAll funcs["last"] = last - // funcs["lastGeneric"] = lastGeneric return funcs } -func lastGeneric[T any](l []T) T { - return l[len(l)-1] -} - func last(list any) any { tp := reflect.TypeOf(list).Kind() switch tp { From 7d2ee3dfcc7aecbf1c7d28332d4b45fb80ed2108 Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Wed, 17 Jan 2024 10:08:12 +0100 Subject: [PATCH 05/10] fix --- hack/tools/prowjob-gen/generator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/tools/prowjob-gen/generator.go b/hack/tools/prowjob-gen/generator.go index 40677b0a0fc6..5f2b98fa0098 100644 --- a/hack/tools/prowjob-gen/generator.go +++ b/hack/tools/prowjob-gen/generator.go @@ -157,7 +157,7 @@ func (g *generator) executeNameTemplate(branch, templateName string) (string, er // templateFunctions returns the functions available inside of templates. func (g *generator) templateFunctions() template.FuncMap { - funcs := map[string]any{} + funcs := template.FuncMap{} funcs["TrimPrefix"] = strings.TrimPrefix funcs["TrimSuffix"] = strings.TrimSuffix funcs["ReplaceAll"] = strings.ReplaceAll From 13fda3ccc23e2929c0d88171854c1516a9f2774b Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Wed, 17 Jan 2024 10:09:37 +0100 Subject: [PATCH 06/10] fix --- hack/tools/prowjob-gen/generator.go | 1 - 1 file changed, 1 deletion(-) diff --git a/hack/tools/prowjob-gen/generator.go b/hack/tools/prowjob-gen/generator.go index 5f2b98fa0098..df604f6940eb 100644 --- a/hack/tools/prowjob-gen/generator.go +++ b/hack/tools/prowjob-gen/generator.go @@ -85,7 +85,6 @@ func (g *generator) generate() error { if err := os.WriteFile(filePath, out.Bytes(), 0644); err != nil { //nolint:gosec return errors.Wrapf(err, "Writing prowjob to %q", filePath) } - } } return nil From 68242676f121f217903e673246f22be8b6e259e8 Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Wed, 17 Jan 2024 10:44:55 +0100 Subject: [PATCH 07/10] fix test --- hack/tools/prowjob-gen/generator.go | 18 +++++++++--- hack/tools/prowjob-gen/main.go | 14 +--------- hack/tools/prowjob-gen/main_test.go | 28 +++++++++++++++++-- .../prowjob-gen/test/test-configuration.yaml | 25 +++++++++++++++++ .../prowjob-gen/test/test-main.yaml.golden | 6 ++++ hack/tools/prowjob-gen/test/test.yaml.tpl | 5 ++++ 6 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 hack/tools/prowjob-gen/test/test-configuration.yaml create mode 100644 hack/tools/prowjob-gen/test/test-main.yaml.golden diff --git a/hack/tools/prowjob-gen/generator.go b/hack/tools/prowjob-gen/generator.go index df604f6940eb..420f0a542c72 100644 --- a/hack/tools/prowjob-gen/generator.go +++ b/hack/tools/prowjob-gen/generator.go @@ -28,19 +28,29 @@ import ( "github.com/pkg/errors" "k8s.io/klog/v2" + "sigs.k8s.io/yaml" ) const generatedFileHeader = "# Code generated by cluster-api's prowjob-gen. DO NOT EDIT.\n" // newGenerator initializes a generator which includes parsing the configured templates. -func newGenerator(config Config, templatesDir, outputDir string) (*generator, error) { +func newGenerator(configFile string, templatesDir, outputDir string) (*generator, error) { + // Read and Unmarshal the configuration file. + rawConfig, err := os.ReadFile(filepath.Clean(configFile)) + if err != nil { + return nil, errors.Wrapf(err, "reading config file %s", configFile) + } + prowIgnoredConfig := ProwIgnoredConfig{} + if err := yaml.UnmarshalStrict(rawConfig, &prowIgnoredConfig); err != nil { + return nil, errors.Wrapf(err, "parsing config file %s", configFile) + } + g := &generator{ - config: config, + config: prowIgnoredConfig.ProwIgnored, outputDir: outputDir, nameTemplates: map[string]*template.Template{}, } - var err error g.templates, err = template.New(""). Funcs(g.templateFunctions()). ParseGlob(templatesDir + "/*.yaml.tpl") @@ -48,7 +58,7 @@ func newGenerator(config Config, templatesDir, outputDir string) (*generator, er return nil, err } - for _, tpl := range config.Templates { + for _, tpl := range g.config.Templates { nameTemplate, err := template.New("").Funcs(g.templateFunctions()).Parse(tpl.Template) if err != nil { return nil, errors.Wrapf(err, "parsing name template %q", tpl.Template) diff --git a/hack/tools/prowjob-gen/main.go b/hack/tools/prowjob-gen/main.go index 5edd9e04913d..9611f5c65e74 100644 --- a/hack/tools/prowjob-gen/main.go +++ b/hack/tools/prowjob-gen/main.go @@ -19,10 +19,8 @@ package main import ( "flag" - "os" "k8s.io/klog/v2" - "sigs.k8s.io/yaml" ) var ( @@ -44,18 +42,8 @@ func main() { klog.Fatal("Expected flag \"templates-dir\" to be set") } - // Read and Unmarshal the configuration file. - rawConfig, err := os.ReadFile(*configFile) - if err != nil { - klog.Fatalf("Failed to read config file %q: %v", *configFile, err) - } - prowIgnoredConfig := ProwIgnoredConfig{} - if err := yaml.Unmarshal(rawConfig, &prowIgnoredConfig); err != nil { - klog.Fatalf("Failed to parse config file %q: %v", *configFile, err) - } - // Initialize a generator using the config data. - g, err := newGenerator(prowIgnoredConfig.ProwIgnored, *templatesDir, *outputDir) + g, err := newGenerator(*configFile, *templatesDir, *outputDir) if err != nil { klog.Fatalf("Failed to initialize generator: %v", err) } diff --git a/hack/tools/prowjob-gen/main_test.go b/hack/tools/prowjob-gen/main_test.go index a1caaa0be601..c026a83ddad2 100644 --- a/hack/tools/prowjob-gen/main_test.go +++ b/hack/tools/prowjob-gen/main_test.go @@ -18,13 +18,37 @@ limitations under the License. package main import ( + "os" "testing" + + "github.com/google/go-cmp/cmp" ) -func Test_newGenerator(t *testing.T) { - _, err := newGenerator(Config{}, "test", "") +func Test_Generator(t *testing.T) { + g, err := newGenerator("test/test-configuration.yaml", "test", "test") if err != nil { t.Errorf("newGenerator() error = %v", err) return } + + if err := g.generate(); err != nil { + t.Errorf("g.generate() error = %v", err) + return + } + + goldenData, err := os.ReadFile("test/test-main.yaml.golden") + if err != nil { + t.Errorf("reading golden file: %v", err) + return + } + + testData, err := os.ReadFile("test/test-main.yaml.tmp") + if err != nil { + t.Errorf("reading file generated from test: %v", err) + return + } + + if diff := cmp.Diff(string(goldenData), string(testData)); diff != "" { + t.Errorf("generated and golden test file differ, diff:\n%s", diff) + } } diff --git a/hack/tools/prowjob-gen/test/test-configuration.yaml b/hack/tools/prowjob-gen/test/test-configuration.yaml new file mode 100644 index 000000000000..3a1ae79f2a1b --- /dev/null +++ b/hack/tools/prowjob-gen/test/test-configuration.yaml @@ -0,0 +1,25 @@ +prow_ignored: + branches: + main: # values below the branch here are available in the template + testImage: "gcr.io/k8s-staging-test-infra/kubekins-e2e:v20231208-8b9fd88e88-1.29" + interval: "2h" + upgradesInterval: "2h" + kubernetesVersionManagement: "v1.26.6@sha256:6e2d8b28a5b601defe327b98bd1c2d1930b49e5d8c512e1895099e4504007adb" + kubebuilderEnvtestKubernetesVersion: "1.26.1" + upgrades: + - from: "1.29" + to: "1.30" + + templates: + - name: "test.yaml.tpl" + template: "test-{{ .branch }}.yaml.tmp" + + versions: + "1.29": + etcd: "3.5.10-0" + coreDNS: "v1.11.1" + k8sRelease: "stable-1.29" + "1.30": + etcd: "3.5.10-0" + coreDNS: "v1.11.1" + k8sRelease: "ci/latest-1.30" \ No newline at end of file diff --git a/hack/tools/prowjob-gen/test/test-main.yaml.golden b/hack/tools/prowjob-gen/test/test-main.yaml.golden new file mode 100644 index 000000000000..39baab837639 --- /dev/null +++ b/hack/tools/prowjob-gen/test/test-main.yaml.golden @@ -0,0 +1,6 @@ +# Code generated by cluster-api's prowjob-gen. DO NOT EDIT. +main +main +bar +foobar +1.29 \ No newline at end of file diff --git a/hack/tools/prowjob-gen/test/test.yaml.tpl b/hack/tools/prowjob-gen/test/test.yaml.tpl index e69de29bb2d1..451d98754b0e 100644 --- a/hack/tools/prowjob-gen/test/test.yaml.tpl +++ b/hack/tools/prowjob-gen/test/test.yaml.tpl @@ -0,0 +1,5 @@ +{{ .branch }} +{{ ReplaceAll .branch "." "-" }} +{{ TrimPrefix "foobar" "foo" }} +{{ TrimPrefix "foobar" "bar" }} +{{ (last $.config.Upgrades).From }} \ No newline at end of file From 8b73f09799f5a83af7b984836af27c846f10b2b5 Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Wed, 17 Jan 2024 10:45:52 +0100 Subject: [PATCH 08/10] fix readme --- hack/tools/prowjob-gen/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hack/tools/prowjob-gen/README.md b/hack/tools/prowjob-gen/README.md index 325753b19372..cc419e5ef4e9 100644 --- a/hack/tools/prowjob-gen/README.md +++ b/hack/tools/prowjob-gen/README.md @@ -28,8 +28,9 @@ A sample configuration looks as follows: prow_ignored: branches: main: # values below the branch here are available in the template - kubekinsImage: "gcr.io/k8s-staging-test-infra/kubekins-e2e:v20231208-8b9fd88e88-1.29" + testImage: "gcr.io/k8s-staging-test-infra/kubekins-e2e:v20231208-8b9fd88e88-1.29" interval: "2h" + upgradesInterval: "2h" kubernetesVersionManagement: "v1.26.6@sha256:6e2d8b28a5b601defe327b98bd1c2d1930b49e5d8c512e1895099e4504007adb" kubebuilderEnvtestKubernetesVersion: "1.26.1" upgrades: @@ -37,8 +38,8 @@ prow_ignored: to: "1.30" templates: - - name: "cluster-api-periodics.yaml.tpl" - format: "cluster-api-periodics-{{ .branch }}.yaml" + - name: "test.yaml.tpl" + template: "test-{{ .branch }}.yaml.tmp" versions: "1.29": From 9a26730f92b698d1629f5d7f7c691995815b0391 Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Wed, 17 Jan 2024 10:52:54 +0100 Subject: [PATCH 09/10] add test for README.md to be in sync with test/test-configuration.yaml --- hack/tools/prowjob-gen/README.md | 2 ++ hack/tools/prowjob-gen/main_test.go | 31 ++++++++++++++++++- .../prowjob-gen/test/test-configuration.yaml | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/hack/tools/prowjob-gen/README.md b/hack/tools/prowjob-gen/README.md index cc419e5ef4e9..f446c5e422f2 100644 --- a/hack/tools/prowjob-gen/README.md +++ b/hack/tools/prowjob-gen/README.md @@ -24,6 +24,7 @@ Because of that the top-level key for the configuration file is `prow-ignored:`. A sample configuration looks as follows: + ```yaml prow_ignored: branches: @@ -51,6 +52,7 @@ prow_ignored: coreDNS: "v1.11.1" k8sRelease: "ci/latest-1.30" ``` + With this configuration, the template `cluster-api-periodics.yaml.tpl` would get executed for each branch. In this example we only configure the `main` branch which results in the output file `cluster-api-periodics-main.yaml`. diff --git a/hack/tools/prowjob-gen/main_test.go b/hack/tools/prowjob-gen/main_test.go index c026a83ddad2..ed5246d8ea14 100644 --- a/hack/tools/prowjob-gen/main_test.go +++ b/hack/tools/prowjob-gen/main_test.go @@ -19,6 +19,7 @@ package main import ( "os" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -49,6 +50,34 @@ func Test_Generator(t *testing.T) { } if diff := cmp.Diff(string(goldenData), string(testData)); diff != "" { - t.Errorf("generated and golden test file differ, diff:\n%s", diff) + t.Errorf("generated and golden test file differ:\n%s", diff) + } +} + +const configFileDelimiter = "" + +func Test_testConfiguration(t *testing.T) { + rawReadme, err := os.ReadFile("README.md") + if err != nil { + t.Errorf("reading file README.md: %v", err) + return + } + + splitted := strings.Split(string(rawReadme), configFileDelimiter) + if len(splitted) != 3 { + t.Errorf("expected README.md to contain %q twice", configFileDelimiter) + } + + rawConfiguration, err := os.ReadFile("test/test-configuration.yaml") + if err != nil { + t.Errorf("reading file test/test-configuration.yaml: %v", err) + return + } + + readmeConfiguration := strings.TrimPrefix(string(rawConfiguration), "\n``yaml\n") + readmeConfiguration = strings.TrimSuffix(readmeConfiguration, "```\n") + + if diff := cmp.Diff(readmeConfiguration, string(rawConfiguration)); diff != "" { + t.Errorf("Configuration in README.md and test/test-configuration.yaml differ:\n%s", diff) } } diff --git a/hack/tools/prowjob-gen/test/test-configuration.yaml b/hack/tools/prowjob-gen/test/test-configuration.yaml index 3a1ae79f2a1b..fefb2b07be61 100644 --- a/hack/tools/prowjob-gen/test/test-configuration.yaml +++ b/hack/tools/prowjob-gen/test/test-configuration.yaml @@ -22,4 +22,4 @@ prow_ignored: "1.30": etcd: "3.5.10-0" coreDNS: "v1.11.1" - k8sRelease: "ci/latest-1.30" \ No newline at end of file + k8sRelease: "ci/latest-1.30" From ce94ad4e03b6b2adc35f0ebd59b05c182a2fe2ba Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Wed, 17 Jan 2024 10:56:10 +0100 Subject: [PATCH 10/10] generate-modules --- hack/tools/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/tools/go.mod b/hack/tools/go.mod index 8ba0167b2154..ee4d6dfb0c08 100644 --- a/hack/tools/go.mod +++ b/hack/tools/go.mod @@ -9,6 +9,7 @@ replace sigs.k8s.io/cluster-api/test => ../../test require ( cloud.google.com/go/storage v1.36.0 github.com/blang/semver/v4 v4.0.0 + github.com/google/go-cmp v0.6.0 github.com/onsi/gomega v1.30.0 github.com/pkg/errors v0.9.1 github.com/spf13/pflag v1.0.5 @@ -71,7 +72,6 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/cel-go v0.16.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v53 v53.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect