diff --git a/.github/ISSUE_TEMPLATE/new_release.md b/.github/ISSUE_TEMPLATE/new_release.md index 5378e7a66..0c5f9ac08 100644 --- a/.github/ISSUE_TEMPLATE/new_release.md +++ b/.github/ISSUE_TEMPLATE/new_release.md @@ -14,12 +14,12 @@ For details, see [RELEASE.md](https://github.com/kubernetes-sigs/cluster-api-pro - [ ] [When bumping `X` or `Y`] Add a new entry of new release branch to [depandabot.yml](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/.github/dependabot.yml). - [ ] [When bumping `X` or `Y`] Add a new entry of new release branch to [security-scan.yaml](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/.github/workflows/security-scan.yaml). - [ ] [When bumping `X` or `Y`] Add a new entry to [metadata.yaml](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/metadata.yaml) - as [described in the CAPI book](https://cluster-api.sigs.k8s.io/clusterctl/provider-contract.html#metadata-yaml) + as [described in the CAPI book](https://cluster-api.sigs.k8s.io/developer/providers/contracts/clusterctl#metadata-yaml) on the release branch prior to release. -- [ ] Push tag to the repository. +- [ ] Create the PR after generating release notes according to [RELEASE.md](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/RELEASE.md). Verify that the release PR looks good and make changes if necessary. When this PR is merged, release automation will push the tag to upstream and create a draft release notes. - [ ] Promote the [staging image](https://console.cloud.google.com/cloud-build/builds?project=k8s-staging-capi-openstack) by adding the new sha=>tag mapping to [images.yaml](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/images/k8s-staging-capi-openstack/images.yaml). -- [ ] Verify that the new draft release looks good and make changes if necessary. +- [ ] Verify that the new draft release looks good. - [ ] Verify that the image was promoted sucessfully. - [ ] Publish the release. Mark the release as "latest" if it is the most recent minor release. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0cb3fae9b..9b50d8007 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,7 +12,7 @@ updates: target-branch: main groups: all-github-actions: - patterns: ["*"] + patterns: [ "*" ] commit-message: prefix: ":seedling:" include: scope @@ -30,20 +30,20 @@ updates: target-branch: main groups: all-go-mod-patch-and-minor: - patterns: ["*"] - update-types: ["patch", "minor"] + patterns: [ "*" ] + update-types: [ "patch", "minor" ] commit-message: prefix: ":seedling:" include: scope ignore: # Ignore controller-runtime major and minor bumps as its upgraded manually. - dependency-name: "sigs.k8s.io/controller-runtime" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] # Ignore k8s major and minor bumps and its transitives modules - dependency-name: "k8s.io/*" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - dependency-name: "sigs.k8s.io/controller-tools" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] labels: - "area/dependency" - "ok-to-test" @@ -58,7 +58,7 @@ updates: target-branch: release-0.13 groups: all-github-actions: - patterns: ["*"] + patterns: [ "*" ] commit-message: prefix: ":seedling:" include: scope @@ -76,23 +76,23 @@ updates: target-branch: release-0.13 groups: all-go-mod-patch-and-minor: - patterns: ["*"] - update-types: ["patch", "minor"] + patterns: [ "*" ] + update-types: [ "patch", "minor" ] commit-message: prefix: ":seedling:" include: scope ignore: # Ignore CAPI major and minor bumps - dependency-name: "sigs.k8s.io/cluster-api*" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] # Ignore controller-runtime major and minor bumps as its upgraded manually. - dependency-name: "sigs.k8s.io/controller-runtime" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] # Ignore k8s major and minor bumps and its transitives modules - dependency-name: "k8s.io/*" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - dependency-name: "sigs.k8s.io/controller-tools" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] labels: - "area/dependency" - "ok-to-test" @@ -107,7 +107,7 @@ updates: target-branch: release-0.12 groups: all-github-actions: - patterns: ["*"] + patterns: [ "*" ] commit-message: prefix: ":seedling:" include: scope @@ -125,28 +125,29 @@ updates: target-branch: release-0.12 groups: all-go-mod-patch-and-minor: - patterns: ["*"] - update-types: ["patch", "minor"] + patterns: [ "*" ] + update-types: [ "patch", "minor" ] commit-message: prefix: ":seedling:" include: scope ignore: # Ignore CAPI major and minor bumps - dependency-name: "sigs.k8s.io/cluster-api*" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] # Ignore controller-runtime major and minor bumps as its upgraded manually. - dependency-name: "sigs.k8s.io/controller-runtime" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] # Ignore k8s major and minor bumps and its transitives modules - dependency-name: "k8s.io/*" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - dependency-name: "sigs.k8s.io/controller-tools" - update-types: ["version-update:semver-major", "version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] # We will need k8s v0.31.3 to bump structured-merge-diff to v4.4.2 (check git history for details). - dependency-name: "sigs.k8s.io/structured-merge-diff/*" # These dependencies are skipped because they require a newer version of go: - dependency-name: "github.com/a8m/envsubst" - dependency-name: "github.com/onsi/gomega" + - dependency-name: "github.com/itchyny/gojq" - dependency-name: "golang.org/x/crypto" - dependency-name: "golang.org/x/text" # Newer kustomize requires a bump to kube-openapi, which has some incompatibility with gengo. @@ -154,4 +155,5 @@ updates: labels: - "area/dependency" - "ok-to-test" + ## release-0.12 branch config ends here diff --git a/.golangci.yml b/.golangci.yml index f2430a1e1..c0aa7e39d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,136 +2,136 @@ version: "2" run: go: "1.24" build-tags: - - e2e + - e2e allow-parallel-runners: true linters: default: none enable: - - asasalint - - asciicheck - - bidichk - - bodyclose - - copyloopvar - - cyclop - - dogsled - - dupword - - durationcheck - - errcheck - - forbidigo - - goconst - - gocritic - - gocyclo - - godot - - goheader - - gomodguard - - goprintffuncname - - gosec - - govet - - importas - - ineffassign - - makezero - - misspell - - nakedret - - nestif - - nilerr - - noctx - - nolintlint - - prealloc - - predeclared - - revive - - rowserrcheck - - sqlclosecheck - - staticcheck - - thelper - - unconvert - - unparam - - unused - - wastedassign - - whitespace + - asasalint + - asciicheck + - bidichk + - bodyclose + - copyloopvar + - cyclop + - dogsled + - dupword + - durationcheck + - errcheck + - forbidigo + - goconst + - gocritic + - gocyclo + - godot + - goheader + - gomodguard + - goprintffuncname + - gosec + - govet + - importas + - ineffassign + - makezero + - misspell + - nakedret + - nestif + - nilerr + - noctx + - nolintlint + - prealloc + - predeclared + - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - thelper + - unconvert + - unparam + - unused + - wastedassign + - whitespace settings: # TODO(sbuerin) fix remaining findings and set to 20 afterwards cyclop: max-complexity: 30 gocritic: disabled-checks: - - appendAssign - - dupImport # https://github.com/go-critic/go-critic/issues/845 - - evalOrder - - ifElseChain - - octalLiteral - - regexpSimplify - - sloppyReassign - - truncateCmp - - typeDefFirst - - unnamedResult - - unnecessaryDefer - - whyNoLint - - wrapperFunc - - rangeValCopy - - hugeParam + - appendAssign + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - evalOrder + - ifElseChain + - octalLiteral + - regexpSimplify + - sloppyReassign + - truncateCmp + - typeDefFirst + - unnamedResult + - unnecessaryDefer + - whyNoLint + - wrapperFunc + - rangeValCopy + - hugeParam enabled-tags: - - diagnostic - - experimental - - performance + - diagnostic + - experimental + - performance importas: alias: - # Kubernetes - - pkg: k8s.io/api/core/v1 - alias: corev1 - - pkg: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 - alias: apiextensionsv1 - - pkg: k8s.io/apimachinery/pkg/apis/meta/v1 - alias: metav1 - - pkg: k8s.io/apimachinery/pkg/api/errors - alias: apierrors - - pkg: k8s.io/apimachinery/pkg/util/errors - alias: kerrors - # Controller Runtime - - pkg: sigs.k8s.io/controller-runtime - alias: ctrl - # CAPO - - pkg: sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1 - alias: infrav1alpha1 - - pkg: sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1 - alias: infrav1 - - pkg: sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors - alias: capoerrors - # CAPI - - pkg: sigs.k8s.io/cluster-api/api/v1alpha3 - alias: clusterv1alpha3 - - pkg: sigs.k8s.io/cluster-api/api/v1alpha4 - alias: clusterv1alpha4 - - pkg: sigs.k8s.io/cluster-api/api/core/v1beta2 - alias: clusterv1 - - pkg: sigs.k8s.io/cluster-api/api/core/v1beta1 - alias: clusterv1beta1 - - pkg: sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions - alias: v1beta1conditions - - pkg: sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions/v1beta2 - alias: deprecatedv1beta2conditions - - pkg: sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1 - alias: deprecatedv1beta1conditions - - pkg: sigs.k8s.io/cluster-api/util/deprecated/v1beta1/patch - alias: v1beta1patch - - pkg: sigs.k8s.io/cluster-api/api/ipam/v1beta2 - alias: ipamv1 - # CABPK - - pkg: sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3 - alias: bootstrapv1alpha3 - - pkg: sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4 - alias: bootstrapv1alpha4 - - pkg: sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1 - alias: bootstrapv1 - # KCP - - pkg: sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1alpha3 - alias: controlplanev1alpha3 - - pkg: sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1alpha4 - alias: controlplanev1alpha4 - - pkg: sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1 - alias: controlplanev1 - # ORC - - pkg: github.com/k-orc/openstack-resource-controller/api/v1alpha1 - alias: orcv1alpha1 + # Kubernetes + - pkg: k8s.io/api/core/v1 + alias: corev1 + - pkg: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 + alias: apiextensionsv1 + - pkg: k8s.io/apimachinery/pkg/apis/meta/v1 + alias: metav1 + - pkg: k8s.io/apimachinery/pkg/api/errors + alias: apierrors + - pkg: k8s.io/apimachinery/pkg/util/errors + alias: kerrors + # Controller Runtime + - pkg: sigs.k8s.io/controller-runtime + alias: ctrl + # CAPO + - pkg: sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1 + alias: infrav1alpha1 + - pkg: sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1 + alias: infrav1 + - pkg: sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors + alias: capoerrors + # CAPI + - pkg: sigs.k8s.io/cluster-api/api/v1alpha3 + alias: clusterv1alpha3 + - pkg: sigs.k8s.io/cluster-api/api/v1alpha4 + alias: clusterv1alpha4 + - pkg: sigs.k8s.io/cluster-api/api/core/v1beta2 + alias: clusterv1 + - pkg: sigs.k8s.io/cluster-api/api/core/v1beta1 + alias: clusterv1beta1 + - pkg: sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions + alias: v1beta1conditions + - pkg: sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions/v1beta2 + alias: deprecatedv1beta2conditions + - pkg: sigs.k8s.io/cluster-api/util/conditions/deprecated/v1beta1 + alias: deprecatedv1beta1conditions + - pkg: sigs.k8s.io/cluster-api/util/deprecated/v1beta1/patch + alias: v1beta1patch + - pkg: sigs.k8s.io/cluster-api/api/ipam/v1beta2 + alias: ipamv1 + # CABPK + - pkg: sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3 + alias: bootstrapv1alpha3 + - pkg: sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4 + alias: bootstrapv1alpha4 + - pkg: sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1 + alias: bootstrapv1 + # KCP + - pkg: sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1alpha3 + alias: controlplanev1alpha3 + - pkg: sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1alpha4 + alias: controlplanev1alpha4 + - pkg: sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1 + alias: controlplanev1 + # ORC + - pkg: github.com/k-orc/openstack-resource-controller/api/v1alpha1 + alias: orcv1alpha1 no-unaliased: true nestif: # minimal complexity of if statements to report, 5 by default @@ -144,73 +144,82 @@ linters: exclusions: generated: lax presets: - - comments - - common-false-positives - - legacy - - std-error-handling + - comments + - common-false-positives + - legacy + - std-error-handling # List of regexps of issue texts to exclude, empty list by default. rules: - - linters: - - gosec - text: 'G108: Profiling endpoint is automatically exposed on /debug/pprof' - - linters: - - gosec - text: 'G108: Profiling endpoint is automatically exposed on /debug/pprof' - # This directive allows the embed package to be imported with an underscore everywhere. - - linters: - - revive - source: _ "embed" - - linters: - - revive - - staticcheck - path: (test)/.*.go - text: should not use dot imports - - linters: - - revive - path: test/e2e/shared/defaults.go - text: 'exported: exported const .* should have comment \(or a comment on this block\) or be unexported' - - linters: - - revive - text: 'var-naming: don''t use underscores in Go names;' - - linters: - - staticcheck - path: (api\/.*|pkg/utils/optional)\/.*conversion.*\.go$ - text: 'ST1003: should not use underscores in Go names;' - - linters: - - staticcheck - path: pkg/utils/conversioncommon/.*.go - text: 'ST1003: should not use underscores in Go names;' - - linters: - - staticcheck - text: 'SA1019: "sigs.k8s.io/cluster-api/api/core/v1beta1" is deprecated: This package is deprecated and is going to be removed when support for v1beta1 will be dropped.' - - linters: - - staticcheck - text: 'SA1019: "sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions" is deprecated: This package is deprecated and is going to be removed when support for v1beta1 will be dropped. Please see https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more details.' + - linters: + - gosec + text: 'G108: Profiling endpoint is automatically exposed on /debug/pprof' + - linters: + - gosec + text: 'G108: Profiling endpoint is automatically exposed on /debug/pprof' + # This directive allows the embed package to be imported with an underscore everywhere. + - linters: + - revive + source: _ "embed" + - linters: + - revive + - staticcheck + path: (test)/.*.go + text: should not use dot imports + - linters: + - revive + path: test/e2e/shared/defaults.go + text: 'exported: exported const .* should have comment \(or a comment on this block\) or be unexported' + - linters: + - revive + text: 'var-naming: don''t use underscores in Go names;' + - linters: + - staticcheck + path: (api\/.*|pkg/utils/optional)\/.*conversion.*\.go$ + text: 'ST1003: should not use underscores in Go names;' + - linters: + - staticcheck + path: pkg/utils/conversioncommon/.*.go + text: 'ST1003: should not use underscores in Go names;' + - linters: + - staticcheck + text: 'SA1019: "sigs.k8s.io/cluster-api/api/core/v1beta1" is deprecated: This package is deprecated and is going to be removed when support for v1beta1 will be dropped.' + - linters: + - staticcheck + text: 'SA1019: "sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions" is deprecated: This package is deprecated and is going to be removed when support for v1beta1 will be dropped. Please see https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more details.' + - linters: + - staticcheck + text: 'SA1019: .*.Status.Ready is deprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to determine the ready state of the *' + - linters: + - staticcheck + text: 'SA1019: .*.Status.FailureReason is deprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to report failures.' + - linters: + - staticcheck + text: 'SA1019: .*.Status.FailureMessage is deprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to report failures.' paths: - - zz_generated.*\.go$ - - third_party$ - - builtin$ - - examples$ + - zz_generated.*\.go$ + - third_party$ + - builtin$ + - examples$ issues: max-issues-per-linter: 0 max-same-issues: 0 formatters: enable: - - gci - - gofmt - - gofumpt - - goimports + - gci + - gofmt + - gofumpt + - goimports settings: gci: sections: - - standard - - default - - prefix(github.com/k-orc/openstack-resource-controller) - - prefix(sigs.k8s.io/cluster-api-provider-openstack) + - standard + - default + - prefix(github.com/k-orc/openstack-resource-controller) + - prefix(sigs.k8s.io/cluster-api-provider-openstack) exclusions: generated: lax paths: - - zz_generated.*\.go$ - - third_party$ - - builtin$ - - examples$ + - zz_generated.*\.go$ + - third_party$ + - builtin$ + - examples$ diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 000000000..ac6823ff3 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,14 @@ +yaml-files: +- '*.yaml' +- '*.yml' +- '.yamllint' + +rules: + trailing-spaces: enable + key-duplicates: enable + indentation: + spaces: 2 + indent-sequences: false # Enforce k8s-style indentation + check-multi-line-strings: false + truthy: + allowed-values: [ 'true', 'false', 'yes', 'no', 'on' ] diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..adb8302d7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,303 @@ +# AGENTS.md - Agent Guidelines for Cluster API Provider OpenStack + +This document provides guidelines and useful commands for AI agents contributing to the Cluster API Provider OpenStack (CAPO) repository. + +> **⚠️ IMPORTANT**: When making changes to Makefile targets, PR requirements, code generation workflows, verification steps, or any other information referenced in this document, **AGENTS.md must be updated accordingly** to keep it synchronized with the actual project state. + +## Overview + +Cluster API Provider OpenStack (CAPO) is a Kubernetes-native declarative infrastructure provider for managing Kubernetes clusters on OpenStack. It implements the Cluster API (CAPI) specification for self-managed Kubernetes clusters on OpenStack infrastructure. + +**Key Concepts:** +- **CAPI**: Cluster API - the upstream Kubernetes project defining cluster lifecycle APIs +- **CAPO**: Cluster API Provider OpenStack - this repository +- **Reconciler**: Controller-runtime pattern for managing Kubernetes custom resources +- **Scope**: Context and configuration wrapper for controllers and services + +## Key Requirements for Contributors + +### Legal Requirements + +- **CLA Required**: All contributors MUST sign the Kubernetes Contributor License Agreement (CLA) +- See: https://git.k8s.io/community/CLA.md + +### Pull Request Labels + +All code PRs MUST be labeled with one of: +- ⚠️ `:warning:` - major or breaking changes +- ✨ `:sparkles:` - feature additions +- 🐛 `:bug:` - patch and bugfixes +- 📖 `:book:` - documentation or proposals +- 🌱 `:seedling:` - minor or other + +## Essential Make Targets + +### Code Quality & Verification + +> **⚠️ IMPORTANT**: The `make verify` targets compare the working tree against `HEAD` (the last commit). This means: +> - `make verify` will **always fail** if you have uncommitted changes, even if those changes are correct +> - You should **commit your changes first**, then run `make verify` to confirm everything is in order +> - The typical workflow is: make changes → `make generate` → `make modules` → `make test` → **commit** → `make verify` + +```bash +# Run all verification checks (should pass before submitting PR) +# NOTE: This compares against HEAD, so commit your changes first! +make verify + +# Verify boilerplate headers +make verify-boilerplate + +# Verify go modules are up to date (runs `go mod tidy` and diffs against HEAD) +make verify-modules + +# Verify generated code is up to date (runs `make generate` and diffs against HEAD) +make verify-gen + +# Verify container images for vulnerabilities +make verify-container-images + +# Check code for security vulnerabilities +make verify-govulncheck + +# Run all security checks (images + code) +make verify-security +``` + +### Linting + +```bash +# Lint codebase +make lint + +# Lint and auto-fix issues +make lint-update + +# Run faster linters only +make lint-fast +``` + +### Testing + +```bash +# Run unit tests +make test + +# Run unit tests for CAPO specifically +make test-capo + +# Build e2e test binaries +make build-e2e-tests + +# Run e2e tests (requires OpenStack environment) +make test-e2e + +# Run conformance tests +make test-conformance + +# Compile e2e tests (verify they build) +make compile-e2e +``` + +### Code Generation + +```bash +# Generate ALL code (manifests, deepcopy, clients, mocks, docs) +make generate + +# Generate Go code (mocks, etc.) +make generate-go + +# Generate controller-gen code (deepcopy, etc.) +make generate-controller-gen + +# Generate client code (clientsets, listers, informers) +make generate-codegen + +# Generate CRD manifests +make generate-manifests + +# Generate API documentation +make generate-api-docs + +# Generate cluster templates +make templates +``` + +### Dependency Management + +```bash +# Update go modules +make modules + +# Check for API differences (useful before breaking changes) +make apidiff +``` + +### Building + +```bash +# Build manager binaries +make managers + +# Build all binaries +make binaries + +# Build Docker image +make docker-build + +# Build debug Docker image +make docker-build-debug + +# Build for all architectures +make docker-build-all +``` + +### Cleanup + +```bash +# Clean all build artifacts +make clean + +# Clean binaries only +make clean-bin + +# Clean temporary files +make clean-temporary + +# Clean release artifacts +make clean-release +``` + +## Important Development Patterns + +### Adding New OpenStack Resources + +1. Define API types in `/api/v1beta1` (or `/api/v1alpha1` for experimental features) +2. Run `make generate` to create deepcopy methods and update CRDs +3. Create controller in `/controllers` +4. Create service implementation in `/pkg/cloud/services/` +5. Update or create scope in `/pkg/scope` if needed +6. Add webhooks in `/pkg/webhooks` for validation/defaulting +7. Add unit tests for controller and services +8. Update documentation +9. Generate cluster templates if applicable with `make templates` + +### Testing Strategy + +1. **Unit Tests**: Test individual functions/methods with mocks +2. **Integration Tests**: Test controller behavior with envtest +3. **E2E Tests**: Deploy real clusters on OpenStack, verify functionality +4. **Conformance Tests**: Run upstream Kubernetes conformance suite + +## Pre-Submit Checklist for Agents + +Before submitting a PR, ensure: + +1. **Code is generated and up to date**: + ```bash + make generate + ``` + +2. **Modules are tidy**: + ```bash + make modules + ``` + +3. **Code passes linting**: + ```bash + make lint + ``` + +4. **Tests pass**: + ```bash + make test + ``` + +5. **All verification checks pass**: + ```bash + make verify + ``` + +## Common Workflows + +### Making Code Changes + +1. Make your code changes +2. Run code generation: `make generate` +3. Update modules if needed: `make modules` +4. Run tests: `make test` +5. Lint the code: `make lint` +6. **Commit changes** with descriptive message +7. Verify everything: `make verify` (this compares against HEAD, so must be done after commit) + +### Updating Dependencies + +1. Update `go.mod` or `hack/tools/go.mod` as needed (e.g., `go get sigs.k8s.io/cluster-api@v1.x.x`) +2. Run: `make modules` to tidy dependencies +3. Run: `make generate` to regenerate code (dependency updates often change generated code) +4. Run: `make test` to ensure everything still works +5. **Commit all changes** (go.mod, go.sum, and any regenerated files) +6. Run: `make verify` to confirm everything is in order + +## Common Issues + +### Linting Errors + +The project uses golangci-lint. If you get lint errors: +1. Run `make lint-update` first to auto-fix +2. Check `.golangci.yml` for enabled linters +3. Some issues require manual fixes (cognitive complexity, error handling, etc.) +4. Don't disable linters without good reason - fix the underlying issue + +### Test Failures + +- **envtest issues**: Ensure KUBEBUILDER_ASSETS is set correctly +- **Flaky E2E tests**: Transient infrastructure issues, failure to deploy devstack + +### Generated File Drift + +If `make verify` fails with generated file drift: +1. Run `make generate` to regenerate all files +2. Run `make modules` to ensure go.mod/go.sum are tidy +3. Review the changes to ensure they're expected +4. **Commit the generated files** - verify targets compare against HEAD! +5. Run `make verify` again - it should pass now +6. Never manually edit generated files + +### Verify Fails Even After Running Generate/Modules + +If `make verify-gen` or `make verify-modules` fails even after running `make generate` or `make modules`: +- Remember that these targets compare against `HEAD` (the last commit) +- If you have uncommitted changes, they will show as "drift" even if correct +- **Solution**: Commit your changes first, then run `make verify` +- This is by design - it ensures the committed code is complete and self-consistent + +## Documentation + +Primary documentation is in `/docs/book/src/` (mdBook format): +- Getting started guides +- Developer documentation +- Troubleshooting guides +- API reference +- Cluster template documentation + +Build and serve docs locally: +```bash +make -C docs/book serve +``` + +## Quick Reference + +| Task | Command | +|------|---------| +| Full verification before PR | `make verify && make test` | +| Generate all code | `make generate` | +| Update dependencies | `make modules` | +| Lint and fix | `make lint-update` | +| Run tests | `make test` | +| Build binary | `make managers` | +| Build Docker image | `make docker-build` | +| Clean everything | `make clean` | +| Check API compatibility | `make apidiff` | +| Generate templates | `make templates` | +| Build and serve docs | `make -C docs/book serve` | diff --git a/Dockerfile b/Dockerfile index f6b4f2a15..1c2f977b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ # Build the manager binary ARG GO_VERSION -FROM golang:${GO_VERSION:-1.24.9} AS builder +FROM golang:${GO_VERSION:-1.24.11} AS builder WORKDIR /workspace # Run this with docker build --build_arg goproxy=$(go env GOPROXY) to override the goproxy diff --git a/Makefile b/Makefile index 6ccff05ab..1a1ed68e6 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ unexport GOPATH TRACE ?= 0 # Go -GO_VERSION ?= 1.24.9 +GO_VERSION ?= 1.24.11 # Directories. ARTIFACTS ?= $(REPO_ROOT)/_artifacts @@ -188,6 +188,7 @@ e2e-templates: $(addprefix $(E2E_NO_ARTIFACT_TEMPLATES_DIR)/, \ cluster-template-multi-network.yaml \ cluster-template-without-lb.yaml \ cluster-template.yaml \ + cluster-template-topology.yaml \ cluster-template-flatcar.yaml \ cluster-template-k8s-upgrade.yaml \ cluster-template-flatcar-sysext.yaml \ @@ -448,14 +449,14 @@ staging-manifests: ## -------------------------------------- ##@ Release ## -------------------------------------- - ifneq (,$(findstring -,$(RELEASE_TAG))) PRE_RELEASE=true endif -PREVIOUS_TAG ?= $(shell git tag -l | grep -E "^v[0-9]+\.[0-9]+\.[0-9]+$$" | sort -V | grep -B1 $(RELEASE_TAG) | head -n 1 2>/dev/null) +# List all tags, add the new tag to the list, sort and pick the previous one. +PREVIOUS_TAG ?= $(shell (git tag -l | grep -E "^v[0-9]+\.[0-9]+\.[0-9]+$$"; echo "$(RELEASE_TAG)") | sort -V | grep -B1 "^$(RELEASE_TAG)$$" | grep -v "^$(RELEASE_TAG)$$" | head -n 1 2>/dev/null) ## set by Prow, ref name of the base branch, e.g., main RELEASE_DIR := out -RELEASE_NOTES_DIR := _releasenotes +RELEASE_NOTES_DIR := releasenotes .PHONY: $(RELEASE_DIR) $(RELEASE_DIR): @@ -478,7 +479,7 @@ list-image: gcloud container images list-tags $(STAGING_REGISTRY)/$(IMAGE) --filter="tags=('$(RELEASE_TAG)')" --format=json .PHONY: release -release: $(RELEASE_NOTES) clean-release $(RELEASE_DIR) ## Builds and push container images using the latest git tag for the commit. +release: $(RELEASE_NOTES) $(RELEASE_DIR) ## Builds and push container images using the latest git tag for the commit. @if [ -z "${RELEASE_TAG}" ]; then echo "RELEASE_TAG is not set"; exit 1; fi @if ! [ -z "$$(git status --porcelain)" ]; then echo "Your local git repository contains uncommitted changes, use git clean before proceeding."; fi git checkout "${RELEASE_TAG}" @@ -486,6 +487,7 @@ release: $(RELEASE_NOTES) clean-release $(RELEASE_DIR) ## Builds and push conta $(MAKE) manifest-modification REGISTRY=$(PROD_REGISTRY) $(MAKE) release-manifests $(MAKE) release-templates + $(MAKE) generate-release-notes .PHONY: manifest-modification manifest-modification: # Set the manifest images to the staging/production bucket. @@ -551,7 +553,7 @@ generate-release-notes: $(RELEASE_NOTES_DIR) $(RELEASE_NOTES) fi "$(RELEASE_NOTES)" --repository=kubernetes-sigs/cluster-api-provider-openstack \ --prefix-area-label=false --add-kubernetes-version-support=false \ - --from=$(PREVIOUS_TAG) --release=$(RELEASE_TAG) >> $(RELEASE_NOTES_DIR)/$(RELEASE_TAG).md + --from=tags/$(PREVIOUS_TAG) --release=$(RELEASE_TAG) >> $(RELEASE_NOTES_DIR)/$(RELEASE_TAG).md .PHONY: templates templates: ## Generate cluster templates diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES index 7f32d2e48..64da5b7f0 100644 --- a/OWNERS_ALIASES +++ b/OWNERS_ALIASES @@ -21,5 +21,8 @@ aliases: cluster-api-openstack-maintainers: - emilienm - lentzi90 + - mandre + - stephenfin cluster-api-openstack-reviewers: + - bnallapeta - smoshiur1237 diff --git a/RELEASE.md b/RELEASE.md index 8b7ea9f06..668567573 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -19,6 +19,7 @@ A release version string is: `vX.Y.Z`. A pre-release version string additionally has a suffix: + - `alpha` for an alpha release - `beta` for a beta release - `rc` for a release candidate @@ -41,40 +42,75 @@ The content of the release notes differs depending on the type of release, speci There is an [issue template](.github/ISSUE_TEMPLATE/new_release.md) to help track release activities. 1. Make sure your repo is clean by git's standards. It is recommended to use a fresh checkout. -1. When bumping `X` or `Y` (but not Z or the pre-release suffix) in the release version you must create a new release branch called `release-X.Y`. - > NOTE: `upstream` should be the name of the remote pointing to `github.com/kubernetes-sigs/cluster-api-provider-openstack` - - `git checkout main` - - `git pull` - - `git checkout -b release-X.Y` - - `git push --set-upstream upstream` +1. Repository Setup + - Clone the repository: `git clone git@github.com:kubernetes-sigs/cluster-api-provider-openstack.git` + or if using existing repository, make sure origin is set to the fork and + upstream is set to `kubernetes-sigs`. Verify if your remote is set properly or not + by using following command `git remote -v`, where origin points to fork and upstream points to main repo. + - Fetch the remote (`kubernetes-sigs`): `git fetch upstream` + This makes sure that all the tags are accessible. + 1. When bumping `X` or `Y` (but not Z or the pre-release suffix) in the release version, ensure you have added a new entry to [metadata.yaml](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/metadata.yaml) - as [described in the CAPI book](https://cluster-api.sigs.k8s.io/clusterctl/provider-contract.html#metadata-yaml), and + as [described in the CAPI book](https://cluster-api.sigs.k8s.io/developer/providers/contracts/clusterctl#metadata-yaml), and that this has been committed to the release branch prior to release. -1. Make sure you are on the correct release branch: `release-X.Y` -1. Set an environment variable with the version, e.g.: - - `VERSION=v0.6.0` -1. Create an annotated tag - - `git tag -s -a $VERSION -m $VERSION`. -1. Push the tag to the GitHub repository: - > NOTE: `upstream` should be the name of the remote pointing to `github.com/kubernetes-sigs/cluster-api-provider-openstack` - - `git push upstream $VERSION` - - This will cause the image to be automatically built by CI and pushed to the staging repository. As this only builds - the image, it only takes a few minutes. - It also triggers the [release](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/.github/workflows/release.yaml) workflow which will generate release notes and artifacts, and create a draft release in GitHub. -1. Follow the [image promotion process](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/README.md#image-promoter) to promote the image from the staging repo to `registry.k8s.io/capi-openstack`. - The staging repository can be inspected at https://console.cloud.google.com/gcr/images/k8s-staging-capi-openstack/GLOBAL. Be + +1. Creating Release Notes + - Switch to the main branch: `git checkout main` + - Create a new branch for the release notes**: + `git checkout -b release-notes-X.Y.Z origin/main` + - Generate the release notes: `RELEASE_TAG=vX.Y.Z make generate-release-notes` + - Replace `vX.Y.Z` with the new release tag you're creating. + - This command generates the release notes here + `releasenotes/.md` . + +1. Next step is to clean up the release note manually. + - If release is not an alpha or a beta or release candidate, check for duplicates, + reverts, and incorrect classifications of PRs, and whatever release + creation tagged to be manually checked. + - For any superseded PRs (like same dependency uplifted multiple times, or + commit revertion) that provide no value to the release, move them to + Superseded section. This way the changes are acknowledged to be part of the + release, but not overwhelming the important changes contained in the release. + - Commit your changes, push the new branch and create a pull request: + - The commit and PR title should be 🚀 Release v1.x.y: + -`git commit -S -s -m ":rocket: Release vX.Y.Z"` + -`git push -u origin release-notes-X.Y.Z` + - Important! The commit should only contain the release notes file, nothing + else, otherwise automation will not work. Push as normal, through your fork (`origin`). + - Ask maintainers and release team members to review your pull request. + + Once the PR is merged, the following GitHub actions are triggered: + + - GitHub action `Create Release` runs following jobs + - GitHub job `push_release_tags` will create and push the tags. This action + will also create release branch if its missing and release is `rc` or minor. + - GitHub job `create draft release` creates draft release. Don't publish the + release yet. Running actions are visible on the + [Actions](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/actions) + page, and draft release will be visible on top of the + [Releases](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/releases). + + The image will also be automatically built by CI and pushed to the staging repository. As this only builds the image, it only takes a few minutes. + +1. Follow the [image promotion process](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/README.md#image-promoter) + to promote the image from the staging repo to `registry.k8s.io/capi-openstack`. + The staging repository can be inspected at [Staging CAPI Openstack](https://console.cloud.google.com/gcr/images/k8s-staging-capi-openstack/GLOBAL). Be sure to choose the top level `capi-openstack-controller`, which will provide the multi-arch manifest, rather than one for a specific architecture. The image build logs are available at [Cloud Build](https://console.cloud.google.com/cloud-build/builds?project=k8s-staging-capi-openstack). Add the new sha=>tag mapping to the [images.yaml](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/images/k8s-staging-capi-openstack/images.yaml) (use the sha of the image with the corresponding tag). The PR to update the [images.yaml](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/images/k8s-staging-capi-openstack/images.yaml) must be approved in the [OWNERS](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/images/k8s-staging-capi-openstack/OWNERS) file and merged. + Here is an example [pull request](https://github.com/kubernetes/k8s.io/pull/8807). + + It is good practise to get somebody else to review this PR. It is safe to perform the following steps while waiting for review and the promotion of the image. - It is good practise to get somebody else to review this PR. It is safe to perform the following steps while waiting - for review and the promotion of the image. 1. Check carefully the [draft release](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/releases) created by the workflow. Ensure that the release notes are correct and that the artifacts are present. If any changes are needed, edit the release notes in the GitHub UI and add any missing artifacts. 1. Ensure that the release image has been promoted. +1. If the release you're making is not a new major release, new minor release, + or a new patch release from the latest release branch, uncheck the box for + latest release. If it is a release candidate (RC) or a beta or an alpha + release, tick pre-release box. 1. Publish release. ### Post release actions @@ -89,12 +125,16 @@ There is an [issue template](.github/ISSUE_TEMPLATE/new_release.md) to help trac Releasing requires a particular set of permissions. -* Approver role for the image promoter process ([kubernetes/k8s.io/blob/main/registry.k8s.io/images/k8s-staging-capi-openstack/OWNERS](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/images/k8s-staging-capi-openstack/OWNERS)) -* Tag push and release creation rights to the GitHub repository (team `cluster-api-provider-openstack-maintainers` in [kubernetes/org/config/kubernetes-sigs/sig-cluster-lifecycle/teams.yaml](https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cluster-lifecycle/teams.yaml)) +1. Approver role for the image promoter process ([kubernetes/k8s.io/blob/main/registry.k8s.io/images/k8s-staging-capi-openstack/OWNERS](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/images/k8s-staging-capi-openstack/OWNERS)) + +1. Tag push and release creation rights to the GitHub repository (team `cluster-api-provider-openstack-maintainers` in [kubernetes/org/config/kubernetes-sigs/sig-cluster-lifecycle/teams.yaml](https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cluster-lifecycle/teams.yaml)) ## Staging There is a post-submit Prow job running after each commit on `main` which pushes a new image to the staging repo (`gcr.io/k8s-staging-capi-openstack/capi-openstack-controller:latest`). Following configuration is involved: -* staging gcr bucket: [kubernetes/k8s.io/blob/main/registry.k8s.io/manifests/k8s-staging-capi-openstack/promoter-manifest.yaml](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/manifests/k8s-staging-capi-openstack/promoter-manifest.yaml) -* post-submit `post-capi-openstack-push-images` Prow job: [kubernetes/test-infra/blob/master/config/jobs/image-pushing/k8s-staging-cluster-api.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/image-pushing/k8s-staging-cluster-api.yaml)) (corresponding dashboard is located at [https://testgrid.k8s.io/sig-cluster-lifecycle-image-pushes#post-capi-openstack-push-images](https://testgrid.k8s.io/sig-cluster-lifecycle-image-pushes#post-capi-openstack-push-images)) -* Google Cloud Build configuration which is used by the Prow job: [kubernetes-sigs/cluster-api-provider-openstack/cloudbuild.yaml](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/cloudbuild.yaml) + +1. staging gcr bucket: [kubernetes/k8s.io/blob/main/registry.k8s.io/manifests/k8s-staging-capi-openstack/promoter-manifest.yaml](https://github.com/kubernetes/k8s.io/blob/main/registry.k8s.io/manifests/k8s-staging-capi-openstack/promoter-manifest.yaml) + +1. post-submit `post-capi-openstack-push-images` Prow job: [kubernetes/test-infra/blob/master/config/jobs/image-pushing/k8s-staging-cluster-api.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/image-pushing/k8s-staging-cluster-api.yaml) (corresponding dashboard is located at [https://testgrid.k8s.io/sig-cluster-lifecycle-image-pushes#post-capi-openstack-push-images](https://testgrid.k8s.io/sig-cluster-lifecycle-image-pushes#post-capi-openstack-push-images)) + +1. Google Cloud Build configuration which is used by the Prow job: [kubernetes-sigs/cluster-api-provider-openstack/cloudbuild.yaml](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/cloudbuild.yaml) diff --git a/api/v1beta1/conditions_consts.go b/api/v1beta1/conditions_consts.go index d84500772..86dcc086f 100644 --- a/api/v1beta1/conditions_consts.go +++ b/api/v1beta1/conditions_consts.go @@ -69,3 +69,32 @@ const ( // UnableToFindFloatingIPNetworkReason is used when the floating ip network is not found. UnableToFindFloatingIPNetworkReason = "UnableToFindFloatingIPNetwork" ) + +const ( + // NetworkReadyCondition reports on the current status of the cluster network infrastructure. + // Ready indicates that the network, subnets, and related resources have been successfully provisioned. + NetworkReadyCondition clusterv1beta1.ConditionType = "NetworkReady" + + // RouterReadyCondition reports on the current status of the cluster router infrastructure. + // Ready indicates that the router and its interfaces have been successfully provisioned. + RouterReadyCondition clusterv1beta1.ConditionType = "RouterReady" + + // SecurityGroupsReadyCondition reports on the current status of the cluster security groups. + // Ready indicates that all required security groups have been successfully provisioned. + SecurityGroupsReadyCondition clusterv1beta1.ConditionType = "SecurityGroupsReady" + + // APIEndpointReadyCondition reports on the current status of the cluster API endpoint. + // Ready indicates that the control plane endpoint has been successfully configured. + APIEndpointReadyCondition clusterv1beta1.ConditionType = "APIEndpointReady" + + // NetworkReconcileFailedReason is used when network reconciliation fails. + NetworkReconcileFailedReason = "NetworkCreateFailed" + // SubnetReconcileFailedReason is used when subnet reconciliation fails. + SubnetReconcileFailedReason = "SubnetCreateFailed" + // RouterReconcileFailedReason is used when router reconciliation fails. + RouterReconcileFailedReason = "RouterCreateFailed" + // SecurityGroupReconcileFailedReason is used when security group reconciliation fails. + SecurityGroupReconcileFailedReason = "SecurityGroupCreateFailed" + // APIEndpointConfigFailedReason is used when API endpoint configuration fails. + APIEndpointConfigFailedReason = "APIEndpointConfigFailed" +) diff --git a/api/v1beta1/openstackcluster_types.go b/api/v1beta1/openstackcluster_types.go index 02833b1ea..3c35efcf3 100644 --- a/api/v1beta1/openstackcluster_types.go +++ b/api/v1beta1/openstackcluster_types.go @@ -196,12 +196,27 @@ type OpenStackClusterSpec struct { IdentityRef OpenStackIdentityReference `json:"identityRef"` } +// ClusterInitialization represents the initialization status of the cluster. +type ClusterInitialization struct { + // Provisioned is set to true when the initial provisioning of the cluster infrastructure is completed. + // The value of this field is never updated after provisioning is completed. + // +optional + Provisioned bool `json:"provisioned,omitempty"` +} + // OpenStackClusterStatus defines the observed state of OpenStackCluster. type OpenStackClusterStatus struct { // Ready is true when the cluster infrastructure is ready. + // + // Deprecated: This field is deprecated and will be removed in a future API version. + // Use status.conditions to determine the ready state of the cluster. // +kubebuilder:default=false Ready bool `json:"ready"` + // Initialization contains information about the initialization status of the cluster. + // +optional + Initialization *ClusterInitialization `json:"initialization,omitempty"` + // Network contains information about the created OpenStack Network. // +optional Network *NetworkStatusWithSubnets `json:"network,omitempty"` @@ -257,6 +272,9 @@ type OpenStackClusterStatus struct { // Any transient errors that occur during the reconciliation of // OpenStackClusters can be added as events to the OpenStackCluster object // and/or logged in the controller's output. + // + // Deprecated: This field is deprecated and will be removed in a future API version. + // Use status.conditions to report failures. // +optional FailureReason *capoerrors.DeprecatedCAPIClusterStatusError `json:"failureReason,omitempty"` @@ -276,8 +294,18 @@ type OpenStackClusterStatus struct { // Any transient errors that occur during the reconciliation of // OpenStackClusters can be added as events to the OpenStackCluster object // and/or logged in the controller's output. + // + // Deprecated: This field is deprecated and will be removed in a future API version. + // Use status.conditions to report failures. // +optional FailureMessage *string `json:"failureMessage,omitempty"` + + // Conditions defines current service state of the OpenStackCluster. + // This field surfaces into Cluster's status.conditions[InfrastructureReady] condition. + // The Ready condition must surface issues during the entire lifecycle of the OpenStackCluster + // (both during initial provisioning and after the initial provisioning is completed). + // +optional + Conditions clusterv1beta1.Conditions `json:"conditions,omitempty"` } // +genclient @@ -344,6 +372,16 @@ type ManagedSecurityGroups struct { var _ IdentityRefProvider = &OpenStackCluster{} +// GetConditions returns the observations of the operational state of the OpenStackCluster resource. +func (c *OpenStackCluster) GetConditions() clusterv1beta1.Conditions { + return c.Status.Conditions +} + +// SetConditions sets the underlying service state of the OpenStackCluster to the predescribed clusterv1.Conditions. +func (c *OpenStackCluster) SetConditions(conditions clusterv1beta1.Conditions) { + c.Status.Conditions = conditions +} + // GetIdentifyRef returns the cluster's namespace and IdentityRef. func (c *OpenStackCluster) GetIdentityRef() (*string, *OpenStackIdentityReference) { return &c.Namespace, &c.Spec.IdentityRef diff --git a/api/v1beta1/openstackmachine_types.go b/api/v1beta1/openstackmachine_types.go index 8b52e62a4..615a47f73 100644 --- a/api/v1beta1/openstackmachine_types.go +++ b/api/v1beta1/openstackmachine_types.go @@ -185,12 +185,27 @@ type ServerMetadata struct { Value string `json:"value"` } +// MachineInitialization contains information about the initialization status of the machine. +type MachineInitialization struct { + // Provisioned is set to true when the initial provisioning of the machine infrastructure is completed. + // The value of this field is never updated after provisioning is completed. + // +optional + Provisioned bool `json:"provisioned,omitempty"` +} + // OpenStackMachineStatus defines the observed state of OpenStackMachine. type OpenStackMachineStatus struct { // Ready is true when the provider resource is ready. + // + // Deprecated: This field is deprecated and will be removed in a future API version. + // Use status.conditions to determine the ready state of the machine. // +optional Ready bool `json:"ready"` + // Initialization contains information about the initialization status of the machine. + // +optional + Initialization *MachineInitialization `json:"initialization,omitempty"` + // InstanceID is the OpenStack instance ID for this machine. // +optional InstanceID optional.String `json:"instanceID,omitempty"` @@ -213,6 +228,11 @@ type OpenStackMachineStatus struct { // +optional Resources *MachineResources `json:"resources,omitempty"` + // FailureReason explains the reson behind a failure. + // + // Deprecated: This field is deprecated and will be removed in a future API version. + // Use status.conditions to report failures. + // +optional FailureReason *capoerrors.DeprecatedCAPIMachineStatusError `json:"failureReason,omitempty"` // FailureMessage will be set in the event that there is a terminal problem @@ -231,9 +251,17 @@ type OpenStackMachineStatus struct { // Any transient errors that occur during the reconciliation of Machines // can be added as events to the Machine object and/or logged in the // controller's output. + // + // Deprecated: This field is deprecated and will be removed in a future API version. + // Use status.conditions to report failures. // +optional FailureMessage *string `json:"failureMessage,omitempty"` + // Conditions defines current service state of the OpenStackMachine. + // This field surfaces into Machine's status.conditions[InfrastructureReady] condition. + // The Ready condition must surface issues during the entire lifecycle of the OpenStackMachine + // (both during initial provisioning and after the initial provisioning is completed). + // +optional Conditions clusterv1beta1.Conditions `json:"conditions,omitempty"` } diff --git a/api/v1beta1/types.go b/api/v1beta1/types.go index 5cb066219..8232d64d7 100644 --- a/api/v1beta1/types.go +++ b/api/v1beta1/types.go @@ -792,6 +792,11 @@ var ( // InstanceStateDeleted is the string representing an instance in a deleted state. InstanceStateDeleted = InstanceState("DELETED") + // InstanceStateSoftDeleted is the string representing an instance in a soft-deleted state. + // This state occurs when OpenStack is configured with a reclaim_instance_interval > 0, + // allowing recovery of deleted instances within the reclaim period. + InstanceStateSoftDeleted = InstanceState("SOFT_DELETED") + // InstanceStateUndefined is the string representing an undefined instance state. InstanceStateUndefined = InstanceState("") ) diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index b93422b7a..53c724058 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -23,7 +23,7 @@ package v1beta1 import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors" + errors "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors" corev1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" ) @@ -280,6 +280,21 @@ func (in *BlockDeviceVolume) DeepCopy() *BlockDeviceVolume { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterInitialization) DeepCopyInto(out *ClusterInitialization) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInitialization. +func (in *ClusterInitialization) DeepCopy() *ClusterInitialization { + if in == nil { + return nil + } + out := new(ClusterInitialization) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExternalRouterIPParam) DeepCopyInto(out *ExternalRouterIPParam) { *out = *in @@ -441,6 +456,21 @@ func (in *LoadBalancer) DeepCopy() *LoadBalancer { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MachineInitialization) DeepCopyInto(out *MachineInitialization) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineInitialization. +func (in *MachineInitialization) DeepCopy() *MachineInitialization { + if in == nil { + return nil + } + out := new(MachineInitialization) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineResources) DeepCopyInto(out *MachineResources) { *out = *in @@ -765,6 +795,11 @@ func (in *OpenStackClusterSpec) DeepCopy() *OpenStackClusterSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenStackClusterStatus) DeepCopyInto(out *OpenStackClusterStatus) { *out = *in + if in.Initialization != nil { + in, out := &in.Initialization, &out.Initialization + *out = new(ClusterInitialization) + **out = **in + } if in.Network != nil { in, out := &in.Network, &out.Network *out = new(NetworkStatusWithSubnets) @@ -822,6 +857,13 @@ func (in *OpenStackClusterStatus) DeepCopyInto(out *OpenStackClusterStatus) { *out = new(string) **out = **in } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(corev1beta1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackClusterStatus. @@ -1095,6 +1137,11 @@ func (in *OpenStackMachineSpec) DeepCopy() *OpenStackMachineSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenStackMachineStatus) DeepCopyInto(out *OpenStackMachineStatus) { *out = *in + if in.Initialization != nil { + in, out := &in.Initialization, &out.Initialization + *out = new(MachineInitialization) + **out = **in + } if in.InstanceID != nil { in, out := &in.InstanceID, &out.InstanceID *out = new(string) diff --git a/cloudbuild-nightly.yaml b/cloudbuild-nightly.yaml index 1fef4a3b1..a4f379598 100644 --- a/cloudbuild-nightly.yaml +++ b/cloudbuild-nightly.yaml @@ -4,15 +4,15 @@ options: substitution_option: ALLOW_LOOSE machineType: 'N1_HIGHCPU_8' steps: - - name: 'gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20250513-9264efb079' - entrypoint: make - env: - - DOCKER_CLI_EXPERIMENTAL=enabled - - TAG=$_GIT_TAG - - PULL_BASE_REF=$_PULL_BASE_REF - - DOCKER_BUILDKIT=1 - args: - - release-staging-nightly +- name: 'gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20250513-9264efb079' + entrypoint: make + env: + - DOCKER_CLI_EXPERIMENTAL=enabled + - TAG=$_GIT_TAG + - PULL_BASE_REF=$_PULL_BASE_REF + - DOCKER_BUILDKIT=1 + args: + - release-staging-nightly substitutions: # _GIT_TAG will be filled with a git-based tag for the image, of the form vYYYYMMDD-hash, and # can be used as a substitution diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go index a6c2ea433..76a7b61c1 100644 --- a/cmd/models-schema/zz_generated.openapi.go +++ b/cmd/models-schema/zz_generated.openapi.go @@ -61,9 +61,12 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "k8s.io/api/core/v1.ConfigMapProjection": schema_k8sio_api_core_v1_ConfigMapProjection(ref), "k8s.io/api/core/v1.ConfigMapVolumeSource": schema_k8sio_api_core_v1_ConfigMapVolumeSource(ref), "k8s.io/api/core/v1.Container": schema_k8sio_api_core_v1_Container(ref), + "k8s.io/api/core/v1.ContainerExtendedResourceRequest": schema_k8sio_api_core_v1_ContainerExtendedResourceRequest(ref), "k8s.io/api/core/v1.ContainerImage": schema_k8sio_api_core_v1_ContainerImage(ref), "k8s.io/api/core/v1.ContainerPort": schema_k8sio_api_core_v1_ContainerPort(ref), "k8s.io/api/core/v1.ContainerResizePolicy": schema_k8sio_api_core_v1_ContainerResizePolicy(ref), + "k8s.io/api/core/v1.ContainerRestartRule": schema_k8sio_api_core_v1_ContainerRestartRule(ref), + "k8s.io/api/core/v1.ContainerRestartRuleOnExitCodes": schema_k8sio_api_core_v1_ContainerRestartRuleOnExitCodes(ref), "k8s.io/api/core/v1.ContainerState": schema_k8sio_api_core_v1_ContainerState(ref), "k8s.io/api/core/v1.ContainerStateRunning": schema_k8sio_api_core_v1_ContainerStateRunning(ref), "k8s.io/api/core/v1.ContainerStateTerminated": schema_k8sio_api_core_v1_ContainerStateTerminated(ref), @@ -92,6 +95,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "k8s.io/api/core/v1.EventSource": schema_k8sio_api_core_v1_EventSource(ref), "k8s.io/api/core/v1.ExecAction": schema_k8sio_api_core_v1_ExecAction(ref), "k8s.io/api/core/v1.FCVolumeSource": schema_k8sio_api_core_v1_FCVolumeSource(ref), + "k8s.io/api/core/v1.FileKeySelector": schema_k8sio_api_core_v1_FileKeySelector(ref), "k8s.io/api/core/v1.FlexPersistentVolumeSource": schema_k8sio_api_core_v1_FlexPersistentVolumeSource(ref), "k8s.io/api/core/v1.FlexVolumeSource": schema_k8sio_api_core_v1_FlexVolumeSource(ref), "k8s.io/api/core/v1.FlockerVolumeSource": schema_k8sio_api_core_v1_FlockerVolumeSource(ref), @@ -167,10 +171,12 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "k8s.io/api/core/v1.PodAffinityTerm": schema_k8sio_api_core_v1_PodAffinityTerm(ref), "k8s.io/api/core/v1.PodAntiAffinity": schema_k8sio_api_core_v1_PodAntiAffinity(ref), "k8s.io/api/core/v1.PodAttachOptions": schema_k8sio_api_core_v1_PodAttachOptions(ref), + "k8s.io/api/core/v1.PodCertificateProjection": schema_k8sio_api_core_v1_PodCertificateProjection(ref), "k8s.io/api/core/v1.PodCondition": schema_k8sio_api_core_v1_PodCondition(ref), "k8s.io/api/core/v1.PodDNSConfig": schema_k8sio_api_core_v1_PodDNSConfig(ref), "k8s.io/api/core/v1.PodDNSConfigOption": schema_k8sio_api_core_v1_PodDNSConfigOption(ref), "k8s.io/api/core/v1.PodExecOptions": schema_k8sio_api_core_v1_PodExecOptions(ref), + "k8s.io/api/core/v1.PodExtendedResourceClaimStatus": schema_k8sio_api_core_v1_PodExtendedResourceClaimStatus(ref), "k8s.io/api/core/v1.PodIP": schema_k8sio_api_core_v1_PodIP(ref), "k8s.io/api/core/v1.PodList": schema_k8sio_api_core_v1_PodList(ref), "k8s.io/api/core/v1.PodLogOptions": schema_k8sio_api_core_v1_PodLogOptions(ref), @@ -339,12 +345,14 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BindingProfile": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_BindingProfile(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BlockDeviceStorage": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_BlockDeviceStorage(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BlockDeviceVolume": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_BlockDeviceVolume(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ClusterInitialization": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ClusterInitialization(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ExternalRouterIPParam": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ExternalRouterIPParam(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FilterByNeutronTags": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_FilterByNeutronTags(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.FixedIP": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_FixedIP(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ImageFilter": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ImageFilter(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ImageParam": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ImageParam(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.LoadBalancer": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_LoadBalancer(ref), + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.MachineInitialization": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_MachineInitialization(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.MachineResources": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_MachineResources(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ManagedSecurityGroups": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ManagedSecurityGroups(ref), "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkFilter": schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_NetworkFilter(ref), @@ -507,6 +515,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassStatusVariable": schema_cluster_api_api_core_v1beta2_ClusterClassStatusVariable(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassStatusVariableDefinition": schema_cluster_api_api_core_v1beta2_ClusterClassStatusVariableDefinition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassTemplateReference": schema_cluster_api_api_core_v1beta2_ClusterClassTemplateReference(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade": schema_cluster_api_api_core_v1beta2_ClusterClassUpgrade(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal": schema_cluster_api_api_core_v1beta2_ClusterClassUpgradeExternal(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_ClusterClassV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable": schema_cluster_api_api_core_v1beta2_ClusterClassVariable(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariableMetadata": schema_cluster_api_api_core_v1beta2_ClusterClassVariableMetadata(ref), @@ -626,6 +636,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineSetV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_MachineSetV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineSpec": schema_cluster_api_api_core_v1beta2_MachineSpec(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineStatus": schema_cluster_api_api_core_v1beta2_MachineStatus(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint": schema_cluster_api_api_core_v1beta2_MachineTaint(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTemplateSpec": schema_cluster_api_api_core_v1beta2_MachineTemplateSpec(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineV1Beta1DeprecatedStatus": schema_cluster_api_api_core_v1beta2_MachineV1Beta1DeprecatedStatus(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.NetworkRanges": schema_cluster_api_api_core_v1beta2_NetworkRanges(ref), @@ -636,6 +647,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/cluster-api/api/core/v1beta2.PatchSelectorMatchMachineDeploymentClass": schema_cluster_api_api_core_v1beta2_PatchSelectorMatchMachineDeploymentClass(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.PatchSelectorMatchMachinePoolClass": schema_cluster_api_api_core_v1beta2_PatchSelectorMatchMachinePoolClass(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.Topology": schema_cluster_api_api_core_v1beta2_Topology(ref), + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition": schema_cluster_api_api_core_v1beta2_UnhealthyMachineCondition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition": schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.ValidationRule": schema_cluster_api_api_core_v1beta2_ValidationRule(ref), "sigs.k8s.io/cluster-api/api/core/v1beta2.VariableSchema": schema_cluster_api_api_core_v1beta2_VariableSchema(ref), @@ -2129,7 +2141,7 @@ func schema_k8sio_api_core_v1_Container(ref common.ReferenceCallback) common.Ope }, }, SchemaProps: spec.SchemaProps{ - Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Description: "List of sources to populate environment variables in the container. The keys defined within a source may consist of any printable ASCII characters except '='. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -2193,11 +2205,30 @@ func schema_k8sio_api_core_v1_Container(ref common.ReferenceCallback) common.Ope }, "restartPolicy": { SchemaProps: spec.SchemaProps{ - Description: "RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is \"Always\". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. Setting the RestartPolicy as \"Always\" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy \"Always\" will be shut down. This lifecycle differs from normal init containers and is often referred to as a \"sidecar\" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.", + Description: "RestartPolicy defines the restart behavior of individual containers in a pod. This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. Additionally, setting the RestartPolicy as \"Always\" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy \"Always\" will be shut down. This lifecycle differs from normal init containers and is often referred to as a \"sidecar\" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.", Type: []string{"string"}, Format: "", }, }, + "restartPolicyRules": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Represents a list of rules to be checked to determine if the container should be restarted on exit. The rules are evaluated in order. Once a rule matches a container exit condition, the remaining rules are ignored. If no rule matches the container exit condition, the Container-level restart policy determines the whether the container is restarted or not. Constraints on the rules: - At most 20 rules are allowed. - Rules can have the same action. - Identical rules are not forbidden in validations. When rules are specified, container MUST set RestartPolicy explicitly even it if matches the Pod's RestartPolicy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ContainerRestartRule"), + }, + }, + }, + }, + }, "volumeMounts": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ @@ -2325,7 +2356,45 @@ func schema_k8sio_api_core_v1_Container(ref common.ReferenceCallback) common.Ope }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.ContainerResizePolicy", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.ContainerResizePolicy", "k8s.io/api/core/v1.ContainerRestartRule", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + } +} + +func schema_k8sio_api_core_v1_ContainerExtendedResourceRequest(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerExtendedResourceRequest has the mapping of container name, extended resource name to the device request name.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "containerName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the container requesting resources.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the extended resource in that container which gets backed by DRA.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "requestName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the request in the special ResourceClaim which corresponds to the extended resource.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"containerName", "resourceName", "requestName"}, + }, + }, } } @@ -2451,6 +2520,76 @@ func schema_k8sio_api_core_v1_ContainerResizePolicy(ref common.ReferenceCallback } } +func schema_k8sio_api_core_v1_ContainerRestartRule(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerRestartRule describes how a container exit is handled.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "action": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the action taken on a container exit if the requirements are satisfied. The only possible value is \"Restart\" to restart the container.", + Type: []string{"string"}, + Format: "", + }, + }, + "exitCodes": { + SchemaProps: spec.SchemaProps{ + Description: "Represents the exit codes to check on container exits.", + Ref: ref("k8s.io/api/core/v1.ContainerRestartRuleOnExitCodes"), + }, + }, + }, + Required: []string{"action"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerRestartRuleOnExitCodes"}, + } +} + +func schema_k8sio_api_core_v1_ContainerRestartRuleOnExitCodes(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerRestartRuleOnExitCodes describes the condition for handling an exited container based on its exit codes.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "Represents the relationship between the container exit code(s) and the specified values. Possible values are: - In: the requirement is satisfied if the container exit code is in the\n set of specified values.\n- NotIn: the requirement is satisfied if the container exit code is\n not in the set of specified values.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Specifies the set of values to check for container exit codes. At most 255 elements are allowed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + }, + Required: []string{"operator"}, + }, + }, + } +} + func schema_k8sio_api_core_v1_ContainerState(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -3236,7 +3375,7 @@ func schema_k8sio_api_core_v1_EnvFromSource(ref common.ReferenceCallback) common Properties: map[string]spec.Schema{ "prefix": { SchemaProps: spec.SchemaProps{ - Description: "Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER.", + Description: "Optional text to prepend to the name of each environment variable. May consist of any printable ASCII characters except '='.", Type: []string{"string"}, Format: "", }, @@ -3270,7 +3409,7 @@ func schema_k8sio_api_core_v1_EnvVar(ref common.ReferenceCallback) common.OpenAP Properties: map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ - Description: "Name of the environment variable. Must be a C_IDENTIFIER.", + Description: "Name of the environment variable. May consist of any printable ASCII characters except '='.", Default: "", Type: []string{"string"}, Format: "", @@ -3329,11 +3468,17 @@ func schema_k8sio_api_core_v1_EnvVarSource(ref common.ReferenceCallback) common. Ref: ref("k8s.io/api/core/v1.SecretKeySelector"), }, }, + "fileKeyRef": { + SchemaProps: spec.SchemaProps{ + Description: "FileKeyRef selects a key of the env file. Requires the EnvFiles feature gate to be enabled.", + Ref: ref("k8s.io/api/core/v1.FileKeySelector"), + }, + }, }, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ConfigMapKeySelector", "k8s.io/api/core/v1.ObjectFieldSelector", "k8s.io/api/core/v1.ResourceFieldSelector", "k8s.io/api/core/v1.SecretKeySelector"}, + "k8s.io/api/core/v1.ConfigMapKeySelector", "k8s.io/api/core/v1.FileKeySelector", "k8s.io/api/core/v1.ObjectFieldSelector", "k8s.io/api/core/v1.ResourceFieldSelector", "k8s.io/api/core/v1.SecretKeySelector"}, } } @@ -3438,7 +3583,7 @@ func schema_k8sio_api_core_v1_EphemeralContainer(ref common.ReferenceCallback) c }, }, SchemaProps: spec.SchemaProps{ - Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Description: "List of sources to populate environment variables in the container. The keys defined within a source may consist of any printable ASCII characters except '='. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3502,11 +3647,30 @@ func schema_k8sio_api_core_v1_EphemeralContainer(ref common.ReferenceCallback) c }, "restartPolicy": { SchemaProps: spec.SchemaProps{ - Description: "Restart policy for the container to manage the restart behavior of each container within a pod. This may only be set for init containers. You cannot set this field on ephemeral containers.", + Description: "Restart policy for the container to manage the restart behavior of each container within a pod. You cannot set this field on ephemeral containers.", Type: []string{"string"}, Format: "", }, }, + "restartPolicyRules": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Represents a list of rules to be checked to determine if the container should be restarted on exit. You cannot set this field on ephemeral containers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ContainerRestartRule"), + }, + }, + }, + }, + }, "volumeMounts": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ @@ -3641,7 +3805,7 @@ func schema_k8sio_api_core_v1_EphemeralContainer(ref common.ReferenceCallback) c }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.ContainerResizePolicy", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.ContainerResizePolicy", "k8s.io/api/core/v1.ContainerRestartRule", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, } } @@ -3746,7 +3910,7 @@ func schema_k8sio_api_core_v1_EphemeralContainerCommon(ref common.ReferenceCallb }, }, SchemaProps: spec.SchemaProps{ - Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Description: "List of sources to populate environment variables in the container. The keys defined within a source may consist of any printable ASCII characters except '='. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3810,11 +3974,30 @@ func schema_k8sio_api_core_v1_EphemeralContainerCommon(ref common.ReferenceCallb }, "restartPolicy": { SchemaProps: spec.SchemaProps{ - Description: "Restart policy for the container to manage the restart behavior of each container within a pod. This may only be set for init containers. You cannot set this field on ephemeral containers.", + Description: "Restart policy for the container to manage the restart behavior of each container within a pod. You cannot set this field on ephemeral containers.", Type: []string{"string"}, Format: "", }, }, + "restartPolicyRules": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Represents a list of rules to be checked to determine if the container should be restarted on exit. You cannot set this field on ephemeral containers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ContainerRestartRule"), + }, + }, + }, + }, + }, "volumeMounts": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ @@ -3942,7 +4125,7 @@ func schema_k8sio_api_core_v1_EphemeralContainerCommon(ref common.ReferenceCallb }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.ContainerResizePolicy", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.ContainerResizePolicy", "k8s.io/api/core/v1.ContainerRestartRule", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, } } @@ -4312,6 +4495,57 @@ func schema_k8sio_api_core_v1_FCVolumeSource(ref common.ReferenceCallback) commo } } +func schema_k8sio_api_core_v1_FileKeySelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "FileKeySelector selects a key of the env file.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the volume mount containing the env file.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "The path within the volume from which to select the file. Must be relative and may not contain the '..' path or start with '..'.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The key within the env file. An invalid key will prevent the pod from starting. The keys defined within a source may consist of any printable ASCII characters except '='. During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the file or its key must be defined. If the file or key does not exist, then the env var is not published. If optional is set to true and the specified key does not exist, the environment variable will not be set in the Pod's containers.\n\nIf optional is set to false and the specified key does not exist, an error will be returned during Pod creation.", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"volumeName", "path", "key"}, + }, + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-map-type": "atomic", + }, + }, + }, + } +} + func schema_k8sio_api_core_v1_FlexPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4621,7 +4855,7 @@ func schema_k8sio_api_core_v1_GlusterfsVolumeSource(ref common.ReferenceCallback Properties: map[string]spec.Schema{ "endpoints": { SchemaProps: spec.SchemaProps{ - Description: "endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Description: "endpoints is the endpoint name that details Glusterfs topology.", Default: "", Type: []string{"string"}, Format: "", @@ -7331,7 +7565,7 @@ func schema_k8sio_api_core_v1_PersistentVolumeClaimSpec(ref common.ReferenceCall }, "volumeAttributesClassName": { SchemaProps: spec.SchemaProps{ - Description: "volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. If specified, the CSI driver will create or update the volume with the attributes defined in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass will be applied to the claim but it's not allowed to reset this field to empty string once it is set. If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass will be set by the persistentvolume controller if it exists. If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default).", + Description: "volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim. If specified, the CSI driver will create or update the volume with the attributes defined in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName, it can be changed after the claim is created. An empty string or nil value indicates that no VolumeAttributesClass will be applied to the claim. If the claim enters an Infeasible error state, this field can be reset to its previous value (including nil) to cancel the modification. If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/", Type: []string{"string"}, Format: "", }, @@ -7456,14 +7690,14 @@ func schema_k8sio_api_core_v1_PersistentVolumeClaimStatus(ref common.ReferenceCa }, "currentVolumeAttributesClassName": { SchemaProps: spec.SchemaProps{ - Description: "currentVolumeAttributesClassName is the current name of the VolumeAttributesClass the PVC is using. When unset, there is no VolumeAttributeClass applied to this PersistentVolumeClaim This is a beta field and requires enabling VolumeAttributesClass feature (off by default).", + Description: "currentVolumeAttributesClassName is the current name of the VolumeAttributesClass the PVC is using. When unset, there is no VolumeAttributeClass applied to this PersistentVolumeClaim", Type: []string{"string"}, Format: "", }, }, "modifyVolumeStatus": { SchemaProps: spec.SchemaProps{ - Description: "ModifyVolumeStatus represents the status object of ControllerModifyVolume operation. When this is unset, there is no ModifyVolume operation being attempted. This is a beta field and requires enabling VolumeAttributesClass feature (off by default).", + Description: "ModifyVolumeStatus represents the status object of ControllerModifyVolume operation. When this is unset, there is no ModifyVolume operation being attempted.", Ref: ref("k8s.io/api/core/v1.ModifyVolumeStatus"), }, }, @@ -7968,7 +8202,7 @@ func schema_k8sio_api_core_v1_PersistentVolumeSpec(ref common.ReferenceCallback) }, "volumeAttributesClassName": { SchemaProps: spec.SchemaProps{ - Description: "Name of VolumeAttributesClass to which this persistent volume belongs. Empty value is not allowed. When this field is not set, it indicates that this volume does not belong to any VolumeAttributesClass. This field is mutable and can be changed by the CSI driver after a volume has been updated successfully to a new class. For an unbound PersistentVolume, the volumeAttributesClassName will be matched with unbound PersistentVolumeClaims during the binding process. This is a beta field and requires enabling VolumeAttributesClass feature (off by default).", + Description: "Name of VolumeAttributesClass to which this persistent volume belongs. Empty value is not allowed. When this field is not set, it indicates that this volume does not belong to any VolumeAttributesClass. This field is mutable and can be changed by the CSI driver after a volume has been updated successfully to a new class. For an unbound PersistentVolume, the volumeAttributesClassName will be matched with unbound PersistentVolumeClaims during the binding process.", Type: []string{"string"}, Format: "", }, @@ -8285,7 +8519,7 @@ func schema_k8sio_api_core_v1_PodAntiAffinity(ref common.ReferenceCallback) comm }, }, SchemaProps: spec.SchemaProps{ - Description: "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + Description: "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and subtracting \"weight\" from the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -8367,6 +8601,62 @@ func schema_k8sio_api_core_v1_PodAttachOptions(ref common.ReferenceCallback) com } } +func schema_k8sio_api_core_v1_PodCertificateProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodCertificateProjection provides a private key and X.509 certificate in the pod filesystem.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "signerName": { + SchemaProps: spec.SchemaProps{ + Description: "Kubelet's generated CSRs will be addressed to this signer.", + Type: []string{"string"}, + Format: "", + }, + }, + "keyType": { + SchemaProps: spec.SchemaProps{ + Description: "The type of keypair Kubelet will generate for the pod.\n\nValid values are \"RSA3072\", \"RSA4096\", \"ECDSAP256\", \"ECDSAP384\", \"ECDSAP521\", and \"ED25519\".", + Type: []string{"string"}, + Format: "", + }, + }, + "maxExpirationSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "maxExpirationSeconds is the maximum lifetime permitted for the certificate.\n\nKubelet copies this value verbatim into the PodCertificateRequests it generates for this projection.\n\nIf omitted, kube-apiserver will set it to 86400(24 hours). kube-apiserver will reject values shorter than 3600 (1 hour). The maximum allowable value is 7862400 (91 days).\n\nThe signer implementation is then free to issue a certificate with any lifetime *shorter* than MaxExpirationSeconds, but no shorter than 3600 seconds (1 hour). This constraint is enforced by kube-apiserver. `kubernetes.io` signers will never issue certificates with a lifetime longer than 24 hours.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "credentialBundlePath": { + SchemaProps: spec.SchemaProps{ + Description: "Write the credential bundle at this path in the projected volume.\n\nThe credential bundle is a single file that contains multiple PEM blocks. The first PEM block is a PRIVATE KEY block, containing a PKCS#8 private key.\n\nThe remaining blocks are CERTIFICATE blocks, containing the issued certificate chain from the signer (leaf and any intermediates).\n\nUsing credentialBundlePath lets your Pod's application code make a single atomic read that retrieves a consistent key and certificate chain. If you project them to separate files, your application code will need to additionally check that the leaf certificate was issued to the key.", + Type: []string{"string"}, + Format: "", + }, + }, + "keyPath": { + SchemaProps: spec.SchemaProps{ + Description: "Write the key at this path in the projected volume.\n\nMost applications should use credentialBundlePath. When using keyPath and certificateChainPath, your application needs to check that the key and leaf certificate are consistent, because it is possible to read the files mid-rotation.", + Type: []string{"string"}, + Format: "", + }, + }, + "certificateChainPath": { + SchemaProps: spec.SchemaProps{ + Description: "Write the certificate chain at this path in the projected volume.\n\nMost applications should use credentialBundlePath. When using keyPath and certificateChainPath, your application needs to check that the key and leaf certificate are consistent, because it is possible to read the files mid-rotation.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"signerName", "keyType"}, + }, + }, + } +} + func schema_k8sio_api_core_v1_PodCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -8616,6 +8906,49 @@ func schema_k8sio_api_core_v1_PodExecOptions(ref common.ReferenceCallback) commo } } +func schema_k8sio_api_core_v1_PodExtendedResourceClaimStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodExtendedResourceClaimStatus is stored in the PodStatus for the extended resource requests backed by DRA. It stores the generated name for the corresponding special ResourceClaim created by the scheduler.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "requestMappings": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "RequestMappings identifies the mapping of to device request in the generated ResourceClaim.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ContainerExtendedResourceRequest"), + }, + }, + }, + }, + }, + "resourceClaimName": { + SchemaProps: spec.SchemaProps{ + Description: "ResourceClaimName is the name of the ResourceClaim that was generated for the Pod in the namespace of the Pod.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"requestMappings", "resourceClaimName"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerExtendedResourceRequest"}, + } +} + func schema_k8sio_api_core_v1_PodIP(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -9333,7 +9666,7 @@ func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenA }, "hostNetwork": { SchemaProps: spec.SchemaProps{ - Description: "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.", + Description: "Host networking requested for this pod. Use the host's network namespace. When using HostNetwork you should specify ports so the scheduler is aware. When `hostNetwork` is true, specified `hostPort` fields in port definitions must match `containerPort`, and unspecified `hostPort` fields in port definitions are defaulted to match `containerPort`. Default to false.", Type: []string{"boolean"}, Format: "", }, @@ -9568,7 +9901,7 @@ func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenA }, "os": { SchemaProps: spec.SchemaProps{ - Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", + Description: "Specifies the OS of the containers in the pod. Some pod and container fields are restricted if this is set.\n\nIf the OS field is set to linux, the following fields must be unset: -securityContext.windowsOptions\n\nIf the OS field is set to windows, following fields must be unset: - spec.hostPID - spec.hostIPC - spec.hostUsers - spec.resources - spec.securityContext.appArmorProfile - spec.securityContext.seLinuxOptions - spec.securityContext.seccompProfile - spec.securityContext.fsGroup - spec.securityContext.fsGroupChangePolicy - spec.securityContext.sysctls - spec.shareProcessNamespace - spec.securityContext.runAsUser - spec.securityContext.runAsGroup - spec.securityContext.supplementalGroups - spec.securityContext.supplementalGroupsPolicy - spec.containers[*].securityContext.appArmorProfile - spec.containers[*].securityContext.seLinuxOptions - spec.containers[*].securityContext.seccompProfile - spec.containers[*].securityContext.capabilities - spec.containers[*].securityContext.readOnlyRootFilesystem - spec.containers[*].securityContext.privileged - spec.containers[*].securityContext.allowPrivilegeEscalation - spec.containers[*].securityContext.procMount - spec.containers[*].securityContext.runAsUser - spec.containers[*].securityContext.runAsGroup", Ref: ref("k8s.io/api/core/v1.PodOS"), }, }, @@ -9629,10 +9962,17 @@ func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenA }, "resources": { SchemaProps: spec.SchemaProps{ - Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\" and \"memory\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", + Description: "Resources is the total amount of CPU and Memory resources required by all containers in the pod. It supports specifying Requests and Limits for \"cpu\", \"memory\" and \"hugepages-\" resource names only. ResourceClaims are not supported.\n\nThis field enables fine-grained control over resource allocation for the entire pod, allowing resource sharing among containers in a pod.\n\nThis is an alpha field and requires enabling the PodLevelResources feature gate.", Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), }, }, + "hostnameOverride": { + SchemaProps: spec.SchemaProps{ + Description: "HostnameOverride specifies an explicit override for the pod's hostname as perceived by the pod. This field only specifies the pod's hostname and does not affect its DNS records. When this field is set to a non-empty string: - It takes precedence over the values set in `hostname` and `subdomain`. - The Pod's hostname will be set to this value. - `setHostnameAsFQDN` must be nil or set to false. - `hostNetwork` must be set to false.\n\nThis field must be a valid DNS subdomain as defined in RFC 1123 and contain at most 64 characters. Requires the HostnameOverride feature gate to be enabled.", + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"containers"}, }, @@ -9870,11 +10210,17 @@ func schema_k8sio_api_core_v1_PodStatus(ref common.ReferenceCallback) common.Ope }, }, }, + "extendedResourceClaimStatus": { + SchemaProps: spec.SchemaProps{ + Description: "Status of extended resource claim backed by DRA.", + Ref: ref("k8s.io/api/core/v1.PodExtendedResourceClaimStatus"), + }, + }, }, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ContainerStatus", "k8s.io/api/core/v1.HostIP", "k8s.io/api/core/v1.PodCondition", "k8s.io/api/core/v1.PodIP", "k8s.io/api/core/v1.PodResourceClaimStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + "k8s.io/api/core/v1.ContainerStatus", "k8s.io/api/core/v1.HostIP", "k8s.io/api/core/v1.PodCondition", "k8s.io/api/core/v1.PodExtendedResourceClaimStatus", "k8s.io/api/core/v1.PodIP", "k8s.io/api/core/v1.PodResourceClaimStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } @@ -11271,7 +11617,7 @@ func schema_k8sio_api_core_v1_ResourceRequirements(ref common.ReferenceCallback) }, }, SchemaProps: spec.SchemaProps{ - Description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.", + Description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis field depends on the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -13049,7 +13395,7 @@ func schema_k8sio_api_core_v1_Taint(ref common.ReferenceCallback) common.OpenAPI }, "timeAdded": { SchemaProps: spec.SchemaProps{ - Description: "TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints.", + Description: "TimeAdded represents the time at which the taint was added.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, @@ -13428,13 +13774,13 @@ func schema_k8sio_api_core_v1_Volume(ref common.ReferenceCallback) common.OpenAP }, "iscsi": { SchemaProps: spec.SchemaProps{ - Description: "iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", + Description: "iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes/#iscsi", Ref: ref("k8s.io/api/core/v1.ISCSIVolumeSource"), }, }, "glusterfs": { SchemaProps: spec.SchemaProps{ - Description: "glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Description: "glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported.", Ref: ref("k8s.io/api/core/v1.GlusterfsVolumeSource"), }, }, @@ -13446,7 +13792,7 @@ func schema_k8sio_api_core_v1_Volume(ref common.ReferenceCallback) common.OpenAP }, "rbd": { SchemaProps: spec.SchemaProps{ - Description: "rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported. More info: https://examples.k8s.io/volumes/rbd/README.md", + Description: "rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported.", Ref: ref("k8s.io/api/core/v1.RBDVolumeSource"), }, }, @@ -13771,11 +14117,17 @@ func schema_k8sio_api_core_v1_VolumeProjection(ref common.ReferenceCallback) com Ref: ref("k8s.io/api/core/v1.ClusterTrustBundleProjection"), }, }, + "podCertificate": { + SchemaProps: spec.SchemaProps{ + Description: "Projects an auto-rotating credential bundle (private key and certificate chain) that the pod can use either as a TLS client or server.\n\nKubelet generates a private key and uses it to send a PodCertificateRequest to the named signer. Once the signer approves the request and issues a certificate chain, Kubelet writes the key and certificate chain to the pod filesystem. The pod does not start until certificates have been issued for each podCertificate projected volume source in its spec.\n\nKubelet will begin trying to rotate the certificate at the time indicated by the signer using the PodCertificateRequest.Status.BeginRefreshAt timestamp.\n\nKubelet can write a single file, indicated by the credentialBundlePath field, or separate files, indicated by the keyPath and certificateChainPath fields.\n\nThe credential bundle is a single file in PEM format. The first PEM entry is the private key (in PKCS#8 format), and the remaining PEM entries are the certificate chain issued by the signer (typically, signers will return their certificate chain in leaf-to-root order).\n\nPrefer using the credential bundle format, since your application code can read it atomically. If you use keyPath and certificateChainPath, your application must make two separate file reads. If these coincide with a certificate rotation, it is possible that the private key and leaf certificate you read may not correspond to each other. Your application will need to check for this condition, and re-read until they are consistent.\n\nThe named signer controls chooses the format of the certificate it issues; consult the signer implementation's documentation to learn how to use the certificates it issues.", + Ref: ref("k8s.io/api/core/v1.PodCertificateProjection"), + }, + }, }, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ClusterTrustBundleProjection", "k8s.io/api/core/v1.ConfigMapProjection", "k8s.io/api/core/v1.DownwardAPIProjection", "k8s.io/api/core/v1.SecretProjection", "k8s.io/api/core/v1.ServiceAccountTokenProjection"}, + "k8s.io/api/core/v1.ClusterTrustBundleProjection", "k8s.io/api/core/v1.ConfigMapProjection", "k8s.io/api/core/v1.DownwardAPIProjection", "k8s.io/api/core/v1.PodCertificateProjection", "k8s.io/api/core/v1.SecretProjection", "k8s.io/api/core/v1.ServiceAccountTokenProjection"}, } } @@ -13873,13 +14225,13 @@ func schema_k8sio_api_core_v1_VolumeSource(ref common.ReferenceCallback) common. }, "iscsi": { SchemaProps: spec.SchemaProps{ - Description: "iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", + Description: "iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes/#iscsi", Ref: ref("k8s.io/api/core/v1.ISCSIVolumeSource"), }, }, "glusterfs": { SchemaProps: spec.SchemaProps{ - Description: "glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Description: "glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported.", Ref: ref("k8s.io/api/core/v1.GlusterfsVolumeSource"), }, }, @@ -13891,7 +14243,7 @@ func schema_k8sio_api_core_v1_VolumeSource(ref common.ReferenceCallback) common. }, "rbd": { SchemaProps: spec.SchemaProps{ - Description: "rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported. More info: https://examples.k8s.io/volumes/rbd/README.md", + Description: "rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported.", Ref: ref("k8s.io/api/core/v1.RBDVolumeSource"), }, }, @@ -18054,6 +18406,26 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_BlockDeviceVolu } } +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ClusterInitialization(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterInitialization represents the initialization status of the cluster.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "provisioned": { + SchemaProps: spec.SchemaProps{ + Description: "Provisioned is set to true when the initial provisioning of the cluster infrastructure is completed. The value of this field is never updated after provisioning is completed.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_ExternalRouterIPParam(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -18354,6 +18726,26 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_LoadBalancer(re } } +func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_MachineInitialization(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "MachineInitialization contains information about the initialization status of the machine.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "provisioned": { + SchemaProps: spec.SchemaProps{ + Description: "Provisioned is set to true when the initial provisioning of the machine infrastructure is completed. The value of this field is never updated after provisioning is completed.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_MachineResources(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -19040,12 +19432,18 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_OpenStackCluste Properties: map[string]spec.Schema{ "ready": { SchemaProps: spec.SchemaProps{ - Description: "Ready is true when the cluster infrastructure is ready.", + Description: "Ready is true when the cluster infrastructure is ready.\n\nDeprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to determine the ready state of the cluster.", Default: false, Type: []string{"boolean"}, Format: "", }, }, + "initialization": { + SchemaProps: spec.SchemaProps{ + Description: "Initialization contains information about the initialization status of the cluster.", + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ClusterInitialization"), + }, + }, "network": { SchemaProps: spec.SchemaProps{ Description: "Network contains information about the created OpenStack Network.", @@ -19111,24 +19509,38 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_OpenStackCluste }, "failureReason": { SchemaProps: spec.SchemaProps{ - Description: "FailureReason will be set in the event that there is a terminal problem reconciling the OpenStackCluster and will contain a succinct value suitable for machine interpretation.\n\nThis field should not be set for transitive errors that a controller faces that are expected to be fixed automatically over time (like service outages), but instead indicate that something is fundamentally wrong with the OpenStackCluster's spec or the configuration of the controller, and that manual intervention is required. Examples of terminal errors would be invalid combinations of settings in the spec, values that are unsupported by the controller, or the responsible controller itself being critically misconfigured.\n\nAny transient errors that occur during the reconciliation of OpenStackClusters can be added as events to the OpenStackCluster object and/or logged in the controller's output.", + Description: "FailureReason will be set in the event that there is a terminal problem reconciling the OpenStackCluster and will contain a succinct value suitable for machine interpretation.\n\nThis field should not be set for transitive errors that a controller faces that are expected to be fixed automatically over time (like service outages), but instead indicate that something is fundamentally wrong with the OpenStackCluster's spec or the configuration of the controller, and that manual intervention is required. Examples of terminal errors would be invalid combinations of settings in the spec, values that are unsupported by the controller, or the responsible controller itself being critically misconfigured.\n\nAny transient errors that occur during the reconciliation of OpenStackClusters can be added as events to the OpenStackCluster object and/or logged in the controller's output.\n\nDeprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to report failures.", Type: []string{"string"}, Format: "", }, }, "failureMessage": { SchemaProps: spec.SchemaProps{ - Description: "FailureMessage will be set in the event that there is a terminal problem reconciling the OpenStackCluster and will contain a more verbose string suitable for logging and human consumption.\n\nThis field should not be set for transitive errors that a controller faces that are expected to be fixed automatically over time (like service outages), but instead indicate that something is fundamentally wrong with the OpenStackCluster's spec or the configuration of the controller, and that manual intervention is required. Examples of terminal errors would be invalid combinations of settings in the spec, values that are unsupported by the controller, or the responsible controller itself being critically misconfigured.\n\nAny transient errors that occur during the reconciliation of OpenStackClusters can be added as events to the OpenStackCluster object and/or logged in the controller's output.", + Description: "FailureMessage will be set in the event that there is a terminal problem reconciling the OpenStackCluster and will contain a more verbose string suitable for logging and human consumption.\n\nThis field should not be set for transitive errors that a controller faces that are expected to be fixed automatically over time (like service outages), but instead indicate that something is fundamentally wrong with the OpenStackCluster's spec or the configuration of the controller, and that manual intervention is required. Examples of terminal errors would be invalid combinations of settings in the spec, values that are unsupported by the controller, or the responsible controller itself being critically misconfigured.\n\nAny transient errors that occur during the reconciliation of OpenStackClusters can be added as events to the OpenStackCluster object and/or logged in the controller's output.\n\nDeprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to report failures.", Type: []string{"string"}, Format: "", }, }, + "conditions": { + SchemaProps: spec.SchemaProps{ + Description: "Conditions defines current service state of the OpenStackCluster. This field surfaces into Cluster's status.conditions[InfrastructureReady] condition. The Ready condition must surface issues during the entire lifecycle of the OpenStackCluster (both during initial provisioning and after the initial provisioning is completed).", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta1.Condition"), + }, + }, + }, + }, + }, }, Required: []string{"ready"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BastionStatus", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.LoadBalancer", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkStatus", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkStatusWithSubnets", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.Router", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupStatus", "sigs.k8s.io/cluster-api/api/core/v1beta1.FailureDomainSpec"}, + "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.BastionStatus", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ClusterInitialization", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.LoadBalancer", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkStatus", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.NetworkStatusWithSubnets", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.Router", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.SecurityGroupStatus", "sigs.k8s.io/cluster-api/api/core/v1beta1.Condition", "sigs.k8s.io/cluster-api/api/core/v1beta1.FailureDomainSpec"}, } } @@ -19618,12 +20030,18 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_OpenStackMachin Properties: map[string]spec.Schema{ "ready": { SchemaProps: spec.SchemaProps{ - Description: "Ready is true when the provider resource is ready.", + Description: "Ready is true when the provider resource is ready.\n\nDeprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to determine the ready state of the machine.", Default: false, Type: []string{"boolean"}, Format: "", }, }, + "initialization": { + SchemaProps: spec.SchemaProps{ + Description: "Initialization contains information about the initialization status of the machine.", + Ref: ref("sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.MachineInitialization"), + }, + }, "instanceID": { SchemaProps: spec.SchemaProps{ Description: "InstanceID is the OpenStack instance ID for this machine.", @@ -19666,20 +20084,22 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_OpenStackMachin }, "failureReason": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "FailureReason explains the reson behind a failure.\n\nDeprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to report failures.", + Type: []string{"string"}, + Format: "", }, }, "failureMessage": { SchemaProps: spec.SchemaProps{ - Description: "FailureMessage will be set in the event that there is a terminal problem reconciling the Machine and will contain a more verbose string suitable for logging and human consumption.\n\nThis field should not be set for transitive errors that a controller faces that are expected to be fixed automatically over time (like service outages), but instead indicate that something is fundamentally wrong with the Machine's spec or the configuration of the controller, and that manual intervention is required. Examples of terminal errors would be invalid combinations of settings in the spec, values that are unsupported by the controller, or the responsible controller itself being critically misconfigured.\n\nAny transient errors that occur during the reconciliation of Machines can be added as events to the Machine object and/or logged in the controller's output.", + Description: "FailureMessage will be set in the event that there is a terminal problem reconciling the Machine and will contain a more verbose string suitable for logging and human consumption.\n\nThis field should not be set for transitive errors that a controller faces that are expected to be fixed automatically over time (like service outages), but instead indicate that something is fundamentally wrong with the Machine's spec or the configuration of the controller, and that manual intervention is required. Examples of terminal errors would be invalid combinations of settings in the spec, values that are unsupported by the controller, or the responsible controller itself being critically misconfigured.\n\nAny transient errors that occur during the reconciliation of Machines can be added as events to the Machine object and/or logged in the controller's output.\n\nDeprecated: This field is deprecated and will be removed in a future API version. Use status.conditions to report failures.", Type: []string{"string"}, Format: "", }, }, "conditions": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, + Description: "Conditions defines current service state of the OpenStackMachine. This field surfaces into Machine's status.conditions[InfrastructureReady] condition. The Ready condition must surface issues during the entire lifecycle of the OpenStackMachine (both during initial provisioning and after the initial provisioning is completed).", + Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -19694,7 +20114,7 @@ func schema_sigsk8sio_cluster_api_provider_openstack_api_v1beta1_OpenStackMachin }, }, Dependencies: []string{ - "k8s.io/api/core/v1.NodeAddress", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.MachineResources", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedMachineSpec", "sigs.k8s.io/cluster-api/api/core/v1beta1.Condition"}, + "k8s.io/api/core/v1.NodeAddress", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.MachineInitialization", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.MachineResources", "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1.ResolvedMachineSpec", "sigs.k8s.io/cluster-api/api/core/v1beta1.Condition"}, } } @@ -21428,7 +21848,6 @@ func schema_cluster_api_api_core_v1beta1_APIEndpoint(ref common.ReferenceCallbac }, }, }, - Required: []string{"host", "port"}, }, }, } @@ -27135,12 +27554,39 @@ func schema_cluster_api_api_core_v1beta2_ClusterClassSpec(ref common.ReferenceCa }, }, }, + "upgrade": { + SchemaProps: spec.SchemaProps{ + Description: "upgrade defines the upgrade configuration for clusters using this ClusterClass.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade"), + }, + }, + "kubernetesVersions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "kubernetesVersions is the list of Kubernetes versions that can be used for clusters using this ClusterClass. The list of version must be ordered from the older to the newer version, and there should be at least one version for every minor in between the first and the last version.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, Required: []string{"infrastructure", "controlPlane"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterAvailabilityGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassPatch", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable", "sigs.k8s.io/cluster-api/api/core/v1beta2.ControlPlaneClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.InfrastructureClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.WorkersClass"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterAvailabilityGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassPatch", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgrade", "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassVariable", "sigs.k8s.io/cluster-api/api/core/v1beta2.ControlPlaneClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.InfrastructureClass", "sigs.k8s.io/cluster-api/api/core/v1beta2.WorkersClass"}, } } @@ -27341,6 +27787,48 @@ func schema_cluster_api_api_core_v1beta2_ClusterClassTemplateReference(ref commo } } +func schema_cluster_api_api_core_v1beta2_ClusterClassUpgrade(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterClassUpgrade defines the upgrade configuration for clusters using the ClusterClass.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "external": { + SchemaProps: spec.SchemaProps{ + Description: "external defines external runtime extensions for upgrade operations.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/cluster-api/api/core/v1beta2.ClusterClassUpgradeExternal"}, + } +} + +func schema_cluster_api_api_core_v1beta2_ClusterClassUpgradeExternal(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterClassUpgradeExternal defines external runtime extensions for upgrade operations.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "generateUpgradePlanExtension": { + SchemaProps: spec.SchemaProps{ + Description: "generateUpgradePlanExtension references an extension which is called to generate upgrade plan.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_ClusterClassV1Beta1DeprecatedStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -28134,11 +28622,30 @@ func schema_cluster_api_api_core_v1beta2_ControlPlaneClassHealthCheckChecks(ref }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -28417,11 +28924,30 @@ func schema_cluster_api_api_core_v1beta2_ControlPlaneTopologyHealthCheckChecks(r }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -29438,11 +29964,30 @@ func schema_cluster_api_api_core_v1beta2_MachineDeploymentClassHealthCheckChecks }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -30218,11 +30763,30 @@ func schema_cluster_api_api_core_v1beta2_MachineDeploymentTopologyHealthCheckChe }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -30850,11 +31414,30 @@ func schema_cluster_api_api_core_v1beta2_MachineHealthCheckChecks(ref common.Ref }, }, }, + "unhealthyMachineConditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "unhealthyMachineConditions contains a list of the machine conditions that determine whether a machine is considered unhealthy. The conditions are combined in a logical OR, i.e. if any of the conditions is met, the machine is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyMachineCondition", "sigs.k8s.io/cluster-api/api/core/v1beta2.UnhealthyNodeCondition"}, } } @@ -32514,12 +33097,35 @@ func schema_cluster_api_api_core_v1beta2_MachineSpec(ref common.ReferenceCallbac Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec"), }, }, + "taints": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "key", + "effect", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "taints are the node taints that Cluster API will manage. This list is not necessarily complete: other Kubernetes components may add or remove other taints from nodes, e.g. the node controller might add the node.kubernetes.io/not-ready taint. Only those taints defined in this list will be added or removed by core Cluster API controllers.\n\nThere can be at most 64 taints. A pod would have to tolerate all existing taints to run on the corresponding node.\n\nNOTE: This list is implemented as a \"map\" type, meaning that individual elements can be managed by different owners.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint"), + }, + }, + }, + }, + }, }, Required: []string{"clusterName", "bootstrap", "infrastructureRef"}, }, }, Dependencies: []string{ - "sigs.k8s.io/cluster-api/api/core/v1beta2.Bootstrap", "sigs.k8s.io/cluster-api/api/core/v1beta2.ContractVersionedObjectReference", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineReadinessGate"}, + "sigs.k8s.io/cluster-api/api/core/v1beta2.Bootstrap", "sigs.k8s.io/cluster-api/api/core/v1beta2.ContractVersionedObjectReference", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineDeletionSpec", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineReadinessGate", "sigs.k8s.io/cluster-api/api/core/v1beta2.MachineTaint"}, } } @@ -32540,7 +33146,7 @@ func schema_cluster_api_api_core_v1beta2_MachineStatus(ref common.ReferenceCallb }, }, SchemaProps: spec.SchemaProps{ - Description: "conditions represents the observations of a Machine's current state. Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, NodeHealthy, Deleting, Paused. If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy.", + Description: "conditions represents the observations of a Machine's current state. Known condition types are Available, Ready, UpToDate, BootstrapConfigReady, InfrastructureReady, NodeReady, NodeHealthy, Updating, Deleting, Paused. If a MachineHealthCheck is targeting this machine, also HealthCheckSucceeded, OwnerRemediated conditions are added. Additionally control plane Machines controlled by KubeadmControlPlane will have following additional conditions: APIServerPodHealthy, ControllerManagerPodHealthy, SchedulerPodHealthy, EtcdPodHealthy, EtcdMemberHealthy.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -32632,6 +33238,49 @@ func schema_cluster_api_api_core_v1beta2_MachineStatus(ref common.ReferenceCallb } } +func schema_cluster_api_api_core_v1beta2_MachineTaint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "MachineTaint defines a taint equivalent to corev1.Taint, but additionally having a propagation field.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "key is the taint key to be applied to a node. Must be a valid qualified name of maximum size 63 characters with an optional subdomain prefix of maximum size 253 characters, separated by a `/`.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "value is the taint value corresponding to the taint key. It must be a valid label value of maximum size 63 characters.", + Type: []string{"string"}, + Format: "", + }, + }, + "effect": { + SchemaProps: spec.SchemaProps{ + Description: "effect is the effect for the taint. Valid values are NoSchedule, PreferNoSchedule and NoExecute.\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the taint. Currently enforced by NodeController.\n - `\"NoSchedule\"` Do not allow new pods to schedule onto the node unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running. Enforced by the scheduler.\n - `\"PreferNoSchedule\"` Like TaintEffectNoSchedule, but the scheduler tries not to schedule new pods onto the node, rather than prohibiting new pods from scheduling onto the node entirely. Enforced by the scheduler.", + Type: []string{"string"}, + Format: "", + Enum: []interface{}{"NoExecute", "NoSchedule", "PreferNoSchedule"}, + }, + }, + "propagation": { + SchemaProps: spec.SchemaProps{ + Description: "propagation defines how this taint should be propagated to nodes. Valid values are 'Always' and 'OnInitialization'. Always: The taint will be continuously reconciled. If it is not set for a node, it will be added during reconciliation. OnInitialization: The taint will be added during node initialization. If it gets removed from the node later on it will not get added again.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"key", "effect", "propagation"}, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_MachineTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -33036,6 +33685,41 @@ func schema_cluster_api_api_core_v1beta2_Topology(ref common.ReferenceCallback) } } +func schema_cluster_api_api_core_v1beta2_UnhealthyMachineCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UnhealthyMachineCondition represents a Machine condition type and value with a timeout specified as a duration. When the named condition has been in the given status for at least the timeout value, a machine is considered unhealthy.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "type of Machine condition", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "timeoutSeconds is the duration that a machine must be in a given status for, after which the machine is considered unhealthy. For example, with a value of \"3600\", the machine must match the status for at least 1 hour before being considered unhealthy.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"type", "status", "timeoutSeconds"}, + }, + }, + } +} + func schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -33059,7 +33743,7 @@ func schema_cluster_api_api_core_v1beta2_UnhealthyNodeCondition(ref common.Refer }, "timeoutSeconds": { SchemaProps: spec.SchemaProps{ - Description: "timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. For example, with a value of \"1h\", the node must match the status for at least 1 hour before being considered unhealthy.", + Description: "timeoutSeconds is the duration that a node must be in a given status for, after which the node is considered unhealthy. For example, with a value of \"3600\", the node must match the status for at least 1 hour before being considered unhealthy.", Type: []string{"integer"}, Format: "int32", }, diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml index 4cf9b3dae..4e7f4d60c 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -1295,9 +1295,6 @@ spec: description: port is the port on which the API server is serving. format: int32 type: integer - required: - - host - - port type: object controlPlaneOmitAvailabilityZone: description: |- @@ -2461,6 +2458,62 @@ spec: - id - name type: object + conditions: + description: |- + Conditions defines current service state of the OpenStackCluster. + This field surfaces into Cluster's status.conditions[InfrastructureReady] condition. + The Ready condition must surface issues during the entire lifecycle of the OpenStackCluster + (both during initial provisioning and after the initial provisioning is completed). + items: + description: Condition defines an observation of a Cluster API resource + operational state. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This field may be empty. + maxLength: 10240 + minLength: 1 + type: string + reason: + description: |- + reason is the reason for the condition's last transition in CamelCase. + The specific API may choose whether or not this field is considered a guaranteed API. + This field may be empty. + maxLength: 256 + minLength: 1 + type: string + severity: + description: |- + severity provides an explicit classification of Reason code, so the users or machines can immediately + understand the current situation and act accordingly. + The Severity field MUST be set only when Status=False. + maxLength: 32 + type: string + status: + description: status of the condition, one of True, False, Unknown. + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability to deconflict is important. + maxLength: 256 + minLength: 1 + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array controlPlaneSecurityGroup: description: |- ControlPlaneSecurityGroup contains the information about the @@ -2530,6 +2583,9 @@ spec: Any transient errors that occur during the reconciliation of OpenStackClusters can be added as events to the OpenStackCluster object and/or logged in the controller's output. + + Deprecated: This field is deprecated and will be removed in a future API version. + Use status.conditions to report failures. type: string failureReason: description: |- @@ -2549,7 +2605,20 @@ spec: Any transient errors that occur during the reconciliation of OpenStackClusters can be added as events to the OpenStackCluster object and/or logged in the controller's output. + + Deprecated: This field is deprecated and will be removed in a future API version. + Use status.conditions to report failures. type: string + initialization: + description: Initialization contains information about the initialization + status of the cluster. + properties: + provisioned: + description: |- + Provisioned is set to true when the initial provisioning of the cluster infrastructure is completed. + The value of this field is never updated after provisioning is completed. + type: boolean + type: object network: description: Network contains information about the created OpenStack Network. @@ -2592,7 +2661,11 @@ spec: type: object ready: default: false - description: Ready is true when the cluster infrastructure is ready. + description: |- + Ready is true when the cluster infrastructure is ready. + + Deprecated: This field is deprecated and will be removed in a future API version. + Use status.conditions to determine the ready state of the cluster. type: boolean router: description: Router describes the default cluster router diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml index 084846a63..d13da4679 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml @@ -1301,9 +1301,6 @@ spec: is serving. format: int32 type: integer - required: - - host - - port type: object controlPlaneOmitAvailabilityZone: description: |- diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml index 0dc88ef8c..e752a7d2f 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml @@ -967,8 +967,11 @@ spec: type: object type: array conditions: - description: Conditions provide observations of the operational state - of a Cluster API resource. + description: |- + Conditions defines current service state of the OpenStackMachine. + This field surfaces into Machine's status.conditions[InfrastructureReady] condition. + The Ready condition must surface issues during the entire lifecycle of the OpenStackMachine + (both during initial provisioning and after the initial provisioning is completed). items: description: Condition defines an observation of a Cluster API resource operational state. @@ -1037,11 +1040,27 @@ spec: Any transient errors that occur during the reconciliation of Machines can be added as events to the Machine object and/or logged in the controller's output. + + Deprecated: This field is deprecated and will be removed in a future API version. + Use status.conditions to report failures. type: string failureReason: - description: DeprecatedCAPIMachineStatusError defines errors states - for Machine objects. + description: |- + FailureReason explains the reson behind a failure. + + Deprecated: This field is deprecated and will be removed in a future API version. + Use status.conditions to report failures. type: string + initialization: + description: Initialization contains information about the initialization + status of the machine. + properties: + provisioned: + description: |- + Provisioned is set to true when the initial provisioning of the machine infrastructure is completed. + The value of this field is never updated after provisioning is completed. + type: boolean + type: object instanceID: description: InstanceID is the OpenStack instance ID for this machine. type: string @@ -1052,7 +1071,11 @@ spec: Instead, it's set by the OpenStackServer controller. type: string ready: - description: Ready is true when the provider resource is ready. + description: |- + Ready is true when the provider resource is ready. + + Deprecated: This field is deprecated and will be removed in a future API version. + Use status.conditions to determine the ready state of the machine. type: boolean resolved: description: |- diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 720fff6c0..ff5280841 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -21,134 +21,138 @@ patches: - path: manager_webhook_patch.yaml replacements: - - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs +- source: + # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml + fieldPath: .metadata.namespace # namespace of the certificate CR + targets: + - select: + kind: ValidatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: MutatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: CustomResourceDefinition + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true +- source: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml + fieldPath: .metadata.name + targets: + - select: + kind: ValidatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: MutatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: CustomResourceDefinition + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true +- source: + # Add cert-manager annotation to the webhook Service + kind: Service + version: v1 + name: webhook-service + fieldPath: .metadata.name # namespace of the service + targets: + - select: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # this name should match the one in certificate.yaml - fieldPath: .metadata.namespace # namespace of the certificate CR - targets: - - select: - kind: ValidatingWebhookConfiguration - fieldPaths: - - .metadata.annotations.[cert-manager.io/inject-ca-from] - options: - delimiter: '/' - index: 0 - create: true - - select: - kind: MutatingWebhookConfiguration - fieldPaths: - - .metadata.annotations.[cert-manager.io/inject-ca-from] - options: - delimiter: '/' - index: 0 - create: true - - select: - kind: CustomResourceDefinition - fieldPaths: - - .metadata.annotations.[cert-manager.io/inject-ca-from] - options: - delimiter: '/' - index: 0 - create: true - - source: + name: serving-cert + fieldPaths: + - .spec.dnsNames.0 + - .spec.dnsNames.1 + options: + delimiter: '.' + index: 0 + create: false +- source: + kind: Service + version: v1 + name: webhook-service + fieldPath: .metadata.namespace # namespace of the service + targets: + - select: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # this name should match the one in certificate.yaml - fieldPath: .metadata.name - targets: - - select: - kind: ValidatingWebhookConfiguration - fieldPaths: - - .metadata.annotations.[cert-manager.io/inject-ca-from] - options: - delimiter: '/' - index: 1 - create: true - - select: - kind: MutatingWebhookConfiguration - fieldPaths: - - .metadata.annotations.[cert-manager.io/inject-ca-from] - options: - delimiter: '/' - index: 1 - create: true - - select: - kind: CustomResourceDefinition - fieldPaths: - - .metadata.annotations.[cert-manager.io/inject-ca-from] - options: - delimiter: '/' - index: 1 - create: true - - source: # Add cert-manager annotation to the webhook Service - kind: Service - version: v1 - name: webhook-service - fieldPath: .metadata.name # namespace of the service - targets: - - select: - kind: Certificate - group: cert-manager.io - version: v1 - name: serving-cert - fieldPaths: - - .spec.dnsNames.0 - - .spec.dnsNames.1 - options: - delimiter: '.' - index: 0 - create: false - - source: - kind: Service - version: v1 - name: webhook-service - fieldPath: .metadata.namespace # namespace of the service - targets: - - select: - kind: Certificate - group: cert-manager.io - version: v1 - name: serving-cert - fieldPaths: - - .spec.dnsNames.0 - - .spec.dnsNames.1 - options: - delimiter: '.' - index: 1 - create: false - - source: # Prefix the certificate secret name with the name of service - kind: Service - version: v1 - name: webhook-service - fieldPath: .metadata.name # namespace of the service - targets: - - select: - kind: Certificate - group: cert-manager.io - version: v1 - fieldPaths: - - .spec.secretName - options: - delimiter: '-' - index: 0 - create: false - - source: # Certificate secret name + name: serving-cert + fieldPaths: + - .spec.dnsNames.0 + - .spec.dnsNames.1 + options: + delimiter: '.' + index: 1 + create: false +- source: + # Prefix the certificate secret name with the name of service + kind: Service + version: v1 + name: webhook-service + fieldPath: .metadata.name # namespace of the service + targets: + - select: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert - fieldPath: .spec.secretName - targets: - - select: - kind: Deployment - group: apps - version: v1 - name: controller-manager - fieldPaths: - - .spec.template.spec.volumes.[name=cert].secret.secretName + fieldPaths: + - .spec.secretName + options: + delimiter: '-' + index: 0 + create: false +- source: + # Certificate secret name + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert + fieldPath: .spec.secretName + targets: + - select: + kind: Deployment + group: apps + version: v1 + name: controller-manager + fieldPaths: + - .spec.template.spec.volumes.[name=cert].secret.secretName configurations: - - kustomizeconfig.yaml +- kustomizeconfig.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 4aab6e0d3..4177630c3 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -17,37 +17,38 @@ spec: control-plane: capo-controller-manager spec: containers: - - command: - - /manager - args: - - "--leader-elect" - - "--v=2" - - "--diagnostics-address=127.0.0.1:8080" - - "--insecure-diagnostics=true" - image: controller:latest - imagePullPolicy: Always - name: manager - ports: - - containerPort: 9440 - name: healthz - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: healthz - livenessProbe: - httpGet: - path: /healthz - port: healthz - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - privileged: false - runAsUser: 65532 - runAsGroup: 65532 - terminationMessagePolicy: FallbackToLogsOnError + - command: + - /manager + args: + - "--leader-elect" + - "--v=2" + - "--diagnostics-address=127.0.0.1:8080" + - "--insecure-diagnostics=true" + - "--feature-gates=PriorityQueue=${EXP_CAPO_PRIORITY_QUEUE:=false}" + image: controller:latest + imagePullPolicy: Always + name: manager + ports: + - containerPort: 9440 + name: healthz + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz + livenessProbe: + httpGet: + path: /healthz + port: healthz + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + runAsUser: 65532 + runAsGroup: 65532 + terminationMessagePolicy: FallbackToLogsOnError terminationGracePeriodSeconds: 10 securityContext: runAsNonRoot: true @@ -55,7 +56,7 @@ spec: type: RuntimeDefault serviceAccountName: manager tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane + - effect: NoSchedule + key: node-role.kubernetes.io/master + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index dc44ee6b1..70662a53b 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -24,6 +24,17 @@ rules: - get - list - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + - customresourcedefinitions/status + verbs: + - get + - list + - patch + - update + - watch - apiGroups: - authentication.k8s.io resources: @@ -51,10 +62,28 @@ rules: - infrastructure.cluster.x-k8s.io resources: - openstackclusteridentities + - openstackclustertemplates + - openstackmachinetemplates verbs: - get - list + - patch + - update - watch +- apiGroups: + - infrastructure.cluster.x-k8s.io + resources: + - openstackclusteridentities/status + - openstackclusters/status + - openstackclustertemplates/status + - openstackfloatingippools/status + - openstackmachines/status + - openstackmachinetemplates/status + - openstackservers/status + verbs: + - get + - patch + - update - apiGroups: - infrastructure.cluster.x-k8s.io resources: @@ -70,17 +99,6 @@ rules: - patch - update - watch -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - openstackclusters/status - - openstackfloatingippools/status - - openstackmachines/status - - openstackservers/status - verbs: - - get - - patch - - update - apiGroups: - ipam.cluster.x-k8s.io resources: diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml index 9cf26134e..36d4cc6e4 100644 --- a/config/webhook/kustomization.yaml +++ b/config/webhook/kustomization.yaml @@ -1,6 +1,3 @@ resources: - manifests.yaml - service.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml deleted file mode 100644 index 25e21e3c9..000000000 --- a/config/webhook/kustomizeconfig.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# the following config is for teaching kustomize where to look at when substituting vars. -# It requires kustomize v2.1.0 or newer to work properly. -nameReference: -- kind: Service - version: v1 - fieldSpecs: - - kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - - kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - -namespace: -- kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true -- kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true - -varReference: -- path: metadata/annotations diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml index 711977f54..db070dff2 100644 --- a/config/webhook/service.yaml +++ b/config/webhook/service.yaml @@ -5,5 +5,5 @@ metadata: namespace: system spec: ports: - - port: 443 - targetPort: webhook-server + - port: 443 + targetPort: webhook-server diff --git a/controllers/openstackcluster_controller.go b/controllers/openstackcluster_controller.go index 3989fc155..12a7843e0 100644 --- a/controllers/openstackcluster_controller.go +++ b/controllers/openstackcluster_controller.go @@ -37,6 +37,7 @@ import ( "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/collections" + v1beta1conditions "sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" @@ -356,6 +357,24 @@ func (r *OpenStackClusterReconciler) reconcileNormal(ctx context.Context, scope openStackCluster.Status.Ready = true openStackCluster.Status.FailureMessage = nil openStackCluster.Status.FailureReason = nil + + // Set initialization.provisioned to true when initial infrastructure provisioning is complete. + // This field should only be set once and never changed afterward, as per CAPI v1beta2 contract. + // We set it here after all core infrastructure (network, router, security groups, control plane endpoint) + // has been successfully provisioned. + if openStackCluster.Status.Initialization == nil { + openStackCluster.Status.Initialization = &infrav1.ClusterInitialization{} + } + if !openStackCluster.Status.Initialization.Provisioned { + openStackCluster.Status.Initialization.Provisioned = true + scope.Logger().Info("Initial cluster infrastructure provisioning completed") + } + + // Set the Ready condition to True when infrastructure is ready. + // This condition surfaces into Cluster's status.conditions[InfrastructureReady]. + // It reflects the current operational state of the cluster infrastructure. + v1beta1conditions.MarkTrue(openStackCluster, clusterv1beta1.ReadyCondition) + scope.Logger().Info("Reconciled Cluster created successfully") result, err := r.reconcileBastion(ctx, scope, cluster, openStackCluster) @@ -623,60 +642,68 @@ func getBastionSecurityGroupID(openStackCluster *infrav1.OpenStackCluster) *stri func resolveLoadBalancerNetwork(openStackCluster *infrav1.OpenStackCluster, networkingService *networking.Service) error { lbSpec := openStackCluster.Spec.APIServerLoadBalancer - if lbSpec.IsEnabled() { - lbStatus := openStackCluster.Status.APIServerLoadBalancer - if lbStatus == nil { - lbStatus = &infrav1.LoadBalancer{} - openStackCluster.Status.APIServerLoadBalancer = lbStatus + + // if lb is not enabled, return early + if !lbSpec.IsEnabled() { + return nil + } + + lbStatus := openStackCluster.Status.APIServerLoadBalancer + if lbStatus == nil { + lbStatus = &infrav1.LoadBalancer{} + openStackCluster.Status.APIServerLoadBalancer = lbStatus + } + + lbNetStatus := lbStatus.LoadBalancerNetwork + if lbNetStatus == nil { + lbNetStatus = &infrav1.NetworkStatusWithSubnets{ + NetworkStatus: infrav1.NetworkStatus{}, } + } - lbNetStatus := lbStatus.LoadBalancerNetwork - if lbNetStatus == nil { - lbNetStatus = &infrav1.NetworkStatusWithSubnets{ - NetworkStatus: infrav1.NetworkStatus{}, + if lbSpec.Network != nil { + lbNet, err := networkingService.GetNetworkByParam(lbSpec.Network) + if err != nil { + if errors.Is(err, capoerrors.ErrFilterMatch) { + handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to find loadbalancer network: %w", err), true) } + return fmt.Errorf("failed to find network: %w", err) } - if lbSpec.Network != nil { - lbNet, err := networkingService.GetNetworkByParam(lbSpec.Network) - if err != nil { - if errors.Is(err, capoerrors.ErrFilterMatch) { - handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to find loadbalancer network: %w", err), true) + lbNetStatus.Name = lbNet.Name + lbNetStatus.ID = lbNet.ID + lbNetStatus.Tags = lbNet.Tags + + // Filter out only relevant subnets specified by the spec + lbNetStatus.Subnets = []infrav1.Subnet{} + for _, s := range lbSpec.Subnets { + matchFound := false + for _, subnetID := range lbNet.Subnets { + subnet, err := networkingService.GetSubnetByParam(&s) + if s.ID != nil && subnetID == *s.ID && err == nil { + matchFound = true + lbNetStatus.Subnets = append( + lbNetStatus.Subnets, infrav1.Subnet{ + ID: subnet.ID, + Name: subnet.Name, + CIDR: subnet.CIDR, + Tags: subnet.Tags, + }) } - return fmt.Errorf("failed to find network: %w", err) } - - lbNetStatus.Name = lbNet.Name - lbNetStatus.ID = lbNet.ID - lbNetStatus.Tags = lbNet.Tags - - // Filter out only relevant subnets specified by the spec - lbNetStatus.Subnets = []infrav1.Subnet{} - for _, s := range lbSpec.Subnets { - matchFound := false - for _, subnetID := range lbNet.Subnets { - subnet, err := networkingService.GetSubnetByParam(&s) - if s.ID != nil && subnetID == *s.ID && err == nil { - matchFound = true - lbNetStatus.Subnets = append( - lbNetStatus.Subnets, infrav1.Subnet{ - ID: subnet.ID, - Name: subnet.Name, - CIDR: subnet.CIDR, - Tags: subnet.Tags, - }) - } - } - if !matchFound { - handleUpdateOSCError(openStackCluster, fmt.Errorf("no subnet match was found in the specified network (specified subnet: %v, available subnets: %v)", s, lbNet.Subnets), false) - return fmt.Errorf("no subnet match was found in the specified network (specified subnet: %v, available subnets: %v)", s, lbNet.Subnets) - } + if !matchFound { + handleUpdateOSCError(openStackCluster, fmt.Errorf("no subnet match was found in the specified network (specified subnet: %v, available subnets: %v)", s, lbNet.Subnets), false) + return fmt.Errorf("no subnet match was found in the specified network (specified subnet: %v, available subnets: %v)", s, lbNet.Subnets) } - - openStackCluster.Status.APIServerLoadBalancer.LoadBalancerNetwork = lbNetStatus } + } else { + lbNetStatus.ID = openStackCluster.Status.Network.ID + lbNetStatus.Name = openStackCluster.Status.Network.Name + lbNetStatus.Subnets = openStackCluster.Status.Network.Subnets } + openStackCluster.Status.APIServerLoadBalancer.LoadBalancerNetwork = lbNetStatus + return nil } @@ -716,11 +743,20 @@ func reconcileNetworkComponents(scope *scope.WithLogger, cluster *clusterv1.Clus err = networkingService.ReconcileSecurityGroups(openStackCluster, clusterResourceName) if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.SecurityGroupsReadyCondition, infrav1.SecurityGroupReconcileFailedReason, clusterv1beta1.ConditionSeverityError, "Failed to reconcile security groups: %v", err) handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile security groups: %w", err), false) return fmt.Errorf("failed to reconcile security groups: %w", err) } + v1beta1conditions.MarkTrue(openStackCluster, infrav1.SecurityGroupsReadyCondition) - return reconcileControlPlaneEndpoint(scope, networkingService, openStackCluster, clusterResourceName) + err = reconcileControlPlaneEndpoint(scope, networkingService, openStackCluster, clusterResourceName) + if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.APIEndpointReadyCondition, infrav1.APIEndpointConfigFailedReason, clusterv1beta1.ConditionSeverityError, "Failed to reconcile control plane endpoint: %v", err) + return err + } + v1beta1conditions.MarkTrue(openStackCluster, infrav1.APIEndpointReadyCondition) + + return nil } // reconcilePreExistingNetworkComponents reconciles the cluster network status when the cluster is @@ -736,6 +772,7 @@ func reconcilePreExistingNetworkComponents(scope *scope.WithLogger, networkingSe if openStackCluster.Spec.Network != nil { network, err := networkingService.GetNetworkByParam(openStackCluster.Spec.Network) if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.NetworkReadyCondition, infrav1.OpenStackErrorReason, clusterv1beta1.ConditionSeverityError, "Failed to find network: %v", err) handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to find network: %w", err), false) return fmt.Errorf("error fetching cluster network: %w", err) } @@ -744,6 +781,7 @@ func reconcilePreExistingNetworkComponents(scope *scope.WithLogger, networkingSe subnets, err := getClusterSubnets(networkingService, openStackCluster) if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.NetworkReadyCondition, infrav1.OpenStackErrorReason, clusterv1beta1.ConditionSeverityError, "Failed to get cluster subnets: %v", err) return err } @@ -759,6 +797,7 @@ func reconcilePreExistingNetworkComponents(scope *scope.WithLogger, networkingSe } } if err := utils.ValidateSubnets(capoSubnets); err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.NetworkReadyCondition, infrav1.OpenStackErrorReason, clusterv1beta1.ConditionSeverityError, "Failed to validate subnets: %v", err) return err } openStackCluster.Status.Network.Subnets = capoSubnets @@ -769,14 +808,18 @@ func reconcilePreExistingNetworkComponents(scope *scope.WithLogger, networkingSe if openStackCluster.Status.Network.ID == "" && len(subnets) > 0 { network, err := networkingService.GetNetworkByID(subnets[0].NetworkID) if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.NetworkReadyCondition, infrav1.OpenStackErrorReason, clusterv1beta1.ConditionSeverityError, "Failed to get network by ID: %v", err) return err } setClusterNetwork(openStackCluster, network) } + v1beta1conditions.MarkTrue(openStackCluster, infrav1.NetworkReadyCondition) + if openStackCluster.Spec.Router != nil { router, err := networkingService.GetRouterByParam(openStackCluster.Spec.Router) if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.RouterReadyCondition, infrav1.OpenStackErrorReason, clusterv1beta1.ConditionSeverityError, "Failed to find router: %v", err) handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to find router: %w", err), false) return fmt.Errorf("error fetching cluster router: %w", err) } @@ -794,6 +837,7 @@ func reconcilePreExistingNetworkComponents(scope *scope.WithLogger, networkingSe Tags: router.Tags, IPs: routerIPs, } + v1beta1conditions.MarkTrue(openStackCluster, infrav1.RouterReadyCondition) } return nil @@ -804,19 +848,25 @@ func reconcilePreExistingNetworkComponents(scope *scope.WithLogger, networkingSe func reconcileProvisionedNetworkComponents(networkingService *networking.Service, openStackCluster *infrav1.OpenStackCluster, clusterResourceName string) error { err := networkingService.ReconcileNetwork(openStackCluster, clusterResourceName) if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.NetworkReadyCondition, infrav1.NetworkReconcileFailedReason, clusterv1beta1.ConditionSeverityError, "Failed to reconcile network: %v", err) handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile network: %w", err), false) return fmt.Errorf("failed to reconcile network: %w", err) } err = networkingService.ReconcileSubnet(openStackCluster, clusterResourceName) if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.NetworkReadyCondition, infrav1.SubnetReconcileFailedReason, clusterv1beta1.ConditionSeverityError, "Failed to reconcile subnets: %v", err) handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile subnets: %w", err), false) return fmt.Errorf("failed to reconcile subnets: %w", err) } + v1beta1conditions.MarkTrue(openStackCluster, infrav1.NetworkReadyCondition) + err = networkingService.ReconcileRouter(openStackCluster, clusterResourceName) if err != nil { + v1beta1conditions.MarkFalse(openStackCluster, infrav1.RouterReadyCondition, infrav1.RouterReconcileFailedReason, clusterv1beta1.ConditionSeverityError, "Failed to reconcile router: %v", err) handleUpdateOSCError(openStackCluster, fmt.Errorf("failed to reconcile router: %w", err), false) return fmt.Errorf("failed to reconcile router: %w", err) } + v1beta1conditions.MarkTrue(openStackCluster, infrav1.RouterReadyCondition) return nil } @@ -947,6 +997,12 @@ func handleUpdateOSCError(openstackCluster *infrav1.OpenStackCluster, message er err := capoerrors.DeprecatedCAPOUpdateClusterError openstackCluster.Status.FailureReason = &err openstackCluster.Status.FailureMessage = ptr.To(message.Error()) + // Set the Ready condition to False for fatal errors + v1beta1conditions.MarkFalse(openstackCluster, clusterv1beta1.ReadyCondition, infrav1.OpenStackErrorReason, clusterv1beta1.ConditionSeverityError, "%v", message) + } else { + // For transient (non-fatal) errors, set Ready condition to False with Warning severity + // This indicates a temporary issue that may be resolved on retry + v1beta1conditions.MarkFalse(openstackCluster, clusterv1beta1.ReadyCondition, infrav1.OpenStackErrorReason, clusterv1beta1.ConditionSeverityWarning, "%v", message) } } diff --git a/controllers/openstackcluster_controller_test.go b/controllers/openstackcluster_controller_test.go index 548e7a28c..98f0bd4a8 100644 --- a/controllers/openstackcluster_controller_test.go +++ b/controllers/openstackcluster_controller_test.go @@ -22,7 +22,9 @@ import ( "reflect" "testing" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" . "github.com/onsi/ginkgo/v2" //nolint:revive @@ -35,6 +37,7 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/test/framework" "sigs.k8s.io/cluster-api/util/annotations" + v1beta1conditions "sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -446,6 +449,11 @@ var _ = Describe("OpenStackCluster controller", func() { err = reconcileNetworkComponents(scope, capiCluster, testCluster) Expect(err).To(BeNil()) + + // Verify conditions are set correctly + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.APIEndpointReadyCondition)).To(BeTrue()) }) It("should allow two subnets for the cluster network", func() { @@ -528,6 +536,11 @@ var _ = Describe("OpenStackCluster controller", func() { err = reconcileNetworkComponents(scope, capiCluster, testCluster) Expect(err).To(BeNil()) Expect(len(testCluster.Status.Network.Subnets)).To(Equal(2)) + + // Verify conditions are set correctly + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.APIEndpointReadyCondition)).To(BeTrue()) }) It("should allow fetch network by subnet", func() { @@ -574,6 +587,11 @@ var _ = Describe("OpenStackCluster controller", func() { err = reconcileNetworkComponents(scope, capiCluster, testCluster) Expect(err).To(BeNil()) Expect(testCluster.Status.Network.ID).To(Equal(clusterNetworkID)) + + // Verify conditions are set correctly + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.APIEndpointReadyCondition)).To(BeTrue()) }) It("reconcile pre-existing network components by id", func() { @@ -634,6 +652,10 @@ var _ = Describe("OpenStackCluster controller", func() { Expect(testCluster.Status.Network.ID).To(Equal(clusterNetworkID)) Expect(testCluster.Status.Network.Subnets[0].ID).To(Equal(clusterSubnetID)) Expect(testCluster.Status.Router.ID).To(Equal(clusterRouterID)) + + // Verify conditions are set correctly + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.RouterReadyCondition)).To(BeTrue()) }) It("reconcile pre-existing network components by name", func() { @@ -716,6 +738,462 @@ var _ = Describe("OpenStackCluster controller", func() { Expect(testCluster.Status.Network.ID).To(Equal(clusterNetworkID)) Expect(testCluster.Status.Network.Subnets[0].ID).To(Equal(clusterSubnetID)) Expect(testCluster.Status.Router.ID).To(Equal(clusterRouterID)) + + // Verify conditions are set correctly + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.RouterReadyCondition)).To(BeTrue()) + }) + + It("should reconcile API endpoint with floating IP and set condition", func() { + const externalNetworkID = "a42211a2-4d2c-426f-9413-830e4b4abbbc" + const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0" + const clusterSubnetID = "cad5a91a-36de-4388-823b-b0cc82cadfdc" + const floatingIP = "203.0.113.10" + + testCluster.SetName("api-endpoint-floating-ip") + testCluster.Spec = infrav1.OpenStackClusterSpec{ + IdentityRef: infrav1.OpenStackIdentityReference{ + Name: "test-creds", + CloudName: "openstack", + }, + ExternalNetwork: &infrav1.NetworkParam{ + ID: ptr.To(externalNetworkID), + }, + Network: &infrav1.NetworkParam{ + ID: ptr.To(clusterNetworkID), + }, + // When DisableAPIServerFloatingIP is not set and external network is configured, + // a floating IP should be created for the API server + } + err := k8sClient.Create(ctx, testCluster) + Expect(err).To(BeNil()) + err = k8sClient.Create(ctx, capiCluster) + Expect(err).To(BeNil()) + + log := GinkgoLogr + clientScope, err := mockScopeFactory.NewClientScopeFromObject(ctx, k8sClient, nil, log, testCluster) + Expect(err).To(BeNil()) + scope := scope.NewWithLogger(clientScope, log) + + networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() + + // Fetch external network + networkClientRecorder.GetNetwork(externalNetworkID).Return(&networks.Network{ + ID: externalNetworkID, + Name: "external-network", + }, nil) + + // Fetch cluster network + networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{ + ID: clusterNetworkID, + Name: "cluster-network", + }, nil) + + // Fetching cluster subnets + networkClientRecorder.ListSubnet(subnets.ListOpts{ + NetworkID: clusterNetworkID, + }).Return([]subnets.Subnet{ + { + ID: clusterSubnetID, + Name: "cluster-subnet", + CIDR: "192.168.0.0/24", + }, + }, nil) + + // Mock floating IP creation for API server + // When no specific IP is requested, it will just create a new floating IP + networkClientRecorder.CreateFloatingIP(gomock.Any()).Return(&floatingips.FloatingIP{ + FloatingIP: floatingIP, + ID: "floating-ip-id", + }, nil) + + err = reconcileNetworkComponents(scope, capiCluster, testCluster) + Expect(err).To(BeNil()) + + // Verify API endpoint was set + Expect(testCluster.Spec.ControlPlaneEndpoint).ToNot(BeNil()) + Expect(testCluster.Spec.ControlPlaneEndpoint.Host).To(Equal(floatingIP)) + Expect(testCluster.Spec.ControlPlaneEndpoint.Port).To(Equal(int32(6443))) + + // Verify conditions are set correctly + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.APIEndpointReadyCondition)).To(BeTrue()) + }) + + It("should reconcile API endpoint with fixed IP and set condition", func() { + const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0" + const clusterSubnetID = "cad5a91a-36de-4388-823b-b0cc82cadfdc" + const fixedIP = "192.168.0.10" + + testCluster.SetName("api-endpoint-fixed-ip") + testCluster.Spec = infrav1.OpenStackClusterSpec{ + IdentityRef: infrav1.OpenStackIdentityReference{ + Name: "test-creds", + CloudName: "openstack", + }, + Network: &infrav1.NetworkParam{ + ID: ptr.To(clusterNetworkID), + }, + DisableExternalNetwork: ptr.To(true), + DisableAPIServerFloatingIP: ptr.To(true), + APIServerFixedIP: ptr.To(fixedIP), + } + err := k8sClient.Create(ctx, testCluster) + Expect(err).To(BeNil()) + err = k8sClient.Create(ctx, capiCluster) + Expect(err).To(BeNil()) + + log := GinkgoLogr + clientScope, err := mockScopeFactory.NewClientScopeFromObject(ctx, k8sClient, nil, log, testCluster) + Expect(err).To(BeNil()) + scope := scope.NewWithLogger(clientScope, log) + + networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() + + // Fetch cluster network + networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{ + ID: clusterNetworkID, + Name: "cluster-network", + }, nil) + + // Fetching cluster subnets + networkClientRecorder.ListSubnet(subnets.ListOpts{ + NetworkID: clusterNetworkID, + }).Return([]subnets.Subnet{ + { + ID: clusterSubnetID, + Name: "cluster-subnet", + CIDR: "192.168.0.0/24", + }, + }, nil) + + err = reconcileNetworkComponents(scope, capiCluster, testCluster) + Expect(err).To(BeNil()) + + // Verify API endpoint was set with fixed IP + Expect(testCluster.Spec.ControlPlaneEndpoint).ToNot(BeNil()) + Expect(testCluster.Spec.ControlPlaneEndpoint.Host).To(Equal(fixedIP)) + Expect(testCluster.Spec.ControlPlaneEndpoint.Port).To(Equal(int32(6443))) + + // Verify conditions are set correctly + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.APIEndpointReadyCondition)).To(BeTrue()) + }) + + It("should set NetworkReadyCondition to False when network lookup fails", func() { + const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0" + + testCluster.SetName("network-lookup-failure") + testCluster.Spec = infrav1.OpenStackClusterSpec{ + IdentityRef: infrav1.OpenStackIdentityReference{ + Name: "test-creds", + CloudName: "openstack", + }, + Network: &infrav1.NetworkParam{ + ID: ptr.To(clusterNetworkID), + }, + DisableExternalNetwork: ptr.To(true), + DisableAPIServerFloatingIP: ptr.To(true), + APIServerFixedIP: ptr.To("192.168.0.10"), + } + err := k8sClient.Create(ctx, testCluster) + Expect(err).To(BeNil()) + err = k8sClient.Create(ctx, capiCluster) + Expect(err).To(BeNil()) + + log := GinkgoLogr + clientScope, err := mockScopeFactory.NewClientScopeFromObject(ctx, k8sClient, nil, log, testCluster) + Expect(err).To(BeNil()) + scope := scope.NewWithLogger(clientScope, log) + + networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() + + // Simulate network lookup failure + networkClientRecorder.GetNetwork(clusterNetworkID).Return(nil, fmt.Errorf("unable to get network")) + + err = reconcileNetworkComponents(scope, capiCluster, testCluster) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("error fetching cluster network")) + + // Verify NetworkReadyCondition is set to False + Expect(v1beta1conditions.IsFalse(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + condition := v1beta1conditions.Get(testCluster, infrav1.NetworkReadyCondition) + Expect(condition).ToNot(BeNil()) + Expect(condition.Reason).To(Equal(infrav1.OpenStackErrorReason)) + Expect(condition.Severity).To(Equal(clusterv1beta1.ConditionSeverityError)) + Expect(condition.Message).To(ContainSubstring("Failed to find network")) + }) + + It("should set NetworkReadyCondition to False when subnet lookup fails", func() { + const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0" + + testCluster.SetName("subnet-lookup-failure") + testCluster.Spec = infrav1.OpenStackClusterSpec{ + IdentityRef: infrav1.OpenStackIdentityReference{ + Name: "test-creds", + CloudName: "openstack", + }, + Network: &infrav1.NetworkParam{ + ID: ptr.To(clusterNetworkID), + }, + DisableExternalNetwork: ptr.To(true), + DisableAPIServerFloatingIP: ptr.To(true), + APIServerFixedIP: ptr.To("192.168.0.10"), + } + err := k8sClient.Create(ctx, testCluster) + Expect(err).To(BeNil()) + err = k8sClient.Create(ctx, capiCluster) + Expect(err).To(BeNil()) + + log := GinkgoLogr + clientScope, err := mockScopeFactory.NewClientScopeFromObject(ctx, k8sClient, nil, log, testCluster) + Expect(err).To(BeNil()) + scope := scope.NewWithLogger(clientScope, log) + + networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() + + // Network lookup succeeds + networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{ + ID: clusterNetworkID, + Name: "cluster-network", + }, nil) + + // Subnet list lookup fails + networkClientRecorder.ListSubnet(subnets.ListOpts{ + NetworkID: clusterNetworkID, + }).Return(nil, fmt.Errorf("failed to list subnets")) + + err = reconcileNetworkComponents(scope, capiCluster, testCluster) + Expect(err).ToNot(BeNil()) + + // Verify NetworkReadyCondition is set to False + Expect(v1beta1conditions.IsFalse(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + condition := v1beta1conditions.Get(testCluster, infrav1.NetworkReadyCondition) + Expect(condition).ToNot(BeNil()) + Expect(condition.Reason).To(Equal(infrav1.OpenStackErrorReason)) + Expect(condition.Severity).To(Equal(clusterv1beta1.ConditionSeverityError)) + }) + + It("should set RouterReadyCondition to False when router lookup fails", func() { + const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0" + const clusterSubnetID = "cad5a91a-36de-4388-823b-b0cc82cadfdc" + const clusterRouterID = "a0e2b0a5-4d2f-4e8d-9a1c-6b3e7f8c9d0e" + + testCluster.SetName("router-lookup-failure") + testCluster.Spec = infrav1.OpenStackClusterSpec{ + IdentityRef: infrav1.OpenStackIdentityReference{ + Name: "test-creds", + CloudName: "openstack", + }, + Network: &infrav1.NetworkParam{ + ID: ptr.To(clusterNetworkID), + }, + Router: &infrav1.RouterParam{ + ID: ptr.To(clusterRouterID), + }, + DisableExternalNetwork: ptr.To(true), + DisableAPIServerFloatingIP: ptr.To(true), + APIServerFixedIP: ptr.To("192.168.0.10"), + } + err := k8sClient.Create(ctx, testCluster) + Expect(err).To(BeNil()) + err = k8sClient.Create(ctx, capiCluster) + Expect(err).To(BeNil()) + + log := GinkgoLogr + clientScope, err := mockScopeFactory.NewClientScopeFromObject(ctx, k8sClient, nil, log, testCluster) + Expect(err).To(BeNil()) + scope := scope.NewWithLogger(clientScope, log) + + networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() + + // Network lookup succeeds + networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{ + ID: clusterNetworkID, + Name: "cluster-network", + }, nil) + + // Subnet lookup succeeds + networkClientRecorder.ListSubnet(subnets.ListOpts{ + NetworkID: clusterNetworkID, + }).Return([]subnets.Subnet{ + { + ID: clusterSubnetID, + Name: "cluster-subnet", + CIDR: "192.168.0.0/24", + }, + }, nil) + + // Router lookup fails + networkClientRecorder.GetRouter(clusterRouterID).Return(nil, fmt.Errorf("unable to get router")) + + err = reconcileNetworkComponents(scope, capiCluster, testCluster) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("error fetching cluster router")) + + // Verify RouterReadyCondition is set to False + Expect(v1beta1conditions.IsFalse(testCluster, infrav1.RouterReadyCondition)).To(BeTrue()) + condition := v1beta1conditions.Get(testCluster, infrav1.RouterReadyCondition) + Expect(condition).ToNot(BeNil()) + Expect(condition.Reason).To(Equal(infrav1.OpenStackErrorReason)) + Expect(condition.Severity).To(Equal(clusterv1beta1.ConditionSeverityError)) + Expect(condition.Message).To(ContainSubstring("Failed to find router")) + + // NetworkReadyCondition should still be True since network succeeded + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + }) + + It("should set SecurityGroupsReadyCondition to False when security group reconciliation fails", func() { + const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0" + const clusterSubnetID = "cad5a91a-36de-4388-823b-b0cc82cadfdc" + + testCluster.SetName("security-group-failure") + testCluster.Spec = infrav1.OpenStackClusterSpec{ + IdentityRef: infrav1.OpenStackIdentityReference{ + Name: "test-creds", + CloudName: "openstack", + }, + Network: &infrav1.NetworkParam{ + ID: ptr.To(clusterNetworkID), + }, + DisableExternalNetwork: ptr.To(true), + DisableAPIServerFloatingIP: ptr.To(true), + APIServerFixedIP: ptr.To("192.168.0.10"), + ManagedSecurityGroups: &infrav1.ManagedSecurityGroups{ + AllNodesSecurityGroupRules: []infrav1.SecurityGroupRuleSpec{ + { + Direction: "ingress", + Protocol: ptr.To("tcp"), + RemoteManagedGroups: []infrav1.ManagedSecurityGroupName{ + "worker", + }, + }, + }, + }, + } + err := k8sClient.Create(ctx, testCluster) + Expect(err).To(BeNil()) + err = k8sClient.Create(ctx, capiCluster) + Expect(err).To(BeNil()) + + log := GinkgoLogr + clientScope, err := mockScopeFactory.NewClientScopeFromObject(ctx, k8sClient, nil, log, testCluster) + Expect(err).To(BeNil()) + scope := scope.NewWithLogger(clientScope, log) + + networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() + + // Network lookup succeeds + networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{ + ID: clusterNetworkID, + Name: "cluster-network", + }, nil) + + // Subnet lookup succeeds + networkClientRecorder.ListSubnet(subnets.ListOpts{ + NetworkID: clusterNetworkID, + }).Return([]subnets.Subnet{ + { + ID: clusterSubnetID, + Name: "cluster-subnet", + CIDR: "192.168.0.0/24", + }, + }, nil) + + // Security group creation fails - this will trigger an error in getOrCreateSecurityGroup + networkClientRecorder.ListSecGroup(gomock.Any()).Return([]groups.SecGroup{}, nil).AnyTimes() + networkClientRecorder.CreateSecGroup(gomock.Any()).Return(nil, fmt.Errorf("quota exceeded")).AnyTimes() + + err = reconcileNetworkComponents(scope, capiCluster, testCluster) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("failed to reconcile security groups")) + + // Verify SecurityGroupsReadyCondition is set to False + Expect(v1beta1conditions.IsFalse(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue()) + condition := v1beta1conditions.Get(testCluster, infrav1.SecurityGroupsReadyCondition) + Expect(condition).ToNot(BeNil()) + Expect(condition.Reason).To(Equal(infrav1.SecurityGroupReconcileFailedReason)) + Expect(condition.Severity).To(Equal(clusterv1beta1.ConditionSeverityError)) + Expect(condition.Message).To(ContainSubstring("Failed to reconcile security groups")) + + // NetworkReadyCondition should still be True since network succeeded + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + }) + + It("should set APIEndpointReadyCondition to False when floating IP creation fails", func() { + const externalNetworkID = "a42211a2-4d2c-426f-9413-830e4b4abbbc" + const clusterNetworkID = "6c90b532-7ba0-418a-a276-5ae55060b5b0" + const clusterSubnetID = "cad5a91a-36de-4388-823b-b0cc82cadfdc" + + testCluster.SetName("floating-ip-failure") + testCluster.Spec = infrav1.OpenStackClusterSpec{ + IdentityRef: infrav1.OpenStackIdentityReference{ + Name: "test-creds", + CloudName: "openstack", + }, + ExternalNetwork: &infrav1.NetworkParam{ + ID: ptr.To(externalNetworkID), + }, + Network: &infrav1.NetworkParam{ + ID: ptr.To(clusterNetworkID), + }, + // When DisableAPIServerFloatingIP is not set and external network is configured, + // a floating IP should be created for the API server + } + err := k8sClient.Create(ctx, testCluster) + Expect(err).To(BeNil()) + err = k8sClient.Create(ctx, capiCluster) + Expect(err).To(BeNil()) + + log := GinkgoLogr + clientScope, err := mockScopeFactory.NewClientScopeFromObject(ctx, k8sClient, nil, log, testCluster) + Expect(err).To(BeNil()) + scope := scope.NewWithLogger(clientScope, log) + + networkClientRecorder := mockScopeFactory.NetworkClient.EXPECT() + + // Fetch external network + networkClientRecorder.GetNetwork(externalNetworkID).Return(&networks.Network{ + ID: externalNetworkID, + Name: "external-network", + }, nil) + + // Fetch cluster network + networkClientRecorder.GetNetwork(clusterNetworkID).Return(&networks.Network{ + ID: clusterNetworkID, + Name: "cluster-network", + }, nil) + + // Fetching cluster subnets + networkClientRecorder.ListSubnet(subnets.ListOpts{ + NetworkID: clusterNetworkID, + }).Return([]subnets.Subnet{ + { + ID: clusterSubnetID, + Name: "cluster-subnet", + CIDR: "192.168.0.0/24", + }, + }, nil) + + // Mock floating IP creation failure + networkClientRecorder.CreateFloatingIP(gomock.Any()).Return(nil, fmt.Errorf("quota exceeded")) + + err = reconcileNetworkComponents(scope, capiCluster, testCluster) + Expect(err).ToNot(BeNil()) + + // Verify APIEndpointReadyCondition is set to False + Expect(v1beta1conditions.IsFalse(testCluster, infrav1.APIEndpointReadyCondition)).To(BeTrue()) + condition := v1beta1conditions.Get(testCluster, infrav1.APIEndpointReadyCondition) + Expect(condition).ToNot(BeNil()) + Expect(condition.Reason).To(Equal(infrav1.APIEndpointConfigFailedReason)) + Expect(condition.Severity).To(Equal(clusterv1beta1.ConditionSeverityError)) + Expect(condition.Message).To(ContainSubstring("Failed to reconcile control plane endpoint")) + + // NetworkReadyCondition and SecurityGroupsReadyCondition should still be True + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.NetworkReadyCondition)).To(BeTrue()) + Expect(v1beta1conditions.IsTrue(testCluster, infrav1.SecurityGroupsReadyCondition)).To(BeTrue()) }) }) diff --git a/controllers/openstackmachine_controller.go b/controllers/openstackmachine_controller.go index 725b24b11..325f06d56 100644 --- a/controllers/openstackmachine_controller.go +++ b/controllers/openstackmachine_controller.go @@ -382,6 +382,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope if instanceStatus == nil { v1beta1conditions.MarkFalse(openStackMachine, infrav1.InstanceReadyCondition, infrav1.InstanceDeletedReason, clusterv1beta1.ConditionSeverityError, infrav1.ServerUnexpectedDeletedMessage) + v1beta1conditions.MarkFalse(openStackMachine, clusterv1beta1.ReadyCondition, infrav1.InstanceDeletedReason, clusterv1beta1.ConditionSeverityError, infrav1.ServerUnexpectedDeletedMessage) openStackMachine.SetFailure(capoerrors.DeprecatedCAPIUpdateMachineError, errors.New(infrav1.ServerUnexpectedDeletedMessage)) //nolint:staticcheck // This error is not used as an error return ctrl.Result{}, nil } @@ -437,6 +438,22 @@ func (r *OpenStackMachineReconciler) reconcileMachineState(scope *scope.WithLogg openStackMachine.Spec.ProviderID = ptr.To(fmt.Sprintf("openstack://%s/%s", region, *openStackServer.Status.InstanceID)) openStackMachine.Status.InstanceID = openStackServer.Status.InstanceID openStackMachine.Status.Ready = true + + // Set initialization.provisioned to true when initial infrastructure provisioning is complete. + // This field should only be set once and never changed afterward, as per CAPI v1beta2 contract. + // We set it here when the machine becomes ACTIVE for the first time. + if openStackMachine.Status.Initialization == nil { + openStackMachine.Status.Initialization = &infrav1.MachineInitialization{} + } + if !openStackMachine.Status.Initialization.Provisioned { + openStackMachine.Status.Initialization.Provisioned = true + scope.Logger().Info("Initial machine infrastructure provisioning completed") + } + + // Set the Ready condition to True when infrastructure is ready. + // This condition surfaces into Machine's status.conditions[InfrastructureReady]. + // It reflects the current operational state of the machine infrastructure. + v1beta1conditions.MarkTrue(openStackMachine, clusterv1beta1.ReadyCondition) case infrav1.InstanceStateError: // If the machine has a NodeRef then it must have been working at some point, // so the error could be something temporary. @@ -447,20 +464,24 @@ func (r *OpenStackMachineReconciler) reconcileMachineState(scope *scope.WithLogg openStackMachine.SetFailure(capoerrors.DeprecatedCAPIUpdateMachineError, err) } v1beta1conditions.MarkFalse(openStackMachine, infrav1.InstanceReadyCondition, infrav1.InstanceStateErrorReason, clusterv1beta1.ConditionSeverityError, "") + v1beta1conditions.MarkFalse(openStackMachine, clusterv1beta1.ReadyCondition, infrav1.InstanceStateErrorReason, clusterv1beta1.ConditionSeverityError, "Instance is in ERROR state") return &ctrl.Result{} case infrav1.InstanceStateDeleted: // we should avoid further actions for DELETED VM scope.Logger().Info("Machine instance state is DELETED, no actions") v1beta1conditions.MarkFalse(openStackMachine, infrav1.InstanceReadyCondition, infrav1.InstanceDeletedReason, clusterv1beta1.ConditionSeverityError, "") + v1beta1conditions.MarkFalse(openStackMachine, clusterv1beta1.ReadyCondition, infrav1.InstanceDeletedReason, clusterv1beta1.ConditionSeverityError, "Instance has been deleted") return &ctrl.Result{} case infrav1.InstanceStateBuild, infrav1.InstanceStateUndefined: scope.Logger().Info("Waiting for instance to become ACTIVE", "id", openStackServer.Status.InstanceID, "status", openStackServer.Status.InstanceState) + v1beta1conditions.MarkFalse(openStackMachine, clusterv1beta1.ReadyCondition, infrav1.InstanceNotReadyReason, clusterv1beta1.ConditionSeverityInfo, "Instance is building") return &ctrl.Result{RequeueAfter: waitForBuildingInstanceToReconcile} default: // The other state is normal (for example, migrating, shutoff) but we don't want to proceed until it's ACTIVE // due to potential conflict or unexpected actions scope.Logger().Info("Waiting for instance to become ACTIVE", "id", openStackServer.Status.InstanceID, "status", openStackServer.Status.InstanceState) v1beta1conditions.MarkUnknown(openStackMachine, infrav1.InstanceReadyCondition, infrav1.InstanceNotReadyReason, "Instance state is not handled: %v", ptr.Deref(openStackServer.Status.InstanceState, infrav1.InstanceStateUndefined)) + v1beta1conditions.MarkUnknown(openStackMachine, clusterv1beta1.ReadyCondition, infrav1.InstanceNotReadyReason, "Instance state is: %v", ptr.Deref(openStackServer.Status.InstanceState, infrav1.InstanceStateUndefined)) return &ctrl.Result{RequeueAfter: waitForInstanceBecomeActiveToReconcile} } diff --git a/controllers/openstackmachine_controller_test.go b/controllers/openstackmachine_controller_test.go index 78bf6f693..b9497c8ef 100644 --- a/controllers/openstackmachine_controller_test.go +++ b/controllers/openstackmachine_controller_test.go @@ -20,11 +20,17 @@ import ( "reflect" "testing" + "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + v1beta1conditions "sigs.k8s.io/cluster-api/util/deprecated/v1beta1/conditions" infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" ) const ( @@ -42,6 +48,7 @@ const ( flavorName = "test-flavor" sshKeyName = "test-ssh-key" failureDomain = "test-failure-domain" + testInstanceID = "test-instance-id-12345" ) func TestOpenStackMachineSpecToOpenStackServerSpec(t *testing.T) { @@ -428,3 +435,219 @@ func TestGetPortIDs(t *testing.T) { }) } } + +func TestReconcileMachineState(t *testing.T) { + tests := []struct { + name string + instanceState infrav1.InstanceState + machineHasNodeRef bool + expectRequeue bool + expectedInstanceReadyCondition *clusterv1beta1.Condition + expectedReadyCondition *clusterv1beta1.Condition + expectInitializationProvisioned bool + expectFailureSet bool + }{ + { + name: "Instance state ACTIVE sets conditions to True and initialization.provisioned", + instanceState: infrav1.InstanceStateActive, + expectRequeue: false, + expectedInstanceReadyCondition: &clusterv1beta1.Condition{ + Type: infrav1.InstanceReadyCondition, + Status: corev1.ConditionTrue, + }, + expectedReadyCondition: &clusterv1beta1.Condition{ + Type: clusterv1beta1.ReadyCondition, + Status: corev1.ConditionTrue, + }, + expectInitializationProvisioned: true, + }, + { + name: "Instance state ERROR sets conditions to False without NodeRef", + instanceState: infrav1.InstanceStateError, + machineHasNodeRef: false, + expectRequeue: true, + expectedInstanceReadyCondition: &clusterv1beta1.Condition{ + Type: infrav1.InstanceReadyCondition, + Status: corev1.ConditionFalse, + Severity: clusterv1beta1.ConditionSeverityError, + Reason: infrav1.InstanceStateErrorReason, + }, + expectedReadyCondition: &clusterv1beta1.Condition{ + Type: clusterv1beta1.ReadyCondition, + Status: corev1.ConditionFalse, + Severity: clusterv1beta1.ConditionSeverityError, + Reason: infrav1.InstanceStateErrorReason, + }, + expectFailureSet: true, + }, + { + name: "Instance state ERROR with NodeRef does not set failure", + instanceState: infrav1.InstanceStateError, + machineHasNodeRef: true, + expectRequeue: true, + expectedInstanceReadyCondition: &clusterv1beta1.Condition{ + Type: infrav1.InstanceReadyCondition, + Status: corev1.ConditionFalse, + Severity: clusterv1beta1.ConditionSeverityError, + Reason: infrav1.InstanceStateErrorReason, + }, + expectedReadyCondition: &clusterv1beta1.Condition{ + Type: clusterv1beta1.ReadyCondition, + Status: corev1.ConditionFalse, + Severity: clusterv1beta1.ConditionSeverityError, + Reason: infrav1.InstanceStateErrorReason, + }, + expectFailureSet: false, + }, + { + name: "Instance state DELETED sets conditions to False", + instanceState: infrav1.InstanceStateDeleted, + expectRequeue: true, + expectedInstanceReadyCondition: &clusterv1beta1.Condition{ + Type: infrav1.InstanceReadyCondition, + Status: corev1.ConditionFalse, + Severity: clusterv1beta1.ConditionSeverityError, + Reason: infrav1.InstanceDeletedReason, + }, + expectedReadyCondition: &clusterv1beta1.Condition{ + Type: clusterv1beta1.ReadyCondition, + Status: corev1.ConditionFalse, + Severity: clusterv1beta1.ConditionSeverityError, + Reason: infrav1.InstanceDeletedReason, + }, + }, + { + name: "Instance state BUILD sets ReadyCondition to False", + instanceState: infrav1.InstanceStateBuild, + expectRequeue: true, + expectedReadyCondition: &clusterv1beta1.Condition{ + Type: clusterv1beta1.ReadyCondition, + Status: corev1.ConditionFalse, + Severity: clusterv1beta1.ConditionSeverityInfo, + Reason: infrav1.InstanceNotReadyReason, + }, + }, + { + name: "Instance state SHUTOFF sets conditions to Unknown", + instanceState: infrav1.InstanceStateShutoff, + expectRequeue: true, + expectedInstanceReadyCondition: &clusterv1beta1.Condition{ + Type: infrav1.InstanceReadyCondition, + Status: corev1.ConditionUnknown, + Reason: infrav1.InstanceNotReadyReason, + }, + expectedReadyCondition: &clusterv1beta1.Condition{ + Type: clusterv1beta1.ReadyCondition, + Status: corev1.ConditionUnknown, + Reason: infrav1.InstanceNotReadyReason, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + openStackMachine := &infrav1.OpenStackMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: openStackMachineName, + Namespace: namespace, + }, + Spec: infrav1.OpenStackMachineSpec{ + Flavor: ptr.To(flavorName), + Image: infrav1.ImageParam{ + Filter: &infrav1.ImageFilter{ + Name: ptr.To("test-image"), + }, + }, + }, + } + + machine := &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-machine", + Namespace: namespace, + }, + } + if tt.machineHasNodeRef { + machine.Status.NodeRef = clusterv1.MachineNodeReference{ + Name: "test-node", + } + } + + openStackServer := &infrav1alpha1.OpenStackServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: openStackMachineName, + Namespace: namespace, + }, + Status: infrav1alpha1.OpenStackServerStatus{ + InstanceID: ptr.To(testInstanceID), + InstanceState: ptr.To(tt.instanceState), + }, + } + + r := &OpenStackMachineReconciler{} + result := r.reconcileMachineState(scope.NewWithLogger(nil, logr.Discard()), openStackMachine, machine, openStackServer) + + // Check requeue + if tt.expectRequeue && result == nil { + t.Errorf("expected requeue result, got nil") + } + if !tt.expectRequeue && result != nil { + t.Errorf("expected no requeue, got %v", result) + } + + // Check InstanceReadyCondition + if tt.expectedInstanceReadyCondition != nil { + condition := v1beta1conditions.Get(openStackMachine, tt.expectedInstanceReadyCondition.Type) + if condition == nil { + t.Errorf("expected %s condition to be set", tt.expectedInstanceReadyCondition.Type) + } else { + if condition.Status != tt.expectedInstanceReadyCondition.Status { + t.Errorf("expected %s status %s, got %s", tt.expectedInstanceReadyCondition.Type, tt.expectedInstanceReadyCondition.Status, condition.Status) + } + if tt.expectedInstanceReadyCondition.Reason != "" && condition.Reason != tt.expectedInstanceReadyCondition.Reason { + t.Errorf("expected %s reason %s, got %s", tt.expectedInstanceReadyCondition.Type, tt.expectedInstanceReadyCondition.Reason, condition.Reason) + } + if tt.expectedInstanceReadyCondition.Severity != "" && condition.Severity != tt.expectedInstanceReadyCondition.Severity { + t.Errorf("expected %s severity %s, got %s", tt.expectedInstanceReadyCondition.Type, tt.expectedInstanceReadyCondition.Severity, condition.Severity) + } + } + } + + // Check ReadyCondition + if tt.expectedReadyCondition != nil { + condition := v1beta1conditions.Get(openStackMachine, tt.expectedReadyCondition.Type) + if condition == nil { + t.Errorf("expected %s condition to be set", tt.expectedReadyCondition.Type) + } else { + if condition.Status != tt.expectedReadyCondition.Status { + t.Errorf("expected %s status %s, got %s", tt.expectedReadyCondition.Type, tt.expectedReadyCondition.Status, condition.Status) + } + if tt.expectedReadyCondition.Reason != "" && condition.Reason != tt.expectedReadyCondition.Reason { + t.Errorf("expected %s reason %s, got %s", tt.expectedReadyCondition.Type, tt.expectedReadyCondition.Reason, condition.Reason) + } + if tt.expectedReadyCondition.Severity != "" && condition.Severity != tt.expectedReadyCondition.Severity { + t.Errorf("expected %s severity %s, got %s", tt.expectedReadyCondition.Type, tt.expectedReadyCondition.Severity, condition.Severity) + } + } + } + + // Check initialization.provisioned + if tt.expectInitializationProvisioned { + if openStackMachine.Status.Initialization == nil || !openStackMachine.Status.Initialization.Provisioned { + t.Errorf("expected Initialization.Provisioned to be true") + } + } + + // Check failure is set + if tt.expectFailureSet { + if openStackMachine.Status.FailureReason == nil || openStackMachine.Status.FailureMessage == nil { + t.Errorf("expected FailureReason and FailureMessage to be set") + } + } else { + if openStackMachine.Status.FailureReason != nil || openStackMachine.Status.FailureMessage != nil { + t.Errorf("expected FailureReason and FailureMessage to not be set") + } + } + }) + } +} diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md index 70b609090..fd6937198 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -3,6 +3,8 @@ - [Introduction](./introduction.md) - [Getting Started](getting-started.md) - [Configuration](clusteropenstack/configuration.md) +- [Experimental Features](./experimental-features/experimental-features.md) + - [PriorityQueue](./experimental-features/priority-queue.md) - [Topics](./topics/index.md) - [external cloud provider](./topics/external-cloud-provider.md) - [hosted control plane](./topics/hosted-control-plane.md) diff --git a/docs/book/src/api/v1beta1/api.md b/docs/book/src/api/v1beta1/api.md index 0eb377d11..7984f01fa 100644 --- a/docs/book/src/api/v1beta1/api.md +++ b/docs/book/src/api/v1beta1/api.md @@ -1565,6 +1565,38 @@ availability zone.

+

ClusterInitialization +

+

+(Appears on: +OpenStackClusterStatus) +

+

+

ClusterInitialization represents the initialization status of the cluster.

+

+ + + + + + + + + + + + + +
FieldDescription
+provisioned
+ +bool + +
+(Optional) +

Provisioned is set to true when the initial provisioning of the cluster infrastructure is completed. +The value of this field is never updated after provisioning is completed.

+

ExternalRouterIPParam

@@ -1956,6 +1988,38 @@ subnet in the list is taken into account.

+

MachineInitialization +

+

+(Appears on: +OpenStackMachineStatus) +

+

+

MachineInitialization contains information about the initialization status of the machine.

+

+ + + + + + + + + + + + + +
FieldDescription
+provisioned
+ +bool + +
+(Optional) +

Provisioned is set to true when the initial provisioning of the machine infrastructure is completed. +The value of this field is never updated after provisioning is completed.

+

MachineResources

@@ -2672,6 +2736,22 @@ bool

Ready is true when the cluster infrastructure is ready.

+

Deprecated: This field is deprecated and will be removed in a future API version. +Use status.conditions to determine the ready state of the cluster.

+ + + + +initialization
+ + +ClusterInitialization + + + + +(Optional) +

Initialization contains information about the initialization status of the cluster.

@@ -2824,6 +2904,8 @@ responsible controller itself being critically misconfigured.

Any transient errors that occur during the reconciliation of OpenStackClusters can be added as events to the OpenStackCluster object and/or logged in the controller’s output.

+

Deprecated: This field is deprecated and will be removed in a future API version. +Use status.conditions to report failures.

@@ -2849,6 +2931,23 @@ responsible controller itself being critically misconfigured.

Any transient errors that occur during the reconciliation of OpenStackClusters can be added as events to the OpenStackCluster object and/or logged in the controller’s output.

+

Deprecated: This field is deprecated and will be removed in a future API version. +Use status.conditions to report failures.

+ + + + +conditions
+ +sigs.k8s.io/cluster-api/api/core/v1beta1.Conditions + + + +(Optional) +

Conditions defines current service state of the OpenStackCluster. +This field surfaces into Cluster’s status.conditions[InfrastructureReady] condition. +The Ready condition must surface issues during the entire lifecycle of the OpenStackCluster +(both during initial provisioning and after the initial provisioning is completed).

@@ -3597,6 +3696,22 @@ bool (Optional)

Ready is true when the provider resource is ready.

+

Deprecated: This field is deprecated and will be removed in a future API version. +Use status.conditions to determine the ready state of the machine.

+ + + + +initialization
+ + +MachineInitialization + + + + +(Optional) +

Initialization contains information about the initialization status of the machine.

@@ -3675,6 +3790,10 @@ sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/errors.DeprecatedCAPIMachin +(Optional) +

FailureReason explains the reson behind a failure.

+

Deprecated: This field is deprecated and will be removed in a future API version. +Use status.conditions to report failures.

@@ -3700,6 +3819,8 @@ responsible controller itself being critically misconfigured.

Any transient errors that occur during the reconciliation of Machines can be added as events to the Machine object and/or logged in the controller’s output.

+

Deprecated: This field is deprecated and will be removed in a future API version. +Use status.conditions to report failures.

@@ -3710,6 +3831,11 @@ sigs.k8s.io/cluster-api/api/core/v1beta1.Conditions +(Optional) +

Conditions defines current service state of the OpenStackMachine. +This field surfaces into Machine’s status.conditions[InfrastructureReady] condition. +The Ready condition must surface issues during the entire lifecycle of the OpenStackMachine +(both during initial provisioning and after the initial provisioning is completed).

diff --git a/docs/book/src/experimental-features/experimental-features.md b/docs/book/src/experimental-features/experimental-features.md new file mode 100644 index 000000000..384e140a7 --- /dev/null +++ b/docs/book/src/experimental-features/experimental-features.md @@ -0,0 +1,68 @@ +# Experimental Features + +CAPO now ships with experimental features the users can enable. + +Currently CAPO has the following experimental features: +* `PriorityQueue` (env var: `EXP_CAPO_PRIORITY_QUEUE`): [PriorityQueue](./priority-queue.md) + +## Enabling Experimental Features for Management Clusters Started with clusterctl + +Users can enable/disable features by setting OS environment variables before running `clusterctl init`, e.g.: + +```yaml +export EXP_SOME_FEATURE_NAME=true + +clusterctl init --infrastructure openstack +``` + +As an alternative to environment variables, it is also possible to set variables in the clusterctl config file located at `$XDG_CONFIG_HOME/cluster-api/clusterctl.yaml`, e.g.: +```yaml +# Values for environment variable substitution +EXP_SOME_FEATURE_NAME: "true" +``` +In case a variable is defined in both the config file and as an OS environment variable, the environment variable takes precedence. +For more information on how to set variables for clusterctl, see [clusterctl Configuration File](https://cluster-api.sigs.k8s.io/clusterctl/configuration) + + +## Enabling Experimental Features on Existing Management Clusters + +To enable/disable features on existing management clusters, users can edit the controller manager +deployments, which will then trigger a restart with the requested features. E.g: + +``` +kubectl edit -n capo-system deployment.apps/capo-controller-manager +``` +``` +// Enable/disable available features by modifying Args below. +spec: + template: + spec: + containers: + - args: + - --leader-elect + - --feature-gates=SomeFeature=true,OtherFeature=false +``` + +Similarly, to **validate** if a particular feature is enabled, see the arguments by issuing: + +```bash +kubectl describe -n capo-system deployment.apps/capo-controller-manager +``` + +## Enabling Experimental Features for e2e Tests + +Features can be enabled by setting them as environmental variables before running e2e tests. + +For `ci` this can also be done through updating `./test/e2e/data/e2e_conf.yaml`. + +## Enabling Experimental Features on Tilt + +On development environments started with `Tilt`, features can be enabled by setting the feature variables in `kustomize_substitutions`, e.g.: + +```yaml +kustomize_substitutions: + EXP_CAPO_PRIORITY_QUEUE: 'true' +``` + +For more details on setting up a development environment with `tilt`, see [Developing with Tilt](../development/development.md#developing-with-tilt) + diff --git a/docs/book/src/experimental-features/priority-queue.md b/docs/book/src/experimental-features/priority-queue.md new file mode 100644 index 000000000..1bd4945ea --- /dev/null +++ b/docs/book/src/experimental-features/priority-queue.md @@ -0,0 +1,20 @@ +# Priority Queue + +> **Note:** PriorityQueue is available in >= 0.14 + +The `PriorityQueue` feature flag enables the usage of the controller-runtime PriorityQueue. + +This feature deprioritizes reconciliation of objects that were not edge-triggered (i.e. due to an create/update etc.) and makes the controller more responsive during full resyncs and controller startups. + +More information on controller-runtime PriorityQueue: +- [release-notes](https://github.com/kubernetes-sigs/controller-runtime/releases/tag/v0.20.0) +- [design docs](https://github.com/kubernetes-sigs/controller-runtime/pull/3013) +- [tracking issue](https://github.com/kubernetes-sigs/controller-runtime/issues/2374) + +## Enabling Priority Queue + +You can enable `PriorityQueue` using the following. + +- Environment variable: `EXP_CAPO_PRIORITY_QUEUE=true` +- clusterctl.yaml variable: `EXP_CAPO_PRIORITY_QUEUE: true` +- --feature-gates argument: `PriorityQueue=true` diff --git a/feature/feature.go b/feature/feature.go new file mode 100644 index 000000000..8da931c1d --- /dev/null +++ b/feature/feature.go @@ -0,0 +1,48 @@ +/* +Copyright 2022 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 feature handles feature gates. +package feature + +import ( + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/component-base/featuregate" +) + +const ( + // Every capo-specific feature gate should add method here following this template: + // + // // owner: @username + // // alpha: v1.X + // MyFeature featuregate.Feature = "MyFeature". + + // PriorityQueue is a feature gate that controls if the controller uses the controller-runtime PriorityQueue + // instead of the default queue implementation. + // + // alpha: v0.14 + PriorityQueue featuregate.Feature = "PriorityQueue" +) + +func init() { + runtime.Must(MutableGates.Add(defaultCAPOFeatureGates)) +} + +// defaultCAPOFeatureGates consists of all known capo-specific feature keys. +// To add a new feature, define a key for it above and add it here. +var defaultCAPOFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ + // Every feature should be initiated here: + PriorityQueue: {Default: false, PreRelease: featuregate.Alpha}, +} diff --git a/feature/gates.go b/feature/gates.go new file mode 100644 index 000000000..0b3c71947 --- /dev/null +++ b/feature/gates.go @@ -0,0 +1,33 @@ +/* +Copyright 2022 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 feature + +import ( + "k8s.io/component-base/featuregate" +) + +var ( + // MutableGates is a mutable version of DefaultFeatureGate. + // Only top-level commands/options setup and the k8s.io/component-base/featuregate/testing package should make use of this. + // Tests that need to modify featuregate gates for the duration of their test should use: + // defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features., )() + MutableGates = featuregate.NewFeatureGate() + + // Gates is a shared global FeatureGate. + // Top-level commands/options setup that needs to modify this featuregate gate should use DefaultMutableFeatureGate. + Gates featuregate.FeatureGate = MutableGates +) diff --git a/go.mod b/go.mod index 61719d515..494c0f454 100644 --- a/go.mod +++ b/go.mod @@ -8,40 +8,43 @@ require ( github.com/google/go-cmp v0.7.0 github.com/google/gofuzz v1.2.0 github.com/google/uuid v1.6.0 - github.com/gophercloud/gophercloud/v2 v2.8.0 + github.com/gophercloud/gophercloud/v2 v2.9.0 github.com/gophercloud/utils/v2 v2.0.0-20241220104409-2e0af06694a1 - github.com/hashicorp/go-version v1.7.0 - github.com/k-orc/openstack-resource-controller/v2 v2.2.0 - github.com/onsi/ginkgo/v2 v2.26.0 - github.com/onsi/gomega v1.38.2 + github.com/hashicorp/go-version v1.8.0 + github.com/k-orc/openstack-resource-controller/v2 v2.3.0 + github.com/onsi/ginkgo/v2 v2.27.3 + github.com/onsi/gomega v1.38.3 github.com/prometheus/client_golang v1.23.2 github.com/spf13/pflag v1.0.10 go.uber.org/mock v0.6.0 - golang.org/x/crypto v0.43.0 - golang.org/x/text v0.30.0 + golang.org/x/crypto v0.46.0 + golang.org/x/text v0.32.0 gopkg.in/ini.v1 v1.67.0 - k8s.io/api v0.33.5 - k8s.io/apiextensions-apiserver v0.33.5 - k8s.io/apimachinery v0.33.5 - k8s.io/client-go v0.33.5 - k8s.io/code-generator v0.33.5 - k8s.io/component-base v0.33.5 + k8s.io/api v0.34.3 + k8s.io/apiextensions-apiserver v0.34.3 + k8s.io/apimachinery v0.34.3 + k8s.io/client-go v0.34.3 + k8s.io/code-generator v0.34.3 + k8s.io/component-base v0.34.3 k8s.io/klog/v2 v2.130.1 - k8s.io/kube-openapi v0.0.0-20250610211856-8b98d1ed966a + k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 - sigs.k8s.io/cluster-api v1.11.2 - sigs.k8s.io/cluster-api/test v1.11.0 - sigs.k8s.io/controller-runtime v0.21.0 - sigs.k8s.io/structured-merge-diff/v4 v4.7.0 + sigs.k8s.io/cluster-api v1.12.0 + sigs.k8s.io/cluster-api/test v1.12.0 + sigs.k8s.io/controller-runtime v0.22.4 + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 sigs.k8s.io/yaml v1.6.0 ) require ( al.essio.dev/pkg/shellescape v1.5.1 // indirect - cel.dev/expr v0.19.1 // indirect + cel.dev/expr v0.24.0 // indirect + dario.cat/mergo v1.0.1 // indirect github.com/BurntSushi/toml v1.4.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect @@ -55,16 +58,16 @@ require ( github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v28.3.3+incompatible // indirect - github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/docker v28.5.2+incompatible // indirect + github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -76,81 +79,90 @@ require ( github.com/gofrs/uuid/v5 v5.3.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.23.2 // indirect - github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/cel-go v0.26.0 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-github/v53 v53.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect + github.com/olekukonko/errors v1.1.0 // indirect + github.com/olekukonko/ll v0.1.1 // indirect + github.com/olekukonko/tablewriter v1.0.9 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/rivo/uniseg v0.4.2 // indirect - github.com/sagikazarmark/locafero v0.7.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.12.0 // indirect - github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/viper v1.20.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/cobra v1.10.1 // indirect + github.com/spf13/viper v1.21.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/valyala/fastjson v1.6.4 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect - go.opentelemetry.io/proto/otlp v1.4.0 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.45.0 // indirect - golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/term v0.36.0 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.37.0 // indirect - golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect + golang.org/x/tools v0.39.0 // indirect + golang.org/x/tools/go/expect v0.1.0-deprecated // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect - google.golang.org/grpc v1.71.3 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/grpc v1.72.3 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.33.5 // indirect - k8s.io/cluster-bootstrap v0.33.3 // indirect - k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 // indirect + k8s.io/apiserver v0.34.3 // indirect + k8s.io/cluster-bootstrap v0.34.2 // indirect + k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/kind v0.29.0 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/kind v0.30.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index 6b72efb5b..18a29116a 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho= al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= -cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= -cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -46,8 +46,13 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.28 h1:O8YafUREqUcGbRtcJfOmWU6ifcw2HX76I1QvI5xZpsw= -github.com/coredns/corefile-migration v1.0.28/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= +github.com/coredns/corefile-migration v1.0.29 h1:g4cPYMXXDDs9uLE2gFYrJaPBuUAR07eEMGyh9JBE13w= +github.com/coredns/corefile-migration v1.0.29/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -56,16 +61,16 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= -github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU= -github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= -github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= @@ -76,16 +81,16 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= -github.com/gkampitakis/go-snaps v0.5.14 h1:3fAqdB6BCPKHDMHAKRwtPUwYexKtGrNuw8HX/T/4neo= -github.com/gkampitakis/go-snaps v0.5.14/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -117,13 +122,12 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= -github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= +github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI= @@ -133,20 +137,22 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gophercloud/gophercloud/v2 v2.8.0 h1:of2+8tT6+FbEYHfYC8GBu8TXJNsXYSNm9KuvpX7Neqo= -github.com/gophercloud/gophercloud/v2 v2.8.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk= +github.com/gophercloud/gophercloud/v2 v2.9.0 h1:Y9OMrwKF9EDERcHFSOTpf/6XGoAI0yOxmsLmQki4LPM= +github.com/gophercloud/gophercloud/v2 v2.9.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk= github.com/gophercloud/utils/v2 v2.0.0-20241220104409-2e0af06694a1 h1:LS70kbNdqoalMwLXEzP9Xb/cYv9UCzWioXaOynxrytc= github.com/gophercloud/utils/v2 v2.0.0-20241220104409-2e0af06694a1/go.mod h1:qDhuzCRKi90/Yyl/yEqkg8+qABEvK44LhP0D3GWKGtY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -157,8 +163,8 @@ github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/k-orc/openstack-resource-controller/v2 v2.2.0 h1:/1gs5b799BfYHPksxLaAY/DdWg7usn4ogFJEm01GU+Y= -github.com/k-orc/openstack-resource-controller/v2 v2.2.0/go.mod h1:lwP69Om+l0Xj8wuxVbYgOfDAJI8+8TGu4SH1RiteyCU= +github.com/k-orc/openstack-resource-controller/v2 v2.3.0 h1:jLI/GH/yzqy6MVzu54dMcimzFmpprBiWBrfHEc9eots= +github.com/k-orc/openstack-resource-controller/v2 v2.3.0/go.mod h1:3yPrdRJrWHP0qV0IVvLnEWbEQmK7TyWjwUZHiU5dmzA= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -176,14 +182,12 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -201,32 +205,37 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.26.0 h1:1J4Wut1IlYZNEAWIV3ALrT9NfiaGW2cDCJQSFQMs/gE= -github.com/onsi/ginkgo/v2 v2.26.0/go.mod h1:qhEywmzWTBUY88kfO0BRvX4py7scov9yR+Az2oavUzw= -github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= -github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.1 h1:9Dfeed5/Mgaxb9lHRAftLK9pVfYETvHn+If6lywVhJc= +github.com/olekukonko/ll v0.1.1/go.mod h1:2dJo+hYZcJMLMbKwHEWvxCUbAOLc/CXWS9noET22Mdo= +github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8= +github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= +github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8= +github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM= +github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -236,31 +245,31 @@ github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= -github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= -github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -285,42 +294,50 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/etcd/api/v3 v3.6.6 h1:mcaMp3+7JawWv69p6QShYWS8cIWUOl32bFLb6qf8pOQ= +go.etcd.io/etcd/api/v3 v3.6.6/go.mod h1:f/om26iXl2wSkcTA1zGQv8reJRSLVdoEBsi4JdfMrx4= +go.etcd.io/etcd/client/pkg/v3 v3.6.6 h1:uoqgzSOv2H9KlIF5O1Lsd8sW+eMLuV6wzE3q5GJGQNs= +go.etcd.io/etcd/client/pkg/v3 v3.6.6/go.mod h1:YngfUVmvsvOJ2rRgStIyHsKtOt9SZI2aBJrZiWJhCbI= +go.etcd.io/etcd/client/v3 v3.6.6 h1:G5z1wMf5B9SNexoxOHUGBaULurOZPIgGPsW6CN492ec= +go.etcd.io/etcd/client/v3 v3.6.6/go.mod h1:36Qv6baQ07znPR3+n7t+Rk5VHEzVYPvFfGmfF4wBHV8= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= -go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -329,28 +346,28 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -359,25 +376,24 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -388,12 +404,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= -google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= -google.golang.org/grpc v1.71.3 h1:iEhneYTxOruJyZAxdAv8Y0iRZvsc5M6KoW7UA0/7jn0= -google.golang.org/grpc v1.71.3/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.72.3 h1:6sysal2a4j9trATt+J/TSSEA/Q45ZrXzNh5zy4NMWuA= +google.golang.org/grpc v1.72.3/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -410,47 +426,45 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -k8s.io/api v0.33.5 h1:YR+uhYj05jdRpcksv8kjSliW+v9hwXxn6Cv10aR8Juw= -k8s.io/api v0.33.5/go.mod h1:2gzShdwXKT5yPGiqrTrn/U/nLZ7ZyT4WuAj3XGDVgVs= -k8s.io/apiextensions-apiserver v0.33.5 h1:93NZh6rmrcamX/tfv/dZrTsMiQX69ufANmDcKPEgSeA= -k8s.io/apiextensions-apiserver v0.33.5/go.mod h1:JIbyQnNlu6nQa7b1vgFi51pmlXOk8mdn0WJwUJnz/7U= -k8s.io/apimachinery v0.33.5 h1:NiT64hln4TQXeYR18/ES39OrNsjGz8NguxsBgp+6QIo= -k8s.io/apimachinery v0.33.5/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/apiserver v0.33.5 h1:X1Gy33r4YkRLRqTjGjofk7X1/EjSLEVSJ/A+1qjoj60= -k8s.io/apiserver v0.33.5/go.mod h1:Q+b5Btbc8x0PqOCeh/xBTesKk+cXQRN+PF2wdrTKDeg= -k8s.io/client-go v0.33.5 h1:I8BdmQGxInpkMEnJvV6iG7dqzP3JRlpZZlib3OMFc3o= -k8s.io/client-go v0.33.5/go.mod h1:W8PQP4MxbM4ypgagVE65mUUqK1/ByQkSALF9tzuQ6u0= -k8s.io/cluster-bootstrap v0.33.3 h1:u2NTxJ5CFSBFXaDxLQoOWMly8eni31psVso+caq6uwI= -k8s.io/cluster-bootstrap v0.33.3/go.mod h1:p970f8u8jf273zyQ5raD8WUu2XyAl0SAWOY82o7i/ds= -k8s.io/code-generator v0.33.5 h1:KwkOvhwAaorjSwF2MQhhdhL3i8bBmAal/TWhX67kdHw= -k8s.io/code-generator v0.33.5/go.mod h1:Ra+sdZquRakeTGcEnQAPw6BmlZ92IvxwQQTX/XOvOIE= -k8s.io/component-base v0.33.5 h1:4D3kxjEx1pJRy3WHAZsmX3+LCpmd4ftE+2J4v6naTnQ= -k8s.io/component-base v0.33.5/go.mod h1:Zma1YjBVuuGxIbspj1vGR3/5blzo2ARf1v0QTtog1to= -k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 h1:2OX19X59HxDprNCVrWi6jb7LW1PoqTlYqEq5H2oetog= -k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.3 h1:uGH1qpDvSiYG4HVFqc6A3L4CKiX+aBWDrrsxHYK0Bdo= +k8s.io/apiserver v0.34.3/go.mod h1:QPnnahMO5C2m3lm6fPW3+JmyQbvHZQ8uudAu/493P2w= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= +k8s.io/cluster-bootstrap v0.34.2 h1:oKckPeunVCns37BntcsxaOesDul32yzGd3DFLjW2fc8= +k8s.io/cluster-bootstrap v0.34.2/go.mod h1:f21byPR7X5nt12ivZi+J3pb4sG4SH6VySX8KAAJA8BY= +k8s.io/code-generator v0.34.3 h1:6ipJKsJZZ9q21BO8I2jEj4OLN3y8/1n4aihKN0xKmQk= +k8s.io/code-generator v0.34.3/go.mod h1:oW73UPYpGLsbRN8Ozkhd6ZzkF8hzFCiYmvEuWZDroI4= +k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= +k8s.io/component-base v0.34.3/go.mod h1:5iIlD8wPfWE/xSHTRfbjuvUul2WZbI2nOUK65XL0E/c= +k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q= +k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250610211856-8b98d1ed966a h1:ZV3Zr+/7s7aVbjNGICQt+ppKWsF1tehxggNfbM7XnG8= -k8s.io/kube-openapi v0.0.0-20250610211856-8b98d1ed966a/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/cluster-api v1.11.2 h1:uAczaBavU5Y6aDgyoXWtq28k1kalpSZnVItwXHusw1c= -sigs.k8s.io/cluster-api v1.11.2/go.mod h1:C1gJVAjMXRG+M+djjGYNkoi5kBMhFnOUI9QqZDAtMms= -sigs.k8s.io/cluster-api/test v1.11.0 h1:dvwMAb5rm4Z7Kj3l9FkeYTWfSthpN0oX3gvUrd8ej24= -sigs.k8s.io/cluster-api/test v1.11.0/go.mod h1:2f489Lp5TKPGVhNL6V3huq8fp6eb23APlY2cLbhuDBU= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/kind v0.29.0 h1:3TpCsyh908IkXXpcSnsMjWdwdWjIl7o9IMZImZCWFnI= -sigs.k8s.io/kind v0.29.0/go.mod h1:ldWQisw2NYyM6k64o/tkZng/1qQW7OlzcN5a8geJX3o= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/cluster-api v1.12.0 h1:iFOz8b0LdrMJS5Df1Eb7wyvTkWqlTUM2LHFEHCeI6vA= +sigs.k8s.io/cluster-api v1.12.0/go.mod h1:+S6WJdi8UPdqv5q9nka5al3ed/Qa0zAcSBgzTaa9VKA= +sigs.k8s.io/cluster-api/test v1.12.0 h1:OpxFwNSu9j9jY2v4dl90rSi5W9is8/vBXxGCTci5gyE= +sigs.k8s.io/cluster-api/test v1.12.0/go.mod h1:eS05aBCEZeKzcW7MGnT9U+zap4NCpkxHBWb10DshWp0= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/kind v0.30.0 h1:2Xi1KFEfSMm0XDcvKnUt15ZfgRPCT0OnCBbpgh8DztY= +sigs.k8s.io/kind v0.30.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/hack/boilerplate/boilerplate.py b/hack/boilerplate/boilerplate.py index b1d8cac6c..fb9f45e4b 100755 --- a/hack/boilerplate/boilerplate.py +++ b/hack/boilerplate/boilerplate.py @@ -160,7 +160,7 @@ def file_extension(filename): '.git', '.tiltbuild', 'vendor', - 'orc', + 'openshift', ] # Paths to be ignored for boilerplate detection diff --git a/hack/ci/cloud-init/controller.yaml.tpl b/hack/ci/cloud-init/controller.yaml.tpl index ace405467..e89a44857 100644 --- a/hack/ci/cloud-init/controller.yaml.tpl +++ b/hack/ci/cloud-init/controller.yaml.tpl @@ -10,7 +10,7 @@ # Enable Logging LOGFILE=/opt/stack/logs/stack.sh.log VERBOSE=True - LOG_COLOR=True + LOG_COLOR=False # Host tuning ENABLE_SYSCTL_MEM_TUNING="True" @@ -44,7 +44,7 @@ PUBLIC_BRIDGE_MTU=${MTU} ENABLE_CHASSIS_AS_GW="True" OVN_DBS_LOG_LEVEL="dbg" - Q_ML2_PLUGIN_MECHANISM_DRIVERS="ovn,logger" + Q_ML2_PLUGIN_MECHANISM_DRIVERS="ovn" OVN_L3_CREATE_PUBLIC_NETWORK="True" Q_AGENT="ovn" diff --git a/hack/ci/cloud-init/worker.yaml.tpl b/hack/ci/cloud-init/worker.yaml.tpl index 0a34b69a2..63fd48cb8 100644 --- a/hack/ci/cloud-init/worker.yaml.tpl +++ b/hack/ci/cloud-init/worker.yaml.tpl @@ -9,7 +9,7 @@ # Enable Logging LOGFILE=/opt/stack/logs/stack.sh.log VERBOSE=True - LOG_COLOR=True + LOG_COLOR=False # Host tuning ENABLE_SYSCTL_MEM_TUNING="True" @@ -41,7 +41,7 @@ PUBLIC_BRIDGE_MTU=${MTU} ENABLE_CHASSIS_AS_GW="False" OVN_DBS_LOG_LEVEL="dbg" - Q_ML2_PLUGIN_MECHANISM_DRIVERS="ovn,logger" + Q_ML2_PLUGIN_MECHANISM_DRIVERS="ovn" Q_AGENT="ovn" # WORKAROUND: diff --git a/hack/ci/create_devstack.sh b/hack/ci/create_devstack.sh index 35b66e0ba..8fc40cd96 100755 --- a/hack/ci/create_devstack.sh +++ b/hack/ci/create_devstack.sh @@ -31,7 +31,7 @@ source "${scriptdir}/${RESOURCE_TYPE}.sh" CLUSTER_NAME=${CLUSTER_NAME:-"capo-e2e"} -OPENSTACK_RELEASE=${OPENSTACK_RELEASE:-"2024.2"} +OPENSTACK_RELEASE=${OPENSTACK_RELEASE:-"2025.2"} OPENSTACK_ENABLE_HORIZON=${OPENSTACK_ENABLE_HORIZON:-"false"} # Devstack will create a provider network using this range diff --git a/hack/image-patch/kustomization.yaml b/hack/image-patch/kustomization.yaml index f9032f685..483e205a5 100644 --- a/hack/image-patch/kustomization.yaml +++ b/hack/image-patch/kustomization.yaml @@ -1,16 +1,16 @@ apiVersion: kustomize.config.k8s.io/v1beta1 images: - - name: "" - newName: "" - newTag: "" +- name: "" + newName: "" + newTag: "" kind: Kustomization patchesJson6902: - - path: pull-policy-patch.yaml - target: - group: apps - kind: Deployment - name: controller-name - namespace: namespace - version: v1 +- path: pull-policy-patch.yaml + target: + group: apps + kind: Deployment + name: controller-name + namespace: namespace + version: v1 resources: - - source-manifest.yaml +- source-manifest.yaml diff --git a/hack/tools/Makefile b/hack/tools/Makefile index f027449f9..54f51cc6c 100644 --- a/hack/tools/Makefile +++ b/hack/tools/Makefile @@ -15,7 +15,7 @@ ROOT_DIR_RELATIVE := ../.. include $(ROOT_DIR_RELATIVE)/common.mk -GOLANGCI_LINT_VERSION ?= v2.5.0 +GOLANGCI_LINT_VERSION ?= v2.7.2 # GOTESTSUM version without the leading 'v' GOTESTSUM_VERSION ?= 1.12.0 diff --git a/hack/tools/go.mod b/hack/tools/go.mod index 940e1a824..1f2a9436e 100644 --- a/hack/tools/go.mod +++ b/hack/tools/go.mod @@ -5,15 +5,15 @@ go 1.24.0 require ( github.com/a8m/envsubst v1.4.3 github.com/ahmetb/gen-crd-api-reference-docs v0.3.1-0.20220420215017-3f29e6853552 - github.com/itchyny/gojq v0.12.17 - github.com/onsi/ginkgo/v2 v2.26.0 + github.com/itchyny/gojq v0.12.18 + github.com/onsi/ginkgo/v2 v2.27.3 go.uber.org/mock v0.6.0 - k8s.io/code-generator v0.33.5 + k8s.io/code-generator v0.34.3 sigs.k8s.io/cluster-api-provider-openstack v0.0.0 sigs.k8s.io/cluster-api/hack/tools v0.0.0-20250805173327-a7b9f27af519 sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20250620151452-b9a9ca01fd37 sigs.k8s.io/controller-tools v0.18.0 - sigs.k8s.io/kustomize/kustomize/v5 v5.7.1 + sigs.k8s.io/kustomize/kustomize/v5 v5.8.0 ) require ( @@ -30,15 +30,17 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheggaaa/pb/v3 v3.1.5 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/cyphar/filepath-securejoin v0.2.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect @@ -56,35 +58,36 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.23.2 // indirect + github.com/google/cel-go v0.26.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-github/v58 v58.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gophercloud/gophercloud/v2 v2.8.0 // indirect + github.com/gophercloud/gophercloud/v2 v2.9.0 // indirect github.com/gophercloud/utils/v2 v2.0.0-20241220104409-2e0af06694a1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/itchyny/timefmt-go v0.1.6 // indirect + github.com/itchyny/go-yaml v0.0.0-20251001235044-fca9a0999f15 // indirect + github.com/itchyny/timefmt-go v0.1.7 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/k-orc/openstack-resource-controller/v2 v2.2.0 // indirect + github.com/k-orc/openstack-resource-controller/v2 v2.3.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect - github.com/onsi/gomega v1.38.2 // indirect + github.com/onsi/gomega v1.38.3 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -92,14 +95,13 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/saschagrunert/go-modiff v1.3.5 // indirect - github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/sergi/go-diff v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.0 // indirect - github.com/spf13/afero v1.12.0 // indirect - github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cobra v1.10.1 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -108,29 +110,27 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/otel v1.36.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.36.0 // indirect go.opentelemetry.io/otel/sdk v1.36.0 // indirect go.opentelemetry.io/otel/trace v1.36.0 // indirect - go.opentelemetry.io/proto/otlp v1.4.0 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect + go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.43.0 // indirect + golang.org/x/crypto v0.46.0 // indirect golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.45.0 // indirect - golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/term v0.36.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.37.0 // indirect - golang.org/x/tools/go/expect v0.1.0-deprecated // indirect + golang.org/x/tools v0.39.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 // indirect @@ -141,13 +141,13 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.33.5 // indirect - k8s.io/apiextensions-apiserver v0.33.5 // indirect - k8s.io/apimachinery v0.34.1 // indirect - k8s.io/apiserver v0.33.5 // indirect - k8s.io/client-go v0.33.5 // indirect - k8s.io/cluster-bootstrap v0.33.3 // indirect - k8s.io/component-base v0.33.5 // indirect + k8s.io/api v0.34.3 // indirect + k8s.io/apiextensions-apiserver v0.34.3 // indirect + k8s.io/apimachinery v0.34.3 // indirect + k8s.io/apiserver v0.34.3 // indirect + k8s.io/client-go v0.34.3 // indirect + k8s.io/cluster-bootstrap v0.34.2 // indirect + k8s.io/component-base v0.34.3 // indirect k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9 // indirect k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect k8s.io/klog v0.2.0 // indirect @@ -156,18 +156,17 @@ require ( k8s.io/release v0.16.9 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect - sigs.k8s.io/cluster-api v1.11.2 // indirect - sigs.k8s.io/controller-runtime v0.21.0 // indirect + sigs.k8s.io/cluster-api v1.12.0 // indirect + sigs.k8s.io/controller-runtime v0.22.4 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d // indirect - sigs.k8s.io/kustomize/api v0.20.1 // indirect - sigs.k8s.io/kustomize/cmd/config v0.20.1 // indirect - sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect + sigs.k8s.io/kustomize/api v0.21.0 // indirect + sigs.k8s.io/kustomize/cmd/config v0.21.0 // indirect + sigs.k8s.io/kustomize/kyaml v0.21.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/release-sdk v0.11.0 // indirect sigs.k8s.io/release-utils v0.8.1 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/hack/tools/go.sum b/hack/tools/go.sum index 8eb02d51d..1c3520f69 100644 --- a/hack/tools/go.sum +++ b/hack/tools/go.sum @@ -41,6 +41,10 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk= github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -49,8 +53,8 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.28 h1:O8YafUREqUcGbRtcJfOmWU6ifcw2HX76I1QvI5xZpsw= -github.com/coredns/corefile-migration v1.0.28/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= +github.com/coredns/corefile-migration v1.0.29 h1:g4cPYMXXDDs9uLE2gFYrJaPBuUAR07eEMGyh9JBE13w= +github.com/coredns/corefile-migration v1.0.29/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= @@ -61,18 +65,18 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= -github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU= github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= -github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= -github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= @@ -83,16 +87,16 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= -github.com/gkampitakis/go-snaps v0.5.14 h1:3fAqdB6BCPKHDMHAKRwtPUwYexKtGrNuw8HX/T/4neo= -github.com/gkampitakis/go-snaps v0.5.14/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -139,13 +143,12 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= -github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= +github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= +github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= @@ -159,26 +162,28 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gophercloud/gophercloud/v2 v2.8.0 h1:of2+8tT6+FbEYHfYC8GBu8TXJNsXYSNm9KuvpX7Neqo= -github.com/gophercloud/gophercloud/v2 v2.8.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk= +github.com/gophercloud/gophercloud/v2 v2.9.0 h1:Y9OMrwKF9EDERcHFSOTpf/6XGoAI0yOxmsLmQki4LPM= +github.com/gophercloud/gophercloud/v2 v2.9.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk= github.com/gophercloud/utils/v2 v2.0.0-20241220104409-2e0af06694a1 h1:LS70kbNdqoalMwLXEzP9Xb/cYv9UCzWioXaOynxrytc= github.com/gophercloud/utils/v2 v2.0.0-20241220104409-2e0af06694a1/go.mod h1:qDhuzCRKi90/Yyl/yEqkg8+qABEvK44LhP0D3GWKGtY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg= -github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY= -github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q= -github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg= +github.com/itchyny/go-yaml v0.0.0-20251001235044-fca9a0999f15 h1:m4jKsIK0QS9ihQzOxUN2zJcPdrACwqIWCwvdzv9skMQ= +github.com/itchyny/go-yaml v0.0.0-20251001235044-fca9a0999f15/go.mod h1:Tmbz8uw5I/I6NvVpEGuhzlElCGS5hPoXJkt7l+ul6LE= +github.com/itchyny/gojq v0.12.18 h1:gFGHyt/MLbG9n6dqnvlliiya2TaMMh6FFaR2b1H6Drc= +github.com/itchyny/gojq v0.12.18/go.mod h1:4hPoZ/3lN9fDL1D+aK7DY1f39XZpY9+1Xpjz8atrEkg= +github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA= +github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -187,8 +192,8 @@ github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/k-orc/openstack-resource-controller/v2 v2.2.0 h1:/1gs5b799BfYHPksxLaAY/DdWg7usn4ogFJEm01GU+Y= -github.com/k-orc/openstack-resource-controller/v2 v2.2.0/go.mod h1:lwP69Om+l0Xj8wuxVbYgOfDAJI8+8TGu4SH1RiteyCU= +github.com/k-orc/openstack-resource-controller/v2 v2.3.0 h1:jLI/GH/yzqy6MVzu54dMcimzFmpprBiWBrfHEc9eots= +github.com/k-orc/openstack-resource-controller/v2 v2.3.0/go.mod h1:3yPrdRJrWHP0qV0IVvLnEWbEQmK7TyWjwUZHiU5dmzA= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -210,13 +215,12 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -239,21 +243,27 @@ github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5 github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.1 h1:9Dfeed5/Mgaxb9lHRAftLK9pVfYETvHn+If6lywVhJc= +github.com/olekukonko/ll v0.1.1/go.mod h1:2dJo+hYZcJMLMbKwHEWvxCUbAOLc/CXWS9noET22Mdo= +github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8= +github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.26.0 h1:1J4Wut1IlYZNEAWIV3ALrT9NfiaGW2cDCJQSFQMs/gE= -github.com/onsi/ginkgo/v2 v2.26.0/go.mod h1:qhEywmzWTBUY88kfO0BRvX4py7scov9yR+Az2oavUzw= -github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= -github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8= +github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM= +github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -262,8 +272,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -272,20 +280,19 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= -github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/saschagrunert/go-modiff v1.3.5 h1:Wb2KUhCiuTJfhCwGYIwjZOpC++RbY0MTf7J5m1CfQlw= github.com/saschagrunert/go-modiff v1.3.5/go.mod h1:yWSOFnT8wQIzUMsVflHmkL1qYHk+WLcjzGoeAjqjRXM= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -294,20 +301,20 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -348,10 +355,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6h go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= @@ -360,18 +367,16 @@ go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFw go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= -go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= -go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -380,28 +385,28 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -411,18 +416,17 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -430,8 +434,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -469,22 +473,22 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.33.5 h1:YR+uhYj05jdRpcksv8kjSliW+v9hwXxn6Cv10aR8Juw= -k8s.io/api v0.33.5/go.mod h1:2gzShdwXKT5yPGiqrTrn/U/nLZ7ZyT4WuAj3XGDVgVs= -k8s.io/apiextensions-apiserver v0.33.5 h1:93NZh6rmrcamX/tfv/dZrTsMiQX69ufANmDcKPEgSeA= -k8s.io/apiextensions-apiserver v0.33.5/go.mod h1:JIbyQnNlu6nQa7b1vgFi51pmlXOk8mdn0WJwUJnz/7U= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.33.5 h1:X1Gy33r4YkRLRqTjGjofk7X1/EjSLEVSJ/A+1qjoj60= -k8s.io/apiserver v0.33.5/go.mod h1:Q+b5Btbc8x0PqOCeh/xBTesKk+cXQRN+PF2wdrTKDeg= -k8s.io/client-go v0.33.5 h1:I8BdmQGxInpkMEnJvV6iG7dqzP3JRlpZZlib3OMFc3o= -k8s.io/client-go v0.33.5/go.mod h1:W8PQP4MxbM4ypgagVE65mUUqK1/ByQkSALF9tzuQ6u0= -k8s.io/cluster-bootstrap v0.33.3 h1:u2NTxJ5CFSBFXaDxLQoOWMly8eni31psVso+caq6uwI= -k8s.io/cluster-bootstrap v0.33.3/go.mod h1:p970f8u8jf273zyQ5raD8WUu2XyAl0SAWOY82o7i/ds= -k8s.io/code-generator v0.33.5 h1:KwkOvhwAaorjSwF2MQhhdhL3i8bBmAal/TWhX67kdHw= -k8s.io/code-generator v0.33.5/go.mod h1:Ra+sdZquRakeTGcEnQAPw6BmlZ92IvxwQQTX/XOvOIE= -k8s.io/component-base v0.33.5 h1:4D3kxjEx1pJRy3WHAZsmX3+LCpmd4ftE+2J4v6naTnQ= -k8s.io/component-base v0.33.5/go.mod h1:Zma1YjBVuuGxIbspj1vGR3/5blzo2ARf1v0QTtog1to= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.3 h1:uGH1qpDvSiYG4HVFqc6A3L4CKiX+aBWDrrsxHYK0Bdo= +k8s.io/apiserver v0.34.3/go.mod h1:QPnnahMO5C2m3lm6fPW3+JmyQbvHZQ8uudAu/493P2w= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= +k8s.io/cluster-bootstrap v0.34.2 h1:oKckPeunVCns37BntcsxaOesDul32yzGd3DFLjW2fc8= +k8s.io/cluster-bootstrap v0.34.2/go.mod h1:f21byPR7X5nt12ivZi+J3pb4sG4SH6VySX8KAAJA8BY= +k8s.io/code-generator v0.34.3 h1:6ipJKsJZZ9q21BO8I2jEj4OLN3y8/1n4aihKN0xKmQk= +k8s.io/code-generator v0.34.3/go.mod h1:oW73UPYpGLsbRN8Ozkhd6ZzkF8hzFCiYmvEuWZDroI4= +k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= +k8s.io/component-base v0.34.3/go.mod h1:5iIlD8wPfWE/xSHTRfbjuvUul2WZbI2nOUK65XL0E/c= k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9 h1:1bLA4Agvs1DILmc+q2Bbcqjx6jOHO7YEFA+G+0aTZoc= k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q= @@ -502,44 +506,40 @@ k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8 k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/cluster-api v1.11.2 h1:uAczaBavU5Y6aDgyoXWtq28k1kalpSZnVItwXHusw1c= -sigs.k8s.io/cluster-api v1.11.2/go.mod h1:C1gJVAjMXRG+M+djjGYNkoi5kBMhFnOUI9QqZDAtMms= +sigs.k8s.io/cluster-api v1.12.0 h1:iFOz8b0LdrMJS5Df1Eb7wyvTkWqlTUM2LHFEHCeI6vA= +sigs.k8s.io/cluster-api v1.12.0/go.mod h1:+S6WJdi8UPdqv5q9nka5al3ed/Qa0zAcSBgzTaa9VKA= sigs.k8s.io/cluster-api/hack/tools v0.0.0-20250805173327-a7b9f27af519 h1:WkOO6Fg3tmbuFXqTMFfs80mapaQyBbdIC5p86LIVlBI= sigs.k8s.io/cluster-api/hack/tools v0.0.0-20250805173327-a7b9f27af519/go.mod h1:/Zjkh19AmjuI4piKJ1fAZW7k0cDZsSoAAcGHKGZJTZU= -sigs.k8s.io/cluster-api/test v1.11.0 h1:dvwMAb5rm4Z7Kj3l9FkeYTWfSthpN0oX3gvUrd8ej24= -sigs.k8s.io/cluster-api/test v1.11.0/go.mod h1:2f489Lp5TKPGVhNL6V3huq8fp6eb23APlY2cLbhuDBU= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/cluster-api/test v1.12.0 h1:OpxFwNSu9j9jY2v4dl90rSi5W9is8/vBXxGCTci5gyE= +sigs.k8s.io/cluster-api/test v1.12.0/go.mod h1:eS05aBCEZeKzcW7MGnT9U+zap4NCpkxHBWb10DshWp0= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20250620151452-b9a9ca01fd37 h1:NSnbH7C6/fYc5L3FxMQiSlFBqYi+32LnFsXwArzOlIM= sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20250620151452-b9a9ca01fd37/go.mod h1:zCcqn1oG9844T8/vZSYcnqOyoEmTHro4bliTJI6j4OY= sigs.k8s.io/controller-tools v0.18.0 h1:rGxGZCZTV2wJreeRgqVoWab/mfcumTMmSwKzoM9xrsE= sigs.k8s.io/controller-tools v0.18.0/go.mod h1:gLKoiGBriyNh+x1rWtUQnakUYEujErjXs9pf+x/8n1U= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/kind v0.29.0 h1:3TpCsyh908IkXXpcSnsMjWdwdWjIl7o9IMZImZCWFnI= -sigs.k8s.io/kind v0.29.0/go.mod h1:ldWQisw2NYyM6k64o/tkZng/1qQW7OlzcN5a8geJX3o= +sigs.k8s.io/kind v0.30.0 h1:2Xi1KFEfSMm0XDcvKnUt15ZfgRPCT0OnCBbpgh8DztY= +sigs.k8s.io/kind v0.30.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d h1:KLiQzLW3RZJR19+j4pw2h5iioyAyqCkDBEAFdnGa3N8= sigs.k8s.io/kubebuilder/docs/book/utils v0.0.0-20211028165026-57688c578b5d/go.mod h1:NRdZafr4zSCseLQggdvIMXa7umxf+Q+PJzrj3wFwiGE= -sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= -sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= -sigs.k8s.io/kustomize/cmd/config v0.20.1 h1:4APUORmZe2BYrsqgGfEKdd/r7gM6i43egLrUzilpiFo= -sigs.k8s.io/kustomize/cmd/config v0.20.1/go.mod h1:R7rQ8kxknVlXWVUIbxWtMgu8DCCNVtl8V0KrmeVd/KE= -sigs.k8s.io/kustomize/kustomize/v5 v5.7.1 h1:sYJsarwy/SDJfjjLMUqwFDGPwzUtMOQ1i1Ed49+XSbw= -sigs.k8s.io/kustomize/kustomize/v5 v5.7.1/go.mod h1:+5/SrBcJ4agx1SJknGuR/c9thwRSKLxnKoI5BzXFaLU= -sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78= -sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/kustomize/api v0.21.0 h1:I7nry5p8iDJbuRdYS7ez8MUvw7XVNPcIP5GkzzuXIIQ= +sigs.k8s.io/kustomize/api v0.21.0/go.mod h1:XGVQuR5n2pXKWbzXHweZU683pALGw/AMVO4zU4iS8SE= +sigs.k8s.io/kustomize/cmd/config v0.21.0 h1:ikLtzcNK9isBqSaXXhAg7LRCTNKdp70z5v/c4Y55DOw= +sigs.k8s.io/kustomize/cmd/config v0.21.0/go.mod h1:oxa6eRzeLWUcE7M3Rmio29Sfc4KpqGspHur3GjOYqNA= +sigs.k8s.io/kustomize/kustomize/v5 v5.8.0 h1:CCIJK7z/xJOlkXOaDOcL2jprV53a/eloiL02wg7oJJs= +sigs.k8s.io/kustomize/kustomize/v5 v5.8.0/go.mod h1:qewGAExYZK9LbPPbnJMPK5HQ8nsdxRzpclIg0qslzDo= +sigs.k8s.io/kustomize/kyaml v0.21.0 h1:7mQAf3dUwf0wBerWJd8rXhVcnkk5Tvn/q91cGkaP6HQ= +sigs.k8s.io/kustomize/kyaml v0.21.0/go.mod h1:hmxADesM3yUN2vbA5z1/YTBnzLJ1dajdqpQonwBL1FQ= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/release-sdk v0.11.0 h1:a+zjOO3tHm1NiVZgNcUWq5QrKmv7b63UZXw+XGdPGfk= sigs.k8s.io/release-sdk v0.11.0/go.mod h1:sjbFpskyVjCXcFBnI3Bj1iGQHGjDYPoHVyld/pT+TvU= sigs.k8s.io/release-utils v0.8.1 h1:qSA9p3vZzO6RAq7zvzupCZjR29+n3NK9DSJPe9bSf7w= sigs.k8s.io/release-utils v0.8.1/go.mod h1:vrQ3eR1VmudgX4OUwr4pUZEkYLRms9bdbv06mr3kchQ= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/main.go b/main.go index d510e7539..fdefb41c4 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/pflag" corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" cliflag "k8s.io/component-base/cli/flag" @@ -33,13 +34,16 @@ import ( logsv1 "k8s.io/component-base/logs/api/v1" _ "k8s.io/component-base/logs/json/register" "k8s.io/klog/v2" + "k8s.io/utils/ptr" clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" ipamv1 "sigs.k8s.io/cluster-api/api/ipam/v1beta2" + "sigs.k8s.io/cluster-api/controllers/crdmigrator" "sigs.k8s.io/cluster-api/util/flags" ctrl "sigs.k8s.io/controller-runtime" cache "sigs.k8s.io/controller-runtime/pkg/cache" client "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" + clientconfig "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/config" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/webhook" @@ -48,6 +52,7 @@ import ( infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" "sigs.k8s.io/cluster-api-provider-openstack/controllers" + "sigs.k8s.io/cluster-api-provider-openstack/feature" "sigs.k8s.io/cluster-api-provider-openstack/pkg/metrics" "sigs.k8s.io/cluster-api-provider-openstack/pkg/record" "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" @@ -92,10 +97,12 @@ var ( caCertsPath string showVersion bool scopeCacheMaxSize int + skipCRDMigrationPhases []string logOptions = logs.NewOptions() ) func init() { + _ = apiextensionsv1.AddToScheme(scheme) _ = clientgoscheme.AddToScheme(scheme) _ = clusterv1.AddToScheme(scheme) _ = ipamv1.AddToScheme(scheme) @@ -166,12 +173,21 @@ func InitFlags(fs *pflag.FlagSet) { fs.IntVar(&scopeCacheMaxSize, "scope-cache-max-size", 10, "The maximum credentials count the operator should keep in cache. Setting this value to 0 means no cache.") + fs.StringArrayVar(&skipCRDMigrationPhases, "skip-crd-migration-phases", []string{}, + "List of CRD migration phases to skip. Valid values are: StorageVersionMigration, CleanupManagedFields.") fs.BoolVar(&showVersion, "version", false, "Show current version and exit.") + + feature.MutableGates.AddFlag(fs) } // Add RBAC for the authorized diagnostics endpoint. // +kubebuilder:rbac:groups=authentication.k8s.io,resources=tokenreviews,verbs=create // +kubebuilder:rbac:groups=authorization.k8s.io,resources=subjectaccessreviews,verbs=create +// Setup CRD migrator +// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch;update;patch +// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions/status,verbs=get;list;watch;update;patch +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=openstackclusters;openstackmachines;openstackmachinetemplates;openstackclustertemplates;openstackfloatingippools;openstackservers;openstackclusteridentities,verbs=get;list;watch;patch;update +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=openstackclusters/status;openstackmachines/status;openstackmachinetemplates/status;openstackclustertemplates/status;openstackfloatingippools/status;openstackservers/status;openstackclusteridentities/status,verbs=get;patch;update func main() { InitFlags(pflag.CommandLine) @@ -199,7 +215,7 @@ func main() { }() } - cfg, err := config.GetConfigWithContext(os.Getenv("KUBECONTEXT")) + cfg, err := clientconfig.GetConfigWithContext(os.Getenv("KUBECONTEXT")) if err != nil { setupLog.Error(err, "unable to get kubeconfig") os.Exit(1) @@ -229,6 +245,8 @@ func main() { } } + setupLog.Info(fmt.Sprintf("Feature gates: %+v\n", feature.Gates)) + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme, Metrics: *metricsOpts, @@ -258,6 +276,9 @@ func main() { ), HealthProbeBindAddress: healthAddr, LeaderElectionReleaseOnCancel: true, + Controller: config.Controller{ + UsePriorityQueue: ptr.To[bool](feature.Gates.Enabled(feature.PriorityQueue)), + }, }) if err != nil { setupLog.Error(err, "unable to start manager") @@ -296,6 +317,43 @@ func setupChecks(mgr ctrl.Manager) { func setupReconcilers(ctx context.Context, mgr ctrl.Manager, caCerts []byte) { scopeFactory := scope.NewFactory(scopeCacheMaxSize) + crdMigratorConfig := map[client.Object]crdmigrator.ByObjectConfig{ + &infrav1.OpenStackCluster{}: { + UseCache: true, + }, + &infrav1.OpenStackMachine{}: { + UseCache: true, + }, + &infrav1.OpenStackMachineTemplate{}: { + UseCache: true, + }, + &infrav1.OpenStackClusterTemplate{}: { + UseCache: true, + }, + &infrav1alpha1.OpenStackFloatingIPPool{}: { + UseCache: true, + }, + &infrav1alpha1.OpenStackServer{}: { + UseCache: true, + }, + &infrav1alpha1.OpenStackClusterIdentity{}: { + UseCache: true, + }, + } + crdMigratorSkipPhases := []crdmigrator.Phase{} + for _, p := range skipCRDMigrationPhases { + crdMigratorSkipPhases = append(crdMigratorSkipPhases, crdmigrator.Phase(p)) + } + if err := (&crdmigrator.CRDMigrator{ + Client: mgr.GetClient(), + APIReader: mgr.GetAPIReader(), + SkipCRDMigrationPhases: crdMigratorSkipPhases, + Config: crdMigratorConfig, + }).SetupWithManager(ctx, mgr, concurrency(1)); err != nil { + setupLog.Error(err, "unable to setup CRD migrator") + os.Exit(1) + } + if err := (&controllers.OpenStackClusterReconciler{ Client: mgr.GetClient(), Recorder: mgr.GetEventRecorderFor("openstackcluster-controller"), diff --git a/metadata.yaml b/metadata.yaml index 9d59337bb..5a950bf39 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -27,4 +27,4 @@ releaseSeries: contract: v1beta1 - major: 0 minor: 13 - contract: v1beta2 + contract: v1beta1 diff --git a/netlify.toml b/netlify.toml index b2fcec717..a915db07e 100644 --- a/netlify.toml +++ b/netlify.toml @@ -4,7 +4,7 @@ command = "make -C docs/book build" publish = "docs/book/book" [build.environment] -GO_VERSION = "1.24.9" +GO_VERSION = "1.24.11" # Standard Netlify redirects [[redirects]] diff --git a/pkg/clients/loadbalancer.go b/pkg/clients/loadbalancer.go index 1c3582396..3cf46350d 100644 --- a/pkg/clients/loadbalancer.go +++ b/pkg/clients/loadbalancer.go @@ -51,6 +51,7 @@ type LbClient interface { DeletePool(id string) error CreatePoolMember(poolID string, opts pools.CreateMemberOptsBuilder) (*pools.Member, error) ListPoolMember(poolID string, opts pools.ListMembersOptsBuilder) ([]pools.Member, error) + GetPoolMember(poolID string, lbMemberID string) (*pools.Member, error) DeletePoolMember(poolID string, lbMemberID string) error CreateMonitor(opts monitors.CreateOptsBuilder) (*monitors.Monitor, error) ListMonitors(opts monitors.ListOptsBuilder) ([]monitors.Monitor, error) @@ -213,6 +214,15 @@ func (l lbClient) ListPoolMember(poolID string, opts pools.ListMembersOptsBuilde return pools.ExtractMembers(allPages) } +func (l lbClient) GetPoolMember(poolID string, lbMemberID string) (*pools.Member, error) { + mc := metrics.NewMetricPrometheusContext("loadbalancer_member", "get") + member, err := pools.GetMember(context.TODO(), l.serviceClient, poolID, lbMemberID).Extract() + if mc.ObserveRequest(err) != nil { + return nil, fmt.Errorf("error getting lbmember: %s", err) + } + return member, nil +} + func (l lbClient) DeletePoolMember(poolID string, lbMemberID string) error { mc := metrics.NewMetricPrometheusContext("loadbalancer_member", "delete") err := pools.DeleteMember(context.TODO(), l.serviceClient, poolID, lbMemberID).ExtractErr() diff --git a/pkg/clients/mock/loadbalancer.go b/pkg/clients/mock/loadbalancer.go index 59024521f..586f78843 100644 --- a/pkg/clients/mock/loadbalancer.go +++ b/pkg/clients/mock/loadbalancer.go @@ -251,6 +251,21 @@ func (mr *MockLbClientMockRecorder) GetPool(id any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPool", reflect.TypeOf((*MockLbClient)(nil).GetPool), id) } +// GetPoolMember mocks base method. +func (m *MockLbClient) GetPoolMember(poolID, lbMemberID string) (*pools.Member, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPoolMember", poolID, lbMemberID) + ret0, _ := ret[0].(*pools.Member) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPoolMember indicates an expected call of GetPoolMember. +func (mr *MockLbClientMockRecorder) GetPoolMember(poolID, lbMemberID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPoolMember", reflect.TypeOf((*MockLbClient)(nil).GetPoolMember), poolID, lbMemberID) +} + // ListListeners mocks base method. func (m *MockLbClient) ListListeners(opts listeners.ListOptsBuilder) ([]listeners.Listener, error) { m.ctrl.T.Helper() diff --git a/pkg/cloud/services/compute/instance.go b/pkg/cloud/services/compute/instance.go index 255faf6d5..506c637fd 100644 --- a/pkg/cloud/services/compute/instance.go +++ b/pkg/cloud/services/compute/instance.go @@ -65,8 +65,7 @@ func (s *Service) createInstanceImpl(eventObject runtime.Object, instanceSpec *I }) } - instanceCreateTimeout := getTimeout("CLUSTER_API_OPENSTACK_INSTANCE_CREATE_TIMEOUT", timeoutInstanceCreate) - instanceCreateTimeout *= time.Minute + instanceCreateTimeout := getTimeout("CLUSTER_API_OPENSTACK_INSTANCE_CREATE_TIMEOUT", timeoutInstanceCreate, time.Minute) // Don't set ImageRef on the server if we're booting from volume var serverImageRef string @@ -491,10 +490,15 @@ func (s *Service) DeleteInstance(eventObject runtime.Object, instanceStatus *Ins if err != nil { return false, err } - if i != nil { - return false, nil + // Server not found means it has been permanently deleted + if i == nil { + return true, nil + } + // Server in SOFT_DELETED or DELETED state means deletion succeeded. This respects OpenStack's soft delete policy. + if i.State() == infrav1.InstanceStateSoftDeleted || i.State() == infrav1.InstanceStateDeleted { + return true, nil } - return true, nil + return false, nil }) if err != nil { record.Warnf(eventObject, "FailedDeleteServer", "Failed to delete server %s with id %s: %v", instance.Name, instance.ID, err) @@ -606,14 +610,14 @@ func (s *Service) GetInstanceStatusByName(eventObject runtime.Object, name strin return nil, nil } -func getTimeout(name string, timeout int) time.Duration { +func getTimeout(name string, timeout int, unit time.Duration) time.Duration { if v := os.Getenv(name); v != "" { timeout, err := strconv.Atoi(v) if err == nil { - return time.Duration(timeout) + return time.Duration(timeout) * unit } } - return time.Duration(timeout) + return time.Duration(timeout) * unit } // requiresTagging checks if the instanceSpec requires tagging, diff --git a/pkg/cloud/services/compute/instance_test.go b/pkg/cloud/services/compute/instance_test.go index 32adcea7a..da86f6611 100644 --- a/pkg/cloud/services/compute/instance_test.go +++ b/pkg/cloud/services/compute/instance_test.go @@ -26,6 +26,7 @@ import ( "github.com/go-logr/logr/testr" "github.com/google/go-cmp/cmp" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" @@ -1021,3 +1022,101 @@ func TestService_ReconcileInstance(t *testing.T) { }) } } + +func TestService_DeleteInstance(t *testing.T) { + const ( + serverID = "ce96e584-7ebc-46d6-9e55-987d72e3806c" + serverName = "test-server" + ) + + tests := []struct { + name string + expect func(m *mock.MockComputeClientMockRecorder) + wantErr bool + }{ + { + name: "Server not found after delete", + expect: func(m *mock.MockComputeClientMockRecorder) { + m.DeleteServer(serverID).Return(nil) + m.GetServer(serverID).Return(nil, &gophercloud.ErrResourceNotFound{}) + }, + wantErr: false, + }, + { + name: "Server in SOFT_DELETED state", + expect: func(m *mock.MockComputeClientMockRecorder) { + m.DeleteServer(serverID).Return(nil) + m.GetServer(serverID).Return(&servers.Server{ + ID: serverID, + Name: serverName, + Status: "SOFT_DELETED", + }, nil) + }, + wantErr: false, + }, + { + name: "Server in DELETED state", + expect: func(m *mock.MockComputeClientMockRecorder) { + m.DeleteServer(serverID).Return(nil) + m.GetServer(serverID).Return(&servers.Server{ + ID: serverID, + Name: serverName, + Status: "DELETED", + }, nil) + }, + wantErr: false, + }, + { + name: "Delete API returns not found", + expect: func(m *mock.MockComputeClientMockRecorder) { + m.DeleteServer(serverID).Return(&gophercloud.ErrResourceNotFound{}) + }, + wantErr: false, + }, + { + name: "Delete API returns error", + expect: func(m *mock.MockComputeClientMockRecorder) { + m.DeleteServer(serverID).Return(errors.New("API error")) + }, + wantErr: true, + }, + { + name: "GetServer returns error", + expect: func(m *mock.MockComputeClientMockRecorder) { + m.DeleteServer(serverID).Return(nil) + m.GetServer(serverID).Return(nil, errors.New("API error")) + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) + log := testr.New(t) + mockScopeFactory := scope.NewMockScopeFactory(mockCtrl, "") + + tt.expect(mockScopeFactory.ComputeClient.EXPECT()) + + s, err := NewService(scope.NewWithLogger(mockScopeFactory, log)) + if err != nil { + t.Fatalf("Failed to create service: %v", err) + } + + instanceStatus := &InstanceStatus{ + server: &servers.Server{ + ID: serverID, + Name: serverName, + }, + logger: log, + } + + eventObject := &infrav1.OpenStackMachine{} + err = s.DeleteInstance(eventObject, instanceStatus) + if (err != nil) != tt.wantErr { + t.Errorf("Service.DeleteInstance() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/pkg/cloud/services/loadbalancer/loadbalancer.go b/pkg/cloud/services/loadbalancer/loadbalancer.go index efcd79a16..394a9ed0e 100644 --- a/pkg/cloud/services/loadbalancer/loadbalancer.go +++ b/pkg/cloud/services/loadbalancer/loadbalancer.go @@ -52,6 +52,7 @@ const ( const ( loadBalancerProvisioningStatusActive = "ACTIVE" loadBalancerProvisioningStatusPendingDelete = "PENDING_DELETE" + poolMemberProvisioningStatusActive = "ACTIVE" ) // Default values for Monitor, sync with `kubebuilder:default` annotations on APIServerLoadBalancerMonitor object. @@ -710,11 +711,20 @@ func (s *Service) ReconcileLoadBalancerMember(openStackCluster *infrav1.OpenStac Tags: openStackCluster.Spec.Tags, } + if openStackCluster.Status.Network.ID != openStackCluster.Status.APIServerLoadBalancer.LoadBalancerNetwork.ID { + lbMemberOpts.SubnetID = openStackCluster.Status.Network.Subnets[0].ID + } + if _, err := s.waitForLoadBalancerActive(lbID); err != nil { return err } - if _, err := s.loadbalancerClient.CreatePoolMember(pool.ID, lbMemberOpts); err != nil { + member, err := s.loadbalancerClient.CreatePoolMember(pool.ID, lbMemberOpts) + if err != nil { + return err + } + + if _, err := s.waitForPoolMemberActive(pool.ID, member.ID); err != nil { return err } @@ -928,6 +938,25 @@ func (s *Service) waitForLoadBalancerActive(id string) (*loadbalancers.LoadBalan return lb, nil } +// Possible Pool Member states are documented here: https://docs.openstack.org/api-ref/load-balancer/v2/#prov-status +func (s *Service) waitForPoolMemberActive(poolID, memberID string) (*pools.Member, error) { + var member *pools.Member + + s.scope.Logger().Info("Waiting for pool member", "pool_id", poolID, "member_id", memberID, "targetStatus", "ACTIVE") + err := wait.ExponentialBackoff(backoff, func() (bool, error) { + var err error + member, err = s.loadbalancerClient.GetPoolMember(poolID, memberID) + if err != nil { + return false, err + } + return member.ProvisioningStatus == poolMemberProvisioningStatusActive, nil + }) + if err != nil { + return nil, err + } + return member, nil +} + func (s *Service) waitForListener(id, target string) error { s.scope.Logger().Info("Waiting for load balancer listener", "id", id, "targetStatus", target) return wait.ExponentialBackoff(backoff, func() (bool, error) { diff --git a/pkg/cloud/services/loadbalancer/loadbalancer_test.go b/pkg/cloud/services/loadbalancer/loadbalancer_test.go index 4c92670ba..9a2ec0d61 100644 --- a/pkg/cloud/services/loadbalancer/loadbalancer_test.go +++ b/pkg/cloud/services/loadbalancer/loadbalancer_test.go @@ -32,6 +32,7 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/providers" . "github.com/onsi/gomega" //nolint:revive "go.uber.org/mock/gomock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" @@ -926,3 +927,279 @@ func Test_getOrCreateAPILoadBalancer(t *testing.T) { }) } } + +func Test_ReconcileLoadBalancerMember(t *testing.T) { + g := NewWithT(t) + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + const ( + clusterName = "AAAAA" + clusterResourceName = "k8s-clusterapi-cluster-AAAAA" + memberIP = "10.0.0.1" + wrongMemberIP = "10.0.0.20" + port = 6443 + machineName = "machine-1" + + clusterNetID = "aaaaaaaa-bbbb-cccc-dddd-111111111111" + subnetID = "aaaaaaaa-bbbb-cccc-dddd-222222222222" + lbID = "aaaaaaaa-bbbb-cccc-dddd-333333333333" + listenerID = "aaaaaaaa-bbbb-cccc-dddd-444444444444" + poolID = "aaaaaaaa-bbbb-cccc-dddd-555555555555" + memberID = "aaaaaaaa-bbbb-cccc-dddd-666666666666" + lbNetOtherID = "aaaaaaaa-bbbb-cccc-dddd-999999999999" + ) + + makeCluster := func(provider *string, lbNetworkID string) *infrav1.OpenStackCluster { + return &infrav1.OpenStackCluster{ + Spec: infrav1.OpenStackClusterSpec{ + APIServerLoadBalancer: &infrav1.APIServerLoadBalancer{ + Enabled: ptr.To(true), + Provider: provider, + Network: &infrav1.NetworkParam{ + ID: &lbNetworkID, + }, + }, + DisableAPIServerFloatingIP: ptr.To(true), + ControlPlaneEndpoint: &clusterv1beta1.APIEndpoint{ + Host: apiHostname, + Port: port, + }, + Tags: []string{"k8s", "clusterapi"}, + }, + Status: infrav1.OpenStackClusterStatus{ + APIServerLoadBalancer: &infrav1.LoadBalancer{ + ID: lbID, + LoadBalancerNetwork: &infrav1.NetworkStatusWithSubnets{ + NetworkStatus: infrav1.NetworkStatus{ + ID: lbNetworkID, + }, + }, + }, + Network: &infrav1.NetworkStatusWithSubnets{ + NetworkStatus: infrav1.NetworkStatus{ + ID: clusterNetID, + }, + Subnets: []infrav1.Subnet{ + {ID: subnetID}, + }, + }, + }, + } + } + + openStackMachine := &infrav1.OpenStackMachine{ + ObjectMeta: metav1.ObjectMeta{Name: machineName}, + } + + lbtests := []struct { + name string + clusterSpec *infrav1.OpenStackCluster + expectNetwork func(m *mock.MockNetworkClientMockRecorder) + expectLoadBalancer func(m *mock.MockLbClientMockRecorder) + wantError error + }{ + { + name: "LB member exists, dont create", + clusterSpec: makeCluster(nil, clusterNetID), + expectNetwork: func(*mock.MockNetworkClientMockRecorder) {}, + expectLoadBalancer: func(m *mock.MockLbClientMockRecorder) { + activeLB := loadbalancers.LoadBalancer{ + ID: lbID, + Name: clusterResourceName + "-kubeapi", + ProvisioningStatus: "ACTIVE", + } + m.GetLoadBalancer(lbID).Return(&activeLB, nil).AnyTimes() + + pool := pools.Pool{ + ID: poolID, + Name: fmt.Sprintf("%s-kubeapi-%d", clusterResourceName, port), + } + m.ListPools(pools.ListOpts{Name: pool.Name}).Return([]pools.Pool{pool}, nil) + + member := pools.Member{ + Name: fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName), + Address: memberIP, + } + m.ListPoolMember(poolID, pools.ListMembersOpts{Name: member.Name}).Return([]pools.Member{member}, nil) + }, + wantError: nil, + }, + { + name: "No LB member, create", + clusterSpec: makeCluster(nil, clusterNetID), + expectNetwork: func(*mock.MockNetworkClientMockRecorder) {}, + expectLoadBalancer: func(m *mock.MockLbClientMockRecorder) { + activeLB := loadbalancers.LoadBalancer{ + ID: lbID, + Name: clusterResourceName + "-kubeapi", + ProvisioningStatus: "ACTIVE", + } + m.GetLoadBalancer(lbID).Return(&activeLB, nil).AnyTimes() + + pool := pools.Pool{ + ID: poolID, + Name: fmt.Sprintf("%s-kubeapi-%d", clusterResourceName, port), + } + m.ListPools(pools.ListOpts{Name: pool.Name}).Return([]pools.Pool{pool}, nil) + + poolMemberName := fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName) + m.ListPoolMember(poolID, pools.ListMembersOpts{Name: poolMemberName}).Return([]pools.Member{}, nil) + + m.CreatePoolMember( + poolID, + gomock.AssignableToTypeOf(pools.CreateMemberOpts{}), + ).DoAndReturn(func(_ string, got pools.CreateMemberOpts) (*pools.Member, error) { + // SubnetID must be empty here + g.Expect(got.SubnetID).To(Equal("")) + return &pools.Member{ID: memberID}, nil + }) + + pendingMember := pools.Member{ + ID: memberID, + Name: poolMemberName, + ProvisioningStatus: "PENDING_CREATE", + } + m.GetPoolMember(poolID, memberID).Return(&pendingMember, nil) + + activeMember := pendingMember + activeMember.ProvisioningStatus = "ACTIVE" + m.GetPoolMember(poolID, memberID).Return(&activeMember, nil) + }, + wantError: nil, + }, + { + name: "No pool found, return error", + clusterSpec: makeCluster(nil, clusterNetID), + expectNetwork: func(*mock.MockNetworkClientMockRecorder) {}, + expectLoadBalancer: func(m *mock.MockLbClientMockRecorder) { + activeLB := loadbalancers.LoadBalancer{ + ID: lbID, + Name: clusterResourceName + "-kubeapi", + ProvisioningStatus: "ACTIVE", + } + m.GetLoadBalancer(lbID).Return(&activeLB, nil).AnyTimes() + + poolName := fmt.Sprintf("%s-kubeapi-%d", clusterResourceName, port) + m.ListPools(pools.ListOpts{Name: poolName}).Return([]pools.Pool{}, nil) + }, + wantError: errors.New("load balancer pool does not exist yet"), + }, + { + name: "LB member with wrong address, re-create", + clusterSpec: makeCluster(nil, clusterNetID), + expectNetwork: func(*mock.MockNetworkClientMockRecorder) {}, + expectLoadBalancer: func(m *mock.MockLbClientMockRecorder) { + activeLB := loadbalancers.LoadBalancer{ + ID: lbID, + Name: clusterResourceName + "-kubeapi", + ProvisioningStatus: "ACTIVE", + } + m.GetLoadBalancer(lbID).Return(&activeLB, nil).AnyTimes() + + pool := pools.Pool{ + ID: poolID, + Name: fmt.Sprintf("%s-kubeapi-%d", clusterResourceName, port), + } + m.ListPools(pools.ListOpts{Name: pool.Name}).Return([]pools.Pool{pool}, nil) + + poolMemberName := fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName) + + member := pools.Member{ + Name: poolMemberName, + Address: wrongMemberIP, + ID: memberID, + } + m.ListPoolMember(poolID, pools.ListMembersOpts{Name: member.Name}).Return([]pools.Member{member}, nil) + + m.DeletePoolMember(poolID, memberID).Return(nil) + + m.CreatePoolMember( + poolID, + gomock.AssignableToTypeOf(pools.CreateMemberOpts{}), + ).DoAndReturn(func(_ string, got pools.CreateMemberOpts) (*pools.Member, error) { + // SubnetID must be empty here + g.Expect(got.SubnetID).To(Equal("")) + return &pools.Member{ID: memberID}, nil + }) + + activeMember := pools.Member{ + ID: memberID, + Name: poolMemberName, + ProvisioningStatus: "ACTIVE", + } + m.GetPoolMember(poolID, memberID).Return(&activeMember, nil).AnyTimes() + }, + wantError: nil, + }, + { + name: "different LB and cluster networks, set SubnetID on member create", + clusterSpec: makeCluster(nil, lbNetOtherID), + expectNetwork: func(*mock.MockNetworkClientMockRecorder) { + // not used by this path + }, + expectLoadBalancer: func(m *mock.MockLbClientMockRecorder) { + // LB initially ACTIVE whenever we wait + activeLB := loadbalancers.LoadBalancer{ + ID: lbID, + Name: clusterResourceName + "-kubeapi", + ProvisioningStatus: "ACTIVE", + } + m.GetLoadBalancer(lbID).Return(&activeLB, nil).AnyTimes() + + pool := pools.Pool{ + ID: poolID, + Name: fmt.Sprintf("%s-kubeapi-%d", clusterResourceName, port), + } + m.ListPools(pools.ListOpts{Name: pool.Name}).Return([]pools.Pool{pool}, nil) + + poolMemberName := fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName) + m.ListPoolMember(poolID, pools.ListMembersOpts{Name: poolMemberName}).Return([]pools.Member{}, nil) + + // Expect CreatePoolMember; capture opts to assert SubnetID is set + m.CreatePoolMember( + poolID, + gomock.AssignableToTypeOf(pools.CreateMemberOpts{}), + ).DoAndReturn(func(_ string, got pools.CreateMemberOpts) (*pools.Member, error) { + g.Expect(got.Address).To(Equal(memberIP)) + g.Expect(got.ProtocolPort).To(Equal(port)) + expName := fmt.Sprintf("%s-kubeapi-%d-%s", clusterResourceName, port, machineName) + g.Expect(got.Name).To(Equal(expName)) + g.Expect(got.SubnetID).To(Equal(subnetID)) + // Tags should pass through + g.Expect(got.Tags).To(ConsistOf("k8s", "clusterapi")) + return &pools.Member{ID: memberID, Address: memberIP, ProtocolPort: port}, nil + }) + + activeMember := pools.Member{ + ID: memberID, + Name: poolMemberName, + ProvisioningStatus: "ACTIVE", + } + m.GetPoolMember(poolID, memberID).Return(&activeMember, nil).AnyTimes() + }, + wantError: nil, + }, + } + + for _, tt := range lbtests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + log := testr.New(t) + + mockScopeFactory := scope.NewMockScopeFactory(mockCtrl, "") + lbs, err := NewService(scope.NewWithLogger(mockScopeFactory, log)) + g.Expect(err).NotTo(HaveOccurred()) + + tt.expectNetwork(mockScopeFactory.NetworkClient.EXPECT()) + tt.expectLoadBalancer(mockScopeFactory.LbClient.EXPECT()) + + err = lbs.ReconcileLoadBalancerMember(tt.clusterSpec, openStackMachine, clusterName, memberIP) + if tt.wantError != nil { + g.Expect(err).To(MatchError(tt.wantError)) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + }) + } +} diff --git a/pkg/generated/applyconfiguration/api/v1alpha1/openstackclusteridentity.go b/pkg/generated/applyconfiguration/api/v1alpha1/openstackclusteridentity.go index 4a75dae4d..a9334120a 100644 --- a/pkg/generated/applyconfiguration/api/v1alpha1/openstackclusteridentity.go +++ b/pkg/generated/applyconfiguration/api/v1alpha1/openstackclusteridentity.go @@ -81,6 +81,7 @@ func extractOpenStackClusterIdentity(openStackClusterIdentity *apiv1alpha1.OpenS b.WithAPIVersion("infrastructure.cluster.x-k8s.io/v1alpha1") return b, nil } +func (b OpenStackClusterIdentityApplyConfiguration) IsApplyConfiguration() {} // WithKind sets the Kind field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. @@ -248,8 +249,24 @@ func (b *OpenStackClusterIdentityApplyConfiguration) WithSpec(value *OpenStackCl return b } +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *OpenStackClusterIdentityApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *OpenStackClusterIdentityApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + // GetName retrieves the value of the Name field in the declarative configuration. func (b *OpenStackClusterIdentityApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.ObjectMetaApplyConfiguration.Name } + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *OpenStackClusterIdentityApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/generated/applyconfiguration/api/v1alpha1/openstackserver.go b/pkg/generated/applyconfiguration/api/v1alpha1/openstackserver.go index 0e034aad5..49dc266d8 100644 --- a/pkg/generated/applyconfiguration/api/v1alpha1/openstackserver.go +++ b/pkg/generated/applyconfiguration/api/v1alpha1/openstackserver.go @@ -82,6 +82,7 @@ func extractOpenStackServer(openStackServer *apiv1alpha1.OpenStackServer, fieldM b.WithAPIVersion("infrastructure.cluster.x-k8s.io/v1alpha1") return b, nil } +func (b OpenStackServerApplyConfiguration) IsApplyConfiguration() {} // WithKind sets the Kind field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. @@ -257,8 +258,24 @@ func (b *OpenStackServerApplyConfiguration) WithStatus(value *OpenStackServerSta return b } +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *OpenStackServerApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *OpenStackServerApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + // GetName retrieves the value of the Name field in the declarative configuration. func (b *OpenStackServerApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.ObjectMetaApplyConfiguration.Name } + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *OpenStackServerApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/clusterinitialization.go b/pkg/generated/applyconfiguration/api/v1beta1/clusterinitialization.go new file mode 100644 index 000000000..4896451b3 --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/clusterinitialization.go @@ -0,0 +1,39 @@ +/* +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +// ClusterInitializationApplyConfiguration represents a declarative configuration of the ClusterInitialization type for use +// with apply. +type ClusterInitializationApplyConfiguration struct { + Provisioned *bool `json:"provisioned,omitempty"` +} + +// ClusterInitializationApplyConfiguration constructs a declarative configuration of the ClusterInitialization type for use with +// apply. +func ClusterInitialization() *ClusterInitializationApplyConfiguration { + return &ClusterInitializationApplyConfiguration{} +} + +// WithProvisioned sets the Provisioned field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Provisioned field is set to the value of the last call. +func (b *ClusterInitializationApplyConfiguration) WithProvisioned(value bool) *ClusterInitializationApplyConfiguration { + b.Provisioned = &value + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/machineinitialization.go b/pkg/generated/applyconfiguration/api/v1beta1/machineinitialization.go new file mode 100644 index 000000000..588757505 --- /dev/null +++ b/pkg/generated/applyconfiguration/api/v1beta1/machineinitialization.go @@ -0,0 +1,39 @@ +/* +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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1beta1 + +// MachineInitializationApplyConfiguration represents a declarative configuration of the MachineInitialization type for use +// with apply. +type MachineInitializationApplyConfiguration struct { + Provisioned *bool `json:"provisioned,omitempty"` +} + +// MachineInitializationApplyConfiguration constructs a declarative configuration of the MachineInitialization type for use with +// apply. +func MachineInitialization() *MachineInitializationApplyConfiguration { + return &MachineInitializationApplyConfiguration{} +} + +// WithProvisioned sets the Provisioned field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Provisioned field is set to the value of the last call. +func (b *MachineInitializationApplyConfiguration) WithProvisioned(value bool) *MachineInitializationApplyConfiguration { + b.Provisioned = &value + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/openstackcluster.go b/pkg/generated/applyconfiguration/api/v1beta1/openstackcluster.go index 2db334896..33c22fd97 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/openstackcluster.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/openstackcluster.go @@ -82,6 +82,7 @@ func extractOpenStackCluster(openStackCluster *apiv1beta1.OpenStackCluster, fiel b.WithAPIVersion("infrastructure.cluster.x-k8s.io/v1beta1") return b, nil } +func (b OpenStackClusterApplyConfiguration) IsApplyConfiguration() {} // WithKind sets the Kind field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. @@ -257,8 +258,24 @@ func (b *OpenStackClusterApplyConfiguration) WithStatus(value *OpenStackClusterS return b } +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *OpenStackClusterApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *OpenStackClusterApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + // GetName retrieves the value of the Name field in the declarative configuration. func (b *OpenStackClusterApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.ObjectMetaApplyConfiguration.Name } + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *OpenStackClusterApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/openstackclusterstatus.go b/pkg/generated/applyconfiguration/api/v1beta1/openstackclusterstatus.go index 898b9c3f9..9873807f8 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/openstackclusterstatus.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/openstackclusterstatus.go @@ -27,6 +27,7 @@ import ( // with apply. type OpenStackClusterStatusApplyConfiguration struct { Ready *bool `json:"ready,omitempty"` + Initialization *ClusterInitializationApplyConfiguration `json:"initialization,omitempty"` Network *NetworkStatusWithSubnetsApplyConfiguration `json:"network,omitempty"` ExternalNetwork *NetworkStatusApplyConfiguration `json:"externalNetwork,omitempty"` Router *RouterApplyConfiguration `json:"router,omitempty"` @@ -38,6 +39,7 @@ type OpenStackClusterStatusApplyConfiguration struct { Bastion *BastionStatusApplyConfiguration `json:"bastion,omitempty"` FailureReason *errors.DeprecatedCAPIClusterStatusError `json:"failureReason,omitempty"` FailureMessage *string `json:"failureMessage,omitempty"` + Conditions *corev1beta1.Conditions `json:"conditions,omitempty"` } // OpenStackClusterStatusApplyConfiguration constructs a declarative configuration of the OpenStackClusterStatus type for use with @@ -54,6 +56,14 @@ func (b *OpenStackClusterStatusApplyConfiguration) WithReady(value bool) *OpenSt return b } +// WithInitialization sets the Initialization field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Initialization field is set to the value of the last call. +func (b *OpenStackClusterStatusApplyConfiguration) WithInitialization(value *ClusterInitializationApplyConfiguration) *OpenStackClusterStatusApplyConfiguration { + b.Initialization = value + return b +} + // WithNetwork sets the Network field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Network field is set to the value of the last call. @@ -141,3 +151,11 @@ func (b *OpenStackClusterStatusApplyConfiguration) WithFailureMessage(value stri b.FailureMessage = &value return b } + +// WithConditions sets the Conditions field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Conditions field is set to the value of the last call. +func (b *OpenStackClusterStatusApplyConfiguration) WithConditions(value corev1beta1.Conditions) *OpenStackClusterStatusApplyConfiguration { + b.Conditions = &value + return b +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/openstackclustertemplate.go b/pkg/generated/applyconfiguration/api/v1beta1/openstackclustertemplate.go index 20390c2aa..fca6b3200 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/openstackclustertemplate.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/openstackclustertemplate.go @@ -81,6 +81,7 @@ func extractOpenStackClusterTemplate(openStackClusterTemplate *apiv1beta1.OpenSt b.WithAPIVersion("infrastructure.cluster.x-k8s.io/v1beta1") return b, nil } +func (b OpenStackClusterTemplateApplyConfiguration) IsApplyConfiguration() {} // WithKind sets the Kind field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. @@ -248,8 +249,24 @@ func (b *OpenStackClusterTemplateApplyConfiguration) WithSpec(value *OpenStackCl return b } +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *OpenStackClusterTemplateApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *OpenStackClusterTemplateApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + // GetName retrieves the value of the Name field in the declarative configuration. func (b *OpenStackClusterTemplateApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.ObjectMetaApplyConfiguration.Name } + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *OpenStackClusterTemplateApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/openstackmachine.go b/pkg/generated/applyconfiguration/api/v1beta1/openstackmachine.go index 9cd746673..ccb036b9f 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/openstackmachine.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/openstackmachine.go @@ -82,6 +82,7 @@ func extractOpenStackMachine(openStackMachine *apiv1beta1.OpenStackMachine, fiel b.WithAPIVersion("infrastructure.cluster.x-k8s.io/v1beta1") return b, nil } +func (b OpenStackMachineApplyConfiguration) IsApplyConfiguration() {} // WithKind sets the Kind field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. @@ -257,8 +258,24 @@ func (b *OpenStackMachineApplyConfiguration) WithStatus(value *OpenStackMachineS return b } +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *OpenStackMachineApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *OpenStackMachineApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + // GetName retrieves the value of the Name field in the declarative configuration. func (b *OpenStackMachineApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.ObjectMetaApplyConfiguration.Name } + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *OpenStackMachineApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinestatus.go b/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinestatus.go index 03db4df1c..cba090d31 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinestatus.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinestatus.go @@ -29,6 +29,7 @@ import ( // with apply. type OpenStackMachineStatusApplyConfiguration struct { Ready *bool `json:"ready,omitempty"` + Initialization *MachineInitializationApplyConfiguration `json:"initialization,omitempty"` InstanceID *string `json:"instanceID,omitempty"` Addresses []v1.NodeAddress `json:"addresses,omitempty"` InstanceState *apiv1beta1.InstanceState `json:"instanceState,omitempty"` @@ -53,6 +54,14 @@ func (b *OpenStackMachineStatusApplyConfiguration) WithReady(value bool) *OpenSt return b } +// WithInitialization sets the Initialization field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Initialization field is set to the value of the last call. +func (b *OpenStackMachineStatusApplyConfiguration) WithInitialization(value *MachineInitializationApplyConfiguration) *OpenStackMachineStatusApplyConfiguration { + b.Initialization = value + return b +} + // WithInstanceID sets the InstanceID field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the InstanceID field is set to the value of the last call. diff --git a/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinetemplate.go b/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinetemplate.go index 2f705cd4f..02940086a 100644 --- a/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinetemplate.go +++ b/pkg/generated/applyconfiguration/api/v1beta1/openstackmachinetemplate.go @@ -81,6 +81,7 @@ func extractOpenStackMachineTemplate(openStackMachineTemplate *apiv1beta1.OpenSt b.WithAPIVersion("infrastructure.cluster.x-k8s.io/v1beta1") return b, nil } +func (b OpenStackMachineTemplateApplyConfiguration) IsApplyConfiguration() {} // WithKind sets the Kind field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. @@ -248,8 +249,24 @@ func (b *OpenStackMachineTemplateApplyConfiguration) WithSpec(value *OpenStackMa return b } +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *OpenStackMachineTemplateApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *OpenStackMachineTemplateApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + // GetName retrieves the value of the Name field in the declarative configuration. func (b *OpenStackMachineTemplateApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.ObjectMetaApplyConfiguration.Name } + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *OpenStackMachineTemplateApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/generated/applyconfiguration/internal/internal.go b/pkg/generated/applyconfiguration/internal/internal.go index b11934fa5..f79df4f3d 100644 --- a/pkg/generated/applyconfiguration/internal/internal.go +++ b/pkg/generated/applyconfiguration/internal/internal.go @@ -22,7 +22,7 @@ import ( fmt "fmt" sync "sync" - typed "sigs.k8s.io/structured-merge-diff/v4/typed" + typed "sigs.k8s.io/structured-merge-diff/v6/typed" ) func Parser() *typed.Parser { @@ -598,6 +598,12 @@ var schemaYAML = typed.YAMLObject(`types: - name: type type: scalar: string +- name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ClusterInitialization + map: + fields: + - name: provisioned + type: + scalar: boolean - name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ExternalRouterIPParam map: fields: @@ -675,6 +681,12 @@ var schemaYAML = typed.YAMLObject(`types: elementType: scalar: string elementRelationship: atomic +- name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.MachineInitialization + map: + fields: + - name: provisioned + type: + scalar: boolean - name: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.MachineResources map: fields: @@ -915,6 +927,12 @@ var schemaYAML = typed.YAMLObject(`types: - name: bastionSecurityGroup type: namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SecurityGroupStatus + - name: conditions + type: + list: + elementType: + namedType: io.k8s.sigs.cluster-api.api.core.v1beta1.Condition + elementRelationship: atomic - name: controlPlaneSecurityGroup type: namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.SecurityGroupStatus @@ -932,6 +950,9 @@ var schemaYAML = typed.YAMLObject(`types: - name: failureReason type: scalar: string + - name: initialization + type: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.ClusterInitialization - name: network type: namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.NetworkStatusWithSubnets @@ -1114,6 +1135,9 @@ var schemaYAML = typed.YAMLObject(`types: - name: failureReason type: scalar: string + - name: initialization + type: + namedType: io.k8s.sigs.cluster-api-provider-openstack.api.v1beta1.MachineInitialization - name: instanceID type: scalar: string diff --git a/pkg/generated/applyconfiguration/utils.go b/pkg/generated/applyconfiguration/utils.go index 494dc2afa..17c2a3949 100644 --- a/pkg/generated/applyconfiguration/utils.go +++ b/pkg/generated/applyconfiguration/utils.go @@ -21,7 +21,7 @@ package applyconfiguration import ( runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" - testing "k8s.io/client-go/testing" + managedfields "k8s.io/apimachinery/pkg/util/managedfields" v1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1" v1beta1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" apiv1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/pkg/generated/applyconfiguration/api/v1alpha1" @@ -72,6 +72,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1beta1.BlockDeviceStorageApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("BlockDeviceVolume"): return &apiv1beta1.BlockDeviceVolumeApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("ClusterInitialization"): + return &apiv1beta1.ClusterInitializationApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("ExternalRouterIPParam"): return &apiv1beta1.ExternalRouterIPParamApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("FilterByNeutronTags"): @@ -84,6 +86,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1beta1.ImageParamApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("LoadBalancer"): return &apiv1beta1.LoadBalancerApplyConfiguration{} + case v1beta1.SchemeGroupVersion.WithKind("MachineInitialization"): + return &apiv1beta1.MachineInitializationApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("MachineResources"): return &apiv1beta1.MachineResourcesApplyConfiguration{} case v1beta1.SchemeGroupVersion.WithKind("ManagedSecurityGroups"): @@ -179,6 +183,6 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return nil } -func NewTypeConverter(scheme *runtime.Scheme) *testing.TypeConverter { - return &testing.TypeConverter{Scheme: scheme, TypeResolver: internal.Parser()} +func NewTypeConverter(scheme *runtime.Scheme) managedfields.TypeConverter { + return managedfields.NewSchemeTypeConverter(scheme, internal.Parser()) } diff --git a/pkg/generated/clientset/clientset/fake/clientset_generated.go b/pkg/generated/clientset/clientset/fake/clientset_generated.go index defcf12cf..23dffc61b 100644 --- a/pkg/generated/clientset/clientset/fake/clientset_generated.go +++ b/pkg/generated/clientset/clientset/fake/clientset_generated.go @@ -107,8 +107,8 @@ func NewClientset(objects ...runtime.Object) *Clientset { cs.AddReactor("*", "*", testing.ObjectReaction(o)) cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { var opts metav1.ListOptions - if watchActcion, ok := action.(testing.WatchActionImpl); ok { - opts = watchActcion.ListOptions + if watchAction, ok := action.(testing.WatchActionImpl); ok { + opts = watchAction.ListOptions } gvr := action.GetResource() ns := action.GetNamespace() diff --git a/pkg/utils/errors/errors.go b/pkg/utils/errors/errors.go index 5c7b18d0c..aed3639d6 100644 --- a/pkg/utils/errors/errors.go +++ b/pkg/utils/errors/errors.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package errors +package capoerrors import ( "errors" diff --git a/pkg/utils/errors/terminal.go b/pkg/utils/errors/terminal.go index 4e88356d9..52f5b20bb 100644 --- a/pkg/utils/errors/terminal.go +++ b/pkg/utils/errors/terminal.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package errors +package capoerrors import ( goerrors "errors" diff --git a/releasenotes/v0.12.6.md b/releasenotes/v0.12.6.md new file mode 100644 index 000000000..a1d39164d --- /dev/null +++ b/releasenotes/v0.12.6.md @@ -0,0 +1,54 @@ +## Changes since v0.12.5 +## :chart_with_upwards_trend: Overview +- 16 new commits merged +- 2 bugs fixed 🐛 + +## :bug: Bug Fixes +- Allow changing DNSNameservers in subnet config for OpenstackCluster (#2721) +- Remove invalid kustomizeconfig from config/webhook (#2852) + +## :seedling: Others +- Add bnallapeta to reviewers (#2849) +- Add Moshiur as reviewer (#2771) +- Bump go to 1.24.9 (#2786) +- Migrate CI to stable/2025.2 (#2813) +- Refactor generate-codegen (#2762) +- Remove mdbooth as a maintainer (#2760) + +## Dependencies + +### Added +- github.com/gkampitakis/ciinfo: [v0.3.2](https://github.com/gkampitakis/ciinfo/tree/v0.3.2) +- github.com/gkampitakis/go-diff: [v1.3.2](https://github.com/gkampitakis/go-diff/tree/v1.3.2) +- github.com/gkampitakis/go-snaps: [v0.5.15](https://github.com/gkampitakis/go-snaps/tree/v0.5.15) +- github.com/goccy/go-yaml: [v1.18.0](https://github.com/goccy/go-yaml/tree/v1.18.0) +- github.com/joshdk/go-junit: [v1.0.0](https://github.com/joshdk/go-junit/tree/v1.0.0) +- github.com/maruel/natural: [v1.1.1](https://github.com/maruel/natural/tree/v1.1.1) +- github.com/mfridman/tparse: [v0.18.0](https://github.com/mfridman/tparse/tree/v0.18.0) +- github.com/tidwall/gjson: [v1.18.0](https://github.com/tidwall/gjson/tree/v1.18.0) +- github.com/tidwall/match: [v1.1.1](https://github.com/tidwall/match/tree/v1.1.1) +- github.com/tidwall/pretty: [v1.2.1](https://github.com/tidwall/pretty/tree/v1.2.1) +- github.com/tidwall/sjson: [v1.2.5](https://github.com/tidwall/sjson/tree/v1.2.5) + +### Changed +- github.com/gophercloud/gophercloud/v2: [v2.8.0 → v2.9.0](https://github.com/gophercloud/gophercloud/compare/v2.8.0...v2.9.0) +- github.com/onsi/ginkgo/v2: [v2.25.2 → v2.27.2](https://github.com/onsi/ginkgo/compare/v2.25.2...v2.27.2) +- github.com/prometheus/client_golang: [v1.23.0 → v1.23.2](https://github.com/prometheus/client_golang/compare/v1.23.0...v1.23.2) +- github.com/prometheus/common: [v0.65.0 → v0.66.1](https://github.com/prometheus/common/compare/v0.65.0...v0.66.1) +- github.com/rogpeppe/go-internal: [v1.12.0 → v1.13.1](https://github.com/rogpeppe/go-internal/compare/v1.12.0...v1.13.1) +- github.com/spf13/pflag: [v1.0.9 → v1.0.10](https://github.com/spf13/pflag/compare/v1.0.9...v1.0.10) +- github.com/stretchr/testify: [v1.10.0 → v1.11.1](https://github.com/stretchr/testify/compare/v1.10.0...v1.11.1) +- google.golang.org/protobuf: v1.36.7 → v1.36.8 +- k8s.io/api: v0.31.12 → v0.31.14 +- k8s.io/apiextensions-apiserver: v0.31.12 → v0.31.14 +- k8s.io/apimachinery: v0.31.12 → v0.31.14 +- k8s.io/apiserver: v0.31.12 → v0.31.14 +- k8s.io/client-go: v0.31.12 → v0.31.14 +- k8s.io/code-generator: v0.31.12 → v0.31.14 +- k8s.io/component-base: v0.31.12 → v0.31.14 +- k8s.io/kms: v0.31.12 → v0.31.14 + +### Removed +- github.com/prashantv/gostub: [v1.1.0](https://github.com/prashantv/gostub/tree/v1.1.0) + +_Thanks to all our contributors!_ 😊 diff --git a/releasenotes/v0.13.1.md b/releasenotes/v0.13.1.md new file mode 100644 index 000000000..69728eabd --- /dev/null +++ b/releasenotes/v0.13.1.md @@ -0,0 +1,70 @@ +# Changes since v0.13.0 + +## :chart_with_upwards_trend: Overview + +- 19 new commits merged +- 2 bugs fixed 🐛 + +## :bug: Bug Fixes + +- Remove invalid kustomizeconfig from config/webhook (#2851) +- Uplift go to address CVEs (#2765) + +## :seedling: Others + +- Add bnallapeta to reviewers (#2847) +- Add Moshiur as reviewer (#2769) +- Bump go to 1.24.9 (#2788) +- Bump go version to 1.24.7 (#2719) +- Migrate CI to stable/2025.2 (#2812) +- Remove mdbooth as a maintainer (#2759) + +## Dependencies + +### Added + +- github.com/gkampitakis/ciinfo: [v0.3.2](https://github.com/gkampitakis/ciinfo/tree/v0.3.2) +- github.com/gkampitakis/go-diff: [v1.3.2](https://github.com/gkampitakis/go-diff/tree/v1.3.2) +- github.com/gkampitakis/go-snaps: [v0.5.15](https://github.com/gkampitakis/go-snaps/tree/v0.5.15) +- github.com/goccy/go-yaml: [v1.18.0](https://github.com/goccy/go-yaml/tree/v1.18.0) +- github.com/joshdk/go-junit: [v1.0.0](https://github.com/joshdk/go-junit/tree/v1.0.0) +- github.com/maruel/natural: [v1.1.1](https://github.com/maruel/natural/tree/v1.1.1) +- github.com/mfridman/tparse: [v0.18.0](https://github.com/mfridman/tparse/tree/v0.18.0) +- github.com/tidwall/gjson: [v1.18.0](https://github.com/tidwall/gjson/tree/v1.18.0) +- github.com/tidwall/match: [v1.1.1](https://github.com/tidwall/match/tree/v1.1.1) +- github.com/tidwall/pretty: [v1.2.1](https://github.com/tidwall/pretty/tree/v1.2.1) +- github.com/tidwall/sjson: [v1.2.5](https://github.com/tidwall/sjson/tree/v1.2.5) + +### Changed + +- github.com/coredns/corefile-migration: [v1.0.27 → v1.0.29](https://github.com/coredns/corefile-migration/compare/v1.0.27...v1.0.29) +- github.com/gophercloud/gophercloud/v2: [v2.8.0 → v2.9.0](https://github.com/gophercloud/gophercloud/compare/v2.8.0...v2.9.0) +- github.com/hashicorp/go-version: [v1.7.0 → v1.8.0](https://github.com/hashicorp/go-version/compare/v1.7.0...v1.8.0) +- github.com/k-orc/openstack-resource-controller/v2: [v2.2.0 → v2.3.0](https://github.com/k-orc/openstack-resource-controller/compare/v2.2.0...v2.3.0) +- github.com/onsi/ginkgo/v2: [v2.25.3 → v2.27.2](https://github.com/onsi/ginkgo/compare/v2.25.3...v2.27.2) +- github.com/ulikunitz/xz: [v0.5.12 → v0.5.15](https://github.com/ulikunitz/xz/compare/v0.5.12...v0.5.15) +- golang.org/x/crypto: v0.42.0 → v0.45.0 +- golang.org/x/mod: v0.27.0 → v0.29.0 +- golang.org/x/net: v0.43.0 → v0.47.0 +- golang.org/x/sync: v0.17.0 → v0.18.0 +- golang.org/x/sys: v0.36.0 → v0.38.0 +- golang.org/x/telemetry: 1a19826 → 078029d +- golang.org/x/term: v0.35.0 → v0.37.0 +- golang.org/x/text: v0.29.0 → v0.31.0 +- golang.org/x/tools: v0.36.0 → v0.38.0 +- k8s.io/api: v0.33.4 → v0.33.6 +- k8s.io/apiextensions-apiserver: v0.33.4 → v0.33.6 +- k8s.io/apimachinery: v0.33.4 → v0.33.6 +- k8s.io/apiserver: v0.33.4 → v0.33.6 +- k8s.io/client-go: v0.33.4 → v0.33.6 +- k8s.io/code-generator: v0.33.4 → v0.33.6 +- k8s.io/component-base: v0.33.4 → v0.33.6 +- k8s.io/kms: v0.33.4 → v0.33.6 +- sigs.k8s.io/cluster-api/test: v1.11.1 → v1.11.3 +- sigs.k8s.io/cluster-api: v1.11.1 → v1.11.3 + +### Removed + +- github.com/prashantv/gostub: [v1.1.0](https://github.com/prashantv/gostub/tree/v1.1.0) + +_Thanks to all our contributors!_ 😊 diff --git a/releasenotes/v0.13.2.md b/releasenotes/v0.13.2.md new file mode 100644 index 000000000..2fea9d949 --- /dev/null +++ b/releasenotes/v0.13.2.md @@ -0,0 +1,34 @@ +## Highlights + +**NOTE:** Pervious releases of v0.13 (v0.13.0 and v0.13.1) incorrectly +advertised v1beta2 contract support in the metadata.yaml. This has been fixed in +this version. CAPO v0.13 does NOT fully implement v1beta2 and CAPI will in fact +use v1beta1 with it. We do however test CAPI's v1beta2 API and it works well +together with CAPO since there is still backwards compatibility with the v1beta1 +contract. + +## Changes since v0.13.1 +## :chart_with_upwards_trend: Overview +- 6 new commits merged +- 2 bugs fixed 🐛 + +## :bug: Bug Fixes +- Codegen: Remove sigs.k8s.io/cluster-api/api/ipam/v1beta1 (#2884) +- Revert v1beta2 contract (#2905) + +## :seedling: Others +- Bump go version to 1.24.11 (#2879) +- Do not run golanci-lint update on PRs (#2904) + +## Dependencies + +### Added +_Nothing has changed._ + +### Changed +_Nothing has changed._ + +### Removed +_Nothing has changed._ + +_Thanks to all our contributors!_ 😊 diff --git a/releasenotes/v0.14.0-alpha.0.md b/releasenotes/v0.14.0-alpha.0.md new file mode 100644 index 000000000..fbbb6a6a7 --- /dev/null +++ b/releasenotes/v0.14.0-alpha.0.md @@ -0,0 +1,201 @@ +🚨 This is a ALPHA RELEASE. Use it only for testing purposes. If you find any bugs, file an [issue](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/issues/new). +
+More details about the release + +:warning: **ALPHA RELEASE NOTES** :warning: + +## Deprecation Warning + +The following fields are deprecated and will be removed in a future release: +- OpenStackMachine and OpenStackCluster `status.ready`. +- OpenStackMachine and OpenStackCluster `status.failureReason` and `status.failureMessage`. + +See these issues for more information: +- https://github.com/kubernetes-sigs/cluster-api-provider-openstack/issues/2374 +- https://github.com/kubernetes-sigs/cluster-api-provider-openstack/issues/2375 + +## Changes since v0.13.1 +## :chart_with_upwards_trend: Overview +- 97 new commits merged +- 7 feature additions ✨ +- 8 bugs fixed 🐛 + +## :sparkles: New Features +- Add OpenStackClusterIdentity for centralized credential management (#2682) +- Add PriorityQueue feature flag (#2823) +- CAPI v1beta2 conditions and deprecations for OSC (#2832) +- CAPI v1beta2 conditions and deprecations for OSM (#2872) +- Implement CRD migration (#2810) +- Switch to CAPI v1beta2 for manifests (#2717) +- Update Cluster API to v1.12.0 (#2873) + +## :bug: Bug Fixes +- Allow setting disablePortSecurity on OSM port (#2784) +- Correction of issue #2617 selected fixed subnet not applied (#2639) +- Define subnetID on LB member when networks differ (#2799) +- Ensure pool member reach active state (#2815) +- Fixes out-of-range bug when multiple ports are passed to getSGControlPlaneAdditionalPorts (#2677) +- Remove invalid kustomizeconfig from config/webhook (#2850) +- Revert v1beta2 contract (#2883) +- Uplift go 1.24.8 to address security issue (#2764) + +## :seedling: Others +- Add AGENTS.md (#2807) +- Add bastion and allowedCIDR to dev-test clusterclass (#2725) +- Add bnallapeta to reviewers (#2794) +- Add entry of new release branch for security scan (#2703) +- Add entry of new release branch to depandabot (#2692) +- Add github workflow for pull request to have github approval (#2743) +- Add link check in PR to improve docs (#2741) +- Add missing hash in github workflow (#2731) +- Add Moshiur as reviewer (#2768) +- Add new config to pr-link check (#2876) +- Add PR title verify workflow for CAPO (#2728) +- Add yamlling workflow to check yaml files in pull request (#2845) +- Bump cloudbuild image (#2691) +- Bump e2e to use k8s v1.34.2 (#2836) +- Bump go to 1.24.9 (#2787) +- Bump go version to 1.24.11 (#2877) +- Bump go version to 1.24.7 (#2718) +- Bump golanci-lint to v2.7.2 (#2894) +- Bump golangci-lint to v2.6.1 (#2825) +- Bump golangci-lint to v2.6.2 (#2858) +- Cmd: strip out symbol table & DWARF debugging info (#2750) +- Dependabot: Hold crypto on release-0.12, unify spacing (#2709) +- Dockerfile: add the ARG defaulting within the Dockerfile (#2751) +- Don't run golanci-lint update on PRs (#2659) +- E2E: Add quick-start test using ClusterClass (#2793) +- E2E: Cleanup upgrade patches (#2831) +- E2E: Fix log collection for machines (#2795) +- E2E: Fix the CCM patch for the v1beta1 cluster-template (#2766) +- E2E: Run clusterctl upgrade from v0.13 (#2714) +- E2E: Use kind cluster for clusterctl upgrade tests (#2792) +- E2E: Use secret instead of host path for CCM config (#2744) +- Exclude `openshift` dir from boilerplate (#2901) +- Fix calcualtion of previous tag for release note generation (#2860) +- Fix release note generation (#2688) +- Fix release notes generation (#2846) +- Fix update-golangci-lint permissions (#2748) +- Fix URL for downloading gotestsum based on OS (#2684) +- Hold x/text since it requires go1.24 (#2740) +- Migrate CI to stable/2025.2 (#2809) +- Pin kustomize on release-0.12 (#2772) +- Refactor generate-codegen (#2761) +- Release automation workflow (#2791) +- Remove dependabot config and workflows for release-0.11 (#2779) +- Remove mdbooth as a maintainer (#2727) +- Set osc lb status when default networks are used (#2798) +- Update depandabot ignore list for release-0.12 (#2882) +- Update golangci lint v2.5.0 (#2752) +- Update log verbosity level according to CAPI (#2785) +- Uplift go 1.24.6 to address security issue (#2708) +- Release v0.13.1 (#2871) + +:book: Additionally, there have been 5 contributions to our documentation and book. (#2720, #2802, #2878, #2906, #2908) + +## Dependencies + +### Added +- github.com/go-jose/go-jose/v4: [v4.0.4](https://github.com/go-jose/go-jose/tree/v4.0.4) +- github.com/golang-jwt/jwt/v5: [v5.2.2](https://github.com/golang-jwt/jwt/tree/v5.2.2) +- github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus: [v1.0.1](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/providers/prometheus/v1.0.1) +- github.com/grpc-ecosystem/go-grpc-middleware/v2: [v2.3.0](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/v2.3.0) +- github.com/olekukonko/cat: [50322a0](https://github.com/olekukonko/cat/tree/50322a0) +- github.com/olekukonko/errors: [v1.1.0](https://github.com/olekukonko/errors/tree/v1.1.0) +- github.com/olekukonko/ll: [v0.1.1](https://github.com/olekukonko/ll/tree/v0.1.1) +- github.com/olekukonko/ts: [78ecb04](https://github.com/olekukonko/ts/tree/78ecb04) +- github.com/spiffe/go-spiffe/v2: [v2.5.0](https://github.com/spiffe/go-spiffe/tree/v2.5.0) +- github.com/zeebo/errs: [v1.4.0](https://github.com/zeebo/errs/tree/v1.4.0) +- go.etcd.io/raft/v3: v3.6.0 +- sigs.k8s.io/structured-merge-diff/v6: v6.3.0 + +### Changed +- cel.dev/expr: v0.19.1 → v0.24.0 +- github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp: [v1.25.0 → v1.26.0](https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/compare/detectors/gcp/v1.25.0...detectors/gcp/v1.26.0) +- github.com/cncf/xds/go: [cff3c89 → 2f00578](https://github.com/cncf/xds/compare/cff3c89...2f00578) +- github.com/docker/docker: [v28.3.3+incompatible → v28.5.2+incompatible](https://github.com/docker/docker/compare/v28.3.3...v28.5.2) +- github.com/docker/go-connections: [v0.5.0 → v0.6.0](https://github.com/docker/go-connections/compare/v0.5.0...v0.6.0) +- github.com/emicklei/go-restful/v3: [v3.12.2 → v3.13.0](https://github.com/emicklei/go-restful/compare/v3.12.2...v3.13.0) +- github.com/fsnotify/fsnotify: [v1.8.0 → v1.9.0](https://github.com/fsnotify/fsnotify/compare/v1.8.0...v1.9.0) +- github.com/fxamacker/cbor/v2: [v2.7.0 → v2.9.0](https://github.com/fxamacker/cbor/compare/v2.7.0...v2.9.0) +- github.com/google/cel-go: [v0.23.2 → v0.26.0](https://github.com/google/cel-go/compare/v0.23.2...v0.26.0) +- github.com/google/gnostic-models: [v0.6.9 → v0.7.0](https://github.com/google/gnostic-models/compare/v0.6.9...v0.7.0) +- github.com/google/pprof: [27863c8 → f64d9cf](https://github.com/google/pprof/compare/27863c8...f64d9cf) +- github.com/grpc-ecosystem/grpc-gateway/v2: [v2.24.0 → v2.26.3](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.24.0...v2.26.3) +- github.com/ianlancetaylor/demangle: [bd984b5 → f615e6b](https://github.com/ianlancetaylor/demangle/compare/bd984b5...f615e6b) +- github.com/jonboulle/clockwork: [v0.4.0 → v0.5.0](https://github.com/jonboulle/clockwork/compare/v0.4.0...v0.5.0) +- github.com/mattn/go-colorable: [v0.1.13 → v0.1.14](https://github.com/mattn/go-colorable/compare/v0.1.13...v0.1.14) +- github.com/mattn/go-runewidth: [v0.0.14 → v0.0.16](https://github.com/mattn/go-runewidth/compare/v0.0.14...v0.0.16) +- github.com/modern-go/reflect2: [v1.0.2 → 35a7c28](https://github.com/modern-go/reflect2/compare/v1.0.2...35a7c28) +- github.com/olekukonko/tablewriter: [v0.0.5 → v1.0.9](https://github.com/olekukonko/tablewriter/compare/v0.0.5...v1.0.9) +- github.com/pelletier/go-toml/v2: [v2.2.3 → v2.2.4](https://github.com/pelletier/go-toml/compare/v2.2.3...v2.2.4) +- github.com/rivo/uniseg: [v0.4.2 → v0.4.7](https://github.com/rivo/uniseg/compare/v0.4.2...v0.4.7) +- github.com/sagikazarmark/locafero: [v0.7.0 → v0.11.0](https://github.com/sagikazarmark/locafero/compare/v0.7.0...v0.11.0) +- github.com/sourcegraph/conc: [v0.3.0 → 5f936ab](https://github.com/sourcegraph/conc/compare/v0.3.0...5f936ab) +- github.com/spf13/afero: [v1.12.0 → v1.15.0](https://github.com/spf13/afero/compare/v1.12.0...v1.15.0) +- github.com/spf13/cast: [v1.7.1 → v1.10.0](https://github.com/spf13/cast/compare/v1.7.1...v1.10.0) +- github.com/spf13/cobra: [v1.9.1 → v1.10.1](https://github.com/spf13/cobra/compare/v1.9.1...v1.10.1) +- github.com/spf13/viper: [v1.20.1 → v1.21.0](https://github.com/spf13/viper/compare/v1.20.1...v1.21.0) +- go.etcd.io/bbolt: v1.3.11 → v1.4.2 +- go.etcd.io/etcd/api/v3: v3.5.22 → v3.6.6 +- go.etcd.io/etcd/client/pkg/v3: v3.5.22 → v3.6.6 +- go.etcd.io/etcd/client/v3: v3.5.22 → v3.6.6 +- go.etcd.io/etcd/pkg/v3: v3.5.21 → v3.6.4 +- go.etcd.io/etcd/server/v3: v3.5.21 → v3.6.4 +- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc: v0.58.0 → v0.60.0 +- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc: v1.33.0 → v1.34.0 +- go.opentelemetry.io/otel/exporters/otlp/otlptrace: v1.33.0 → v1.34.0 +- go.opentelemetry.io/otel/metric: v1.34.0 → v1.35.0 +- go.opentelemetry.io/otel/trace: v1.34.0 → v1.35.0 +- go.opentelemetry.io/otel: v1.34.0 → v1.35.0 +- go.opentelemetry.io/proto/otlp: v1.4.0 → v1.5.0 +- go.uber.org/zap: v1.27.0 → v1.27.1 +- golang.org/x/oauth2: v0.30.0 → v0.33.0 +- google.golang.org/genproto/googleapis/api: 5f5ef82 → a0af3ef +- google.golang.org/genproto/googleapis/rpc: 1a7da9e → a0af3ef +- google.golang.org/grpc: v1.71.3 → v1.72.3 +- k8s.io/api: v0.33.6 → v0.34.2 +- k8s.io/apiextensions-apiserver: v0.33.6 → v0.34.2 +- k8s.io/apimachinery: v0.33.6 → v0.34.2 +- k8s.io/apiserver: v0.33.6 → v0.34.2 +- k8s.io/client-go: v0.33.6 → v0.34.2 +- k8s.io/cluster-bootstrap: v0.33.3 → v0.34.2 +- k8s.io/code-generator: v0.33.6 → v0.34.2 +- k8s.io/component-base: v0.33.6 → v0.34.2 +- k8s.io/gengo/v2: 1244d31 → 85fd79d +- k8s.io/kms: v0.33.6 → v0.34.2 +- k8s.io/kube-openapi: 8b98d1e → f3f2b99 +- sigs.k8s.io/cluster-api/test: v1.11.3 → v1.12.0 +- sigs.k8s.io/cluster-api: v1.11.3 → v1.12.0 +- sigs.k8s.io/controller-runtime: v0.21.0 → v0.22.4 +- sigs.k8s.io/json: 9aa6b5e → cfa47c3 + +### Removed +- cloud.google.com/go/auth/oauth2adapt: v0.2.6 +- cloud.google.com/go/auth: v0.13.0 +- cloud.google.com/go/iam: v1.2.2 +- cloud.google.com/go/monitoring: v1.21.2 +- cloud.google.com/go/storage: v1.49.0 +- cloud.google.com/go: v0.116.0 +- github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric: [v0.48.1](https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/tree/exporter/metric/v0.48.1) +- github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping: [v0.48.1](https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/tree/internal/resourcemapping/v0.48.1) +- github.com/census-instrumentation/opencensus-proto: [v0.4.1](https://github.com/census-instrumentation/opencensus-proto/tree/v0.4.1) +- github.com/golang-jwt/jwt/v4: [v4.5.2](https://github.com/golang-jwt/jwt/tree/v4.5.2) +- github.com/golang/groupcache: [41bb18b](https://github.com/golang/groupcache/tree/41bb18b) +- github.com/google/s2a-go: [v0.1.8](https://github.com/google/s2a-go/tree/v0.1.8) +- github.com/googleapis/enterprise-certificate-proxy: [v0.3.4](https://github.com/googleapis/enterprise-certificate-proxy/tree/v0.3.4) +- github.com/googleapis/gax-go/v2: [v2.14.1](https://github.com/googleapis/gax-go/tree/v2.14.1) +- github.com/grpc-ecosystem/go-grpc-middleware: [v1.3.0](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/v1.3.0) +- github.com/grpc-ecosystem/grpc-gateway: [v1.16.0](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.16.0) +- github.com/kr/fs: [v0.1.0](https://github.com/kr/fs/tree/v0.1.0) +- github.com/pkg/sftp: [v1.13.7](https://github.com/pkg/sftp/tree/v1.13.7) +- go.etcd.io/etcd/client/v2: v2.305.21 +- go.etcd.io/etcd/raft/v3: v3.5.21 +- go.opencensus.io: v0.24.0 +- go.uber.org/atomic: v1.9.0 +- google.golang.org/api: v0.215.0 +- google.golang.org/genproto: e639e21 + +
+
+_Thanks to all our contributors!_ 😊 diff --git a/templates/cluster-template-development.yaml b/templates/cluster-template-topology.yaml similarity index 86% rename from templates/cluster-template-development.yaml rename to templates/cluster-template-topology.yaml index df62668d3..d83ca03ea 100644 --- a/templates/cluster-template-development.yaml +++ b/templates/cluster-template-topology.yaml @@ -1,25 +1,20 @@ -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: Cluster metadata: name: ${CLUSTER_NAME} spec: topology: - class: dev-test - version: ${KUBERNETES_VERSION} + classRef: + name: dev-test controlPlane: replicas: ${CONTROL_PLANE_MACHINE_COUNT} - workers: - machineDeployments: - - class: default-worker - name: md-0 - replicas: ${WORKER_MACHINE_COUNT} variables: - name: identityRef value: - name: ${CLOUD_CONFIG_SECRET:=dev-test-cloud-config} cloudName: ${OPENSTACK_CLOUD:=capo-e2e} - - name: imageName - value: ${IMAGE_NAME:=flatcar_production} + name: ${CLOUD_CONFIG_SECRET:=dev-test-cloud-config} + - name: imageRef + value: ${IMAGE_REF:=node-image} - name: addImageVersion value: ${ADD_IMAGE_VERSION:=false} - name: injectIgnitionSysext @@ -32,6 +27,12 @@ spec: spec: flavor: ${OPENSTACK_BASTION_FLAVOR:=m1.small} image: - filter: + imageRef: name: ${OPENSTACK_BASTION_IMAGE_NAME:=ubuntu-24.04} sshKeyName: ${OPENSTACK_SSH_KEY_NAME:=""} + version: ${KUBERNETES_VERSION} + workers: + machineDeployments: + - class: default-worker + name: md-0 + replicas: ${WORKER_MACHINE_COUNT} diff --git a/templates/clusterclass-dev-test.yaml b/templates/clusterclass-dev-test.yaml index eff4f62ae..220d4b066 100644 --- a/templates/clusterclass-dev-test.yaml +++ b/templates/clusterclass-dev-test.yaml @@ -5,7 +5,7 @@ metadata: spec: controlPlane: templateRef: - apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + apiVersion: controlplane.cluster.x-k8s.io/v1beta2 kind: KubeadmControlPlaneTemplate name: dev-test-control-plane machineInfrastructure: @@ -23,7 +23,7 @@ spec: - class: default-worker bootstrap: templateRef: - apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + apiVersion: bootstrap.cluster.x-k8s.io/v1beta2 kind: KubeadmConfigTemplate name: dev-test-default-worker-bootstraptemplate infrastructure: @@ -53,16 +53,22 @@ spec: type: string description: | The base name of the OpenStack image that is used for creating the servers. - This will be combined with the k8s version to create the full name. E.g. imageName-v1.31.2. - default: "ubuntu-2404-kube" - name: addImageVersion required: false schema: openAPIV3Schema: type: boolean description: | - Add a suffix with the Kubernetes version to the imageName. E.g. imageName-v1.32.2. + Add a suffix with the Kubernetes version to the imageName or imageRef. E.g. imageName-v1.32.2. default: true + - name: imageRef + required: false + schema: + openAPIV3Schema: + type: string + description: | + The name of the ORC Image object that is used for creating the servers. + default: "node-image" - name: injectIgnitionSysext required: false schema: @@ -109,6 +115,12 @@ spec: name: type: string description: "Name of the image to use for bastion" + imageRef: + type: object + properties: + name: + type: string + description: "Name of the ORC Image to use for bastion" sshKeyName: type: string description: "SSH key pair name for bastion access" @@ -120,8 +132,9 @@ spec: type: string description: "Availability zone for the bastion host" patches: - - name: image - description: "Sets the OpenStack image that is used for creating the servers." + - name: imageName + description: "Sets the OpenStack image that is used for creating the servers using a name filter." + enabledIf: "{{ if .imageName }}true{{ end }}" definitions: - selector: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 @@ -129,11 +142,13 @@ spec: matchResources: controlPlane: true jsonPatches: + - op: remove + path: /spec/template/spec/image/imageRef - op: add - path: /spec/template/spec/image/filter/name + path: /spec/template/spec/image/filter valueFrom: template: | - {{ .imageName }}{{ if .addImageVersion }}-{{ .builtin.controlPlane.version }}{{ end }} + name: {{ .imageName }}{{ if .addImageVersion }}-{{ .builtin.controlPlane.version }}{{ end }} - selector: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackMachineTemplate @@ -142,11 +157,41 @@ spec: names: - default-worker jsonPatches: + - op: remove + path: /spec/template/spec/image/imageRef - op: add - path: /spec/template/spec/image/filter/name + path: /spec/template/spec/image/filter valueFrom: template: | - {{ .imageName }}{{ if .addImageVersion }}-{{ .builtin.machineDeployment.version }}{{ end }} + name: {{ .imageName }}{{ if .addImageVersion }}-{{ .builtin.machineDeployment.version }}{{ end }} + - name: imageRef + description: "Sets the OpenStack image that is used for creating the servers using an ORC Image reference." + enabledIf: "{{ if .imageRef }}true{{ end }}" + definitions: + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + controlPlane: true + jsonPatches: + - op: replace + path: /spec/template/spec/image/imageRef/name + valueFrom: + template: | + {{ .imageRef }}{{ if .addImageVersion }}-{{ .builtin.controlPlane.version }}{{ end }} + - selector: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + kind: OpenStackMachineTemplate + matchResources: + machineDeploymentClass: + names: + - default-worker + jsonPatches: + - op: replace + path: /spec/template/spec/image/imageRef/name + valueFrom: + template: | + {{ .imageRef }}{{ if .addImageVersion }}-{{ .builtin.controlPlane.version }}{{ end }} - name: identityRef description: "Sets the OpenStack identity reference." definitions: @@ -193,7 +238,7 @@ spec: enabledIf: "{{ .injectIgnitionSysext }}" definitions: - selector: - apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + apiVersion: controlplane.cluster.x-k8s.io/v1beta2 kind: KubeadmControlPlaneTemplate matchResources: controlPlane: true @@ -211,12 +256,18 @@ spec: nodeRegistration: name: $${COREOS_OPENSTACK_HOSTNAME} kubeletExtraArgs: - provider-id: openstack:///$${COREOS_OPENSTACK_INSTANCE_UUID} + - name: cloud-provider + value: external + - name: provider-id + value: openstack:///$${COREOS_OPENSTACK_INSTANCE_UUID} joinConfiguration: nodeRegistration: name: $${COREOS_OPENSTACK_HOSTNAME} kubeletExtraArgs: - provider-id: openstack:///$${COREOS_OPENSTACK_INSTANCE_UUID} + - name: cloud-provider + value: external + - name: provider-id + value: openstack:///$${COREOS_OPENSTACK_INSTANCE_UUID} format: ignition ignition: containerLinuxConfig: @@ -233,16 +284,16 @@ spec: mode: 0644 contents: remote: - url: https://github.com/flatcar/sysext-bakery/releases/download/latest/kubernetes-{{ $minor }}.conf + url: https://extensions.flatcar.org/extensions/kubernetes/kubernetes-{{ $minor }}.conf - path: /etc/sysupdate.d/noop.conf mode: 0644 contents: remote: - url: https://github.com/flatcar/sysext-bakery/releases/download/latest/noop.conf + url: https://extensions.flatcar.org/extensions/noop.conf - path: /opt/extensions/kubernetes/kubernetes-{{ .builtin.controlPlane.version }}-x86-64.raw contents: remote: - url: https://github.com/flatcar/sysext-bakery/releases/download/latest/kubernetes-{{ .builtin.controlPlane.version }}-x86-64.raw + url: https://extensions.flatcar.org/extensions/kubernetes-{{ .builtin.controlPlane.version }}-x86-64.raw systemd: units: - name: systemd-sysupdate.service @@ -277,7 +328,7 @@ spec: [Service] EnvironmentFile=/run/metadata/flatcar - selector: - apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + apiVersion: bootstrap.cluster.x-k8s.io/v1beta2 kind: KubeadmConfigTemplate matchResources: machineDeploymentClass: @@ -297,7 +348,10 @@ spec: nodeRegistration: name: $${COREOS_OPENSTACK_HOSTNAME} kubeletExtraArgs: - provider-id: openstack:///$${COREOS_OPENSTACK_INSTANCE_UUID} + - name: cloud-provider + value: external + - name: provider-id + value: openstack:///$${COREOS_OPENSTACK_INSTANCE_UUID} format: ignition ignition: containerLinuxConfig: @@ -314,16 +368,16 @@ spec: mode: 0644 contents: remote: - url: https://github.com/flatcar/sysext-bakery/releases/download/latest/kubernetes-{{ $minor }}.conf + url: https://extensions.flatcar.org/extensions/kubernetes/kubernetes-{{ $minor }}.conf - path: /etc/sysupdate.d/noop.conf mode: 0644 contents: remote: - url: https://github.com/flatcar/sysext-bakery/releases/download/latest/noop.conf + url: https://extensions.flatcar.org/extensions/noop.conf - path: /opt/extensions/kubernetes/kubernetes-{{ .builtin.machineDeployment.version }}-x86-64.raw contents: remote: - url: https://github.com/flatcar/sysext-bakery/releases/download/latest/kubernetes-{{ .builtin.machineDeployment.version }}-x86-64.raw + url: https://extensions.flatcar.org/extensions/kubernetes-{{ .builtin.machineDeployment.version }}-x86-64.raw systemd: units: - name: systemd-sysupdate.service @@ -365,7 +419,6 @@ metadata: spec: template: spec: - files: [] joinConfiguration: nodeRegistration: kubeletExtraArgs: @@ -382,6 +435,11 @@ metadata: spec: template: spec: + rollout: + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 kubeadmConfigSpec: clusterConfiguration: controllerManager: @@ -454,8 +512,8 @@ spec: spec: flavor: ${OPENSTACK_CONTROL_PLANE_MACHINE_FLAVOR:=m1.medium} image: - filter: - name: overridden-by-patch + imageRef: + name: node-image sshKeyName: ${OPENSTACK_SSH_KEY_NAME:=""} --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 @@ -467,6 +525,6 @@ spec: spec: flavor: ${OPENSTACK_NODE_MACHINE_FLAVOR:=m1.small} image: - filter: - name: overridden-by-patch + imageRef: + name: node-image sshKeyName: ${OPENSTACK_SSH_KEY_NAME:=""} diff --git a/test/e2e/data/e2e_conf.yaml b/test/e2e/data/e2e_conf.yaml index 9914f0eb0..ba2b92dc7 100644 --- a/test/e2e/data/e2e_conf.yaml +++ b/test/e2e/data/e2e_conf.yaml @@ -150,9 +150,10 @@ providers: - name: "{go://github.com/kubernetes-sigs/cluster-api-provider-openstack@v0.13}" value: "https://github.com/kubernetes-sigs/cluster-api-provider-openstack/releases/download/{go://github.com/kubernetes-sigs/cluster-api-provider-openstack@v0.13}/infrastructure-components.yaml" type: url + # NOTE: v0.13.0 and v0.13.1 were mistakenly released with contract v1beta2. contract: v1beta2 files: - - sourcePath: "../data/shared/provider/metadata.yaml" + - sourcePath: "../data/shared/provider/errata/metadata.yaml" - sourcePath: "./infrastructure-openstack-no-artifact/cluster-template.yaml" replacements: - old: "imagePullPolicy: Always" @@ -164,13 +165,13 @@ providers: - name: v0.13.99 value: ../../../config/default # This is the upcoming version. - # Specify no contract so that upgrade tests that start from a specific contract won't pick it up. - # contract: v1beta2 + contract: v1beta1 files: - sourcePath: "../data/shared/provider/metadata.yaml" - sourcePath: "./infrastructure-openstack-no-artifact/cluster-template.yaml" - sourcePath: "./infrastructure-openstack-no-artifact/cluster-template-without-lb.yaml" - sourcePath: "./infrastructure-openstack-no-artifact/cluster-template-cluster-identity.yaml" + - sourcePath: "../../../templates/clusterclass-dev-test.yaml" replacements: - old: gcr.io/k8s-staging-capi-openstack/capi-openstack-controller:dev new: gcr.io/k8s-staging-capi-openstack/capi-openstack-controller:e2e @@ -207,9 +208,11 @@ providers: variables: # used to ensure we deploy to the correct management cluster KUBE_CONTEXT: "kind-capo-e2e" - KUBERNETES_VERSION: "v1.33.1" - KUBERNETES_VERSION_UPGRADE_FROM: "v1.32.5" - KUBERNETES_VERSION_UPGRADE_TO: "v1.33.1" + # Pick a version that has a kind node image available. This is used in clusterctl upgrade tests. + KUBERNETES_KIND_VERSION: "v1.34.0" + KUBERNETES_VERSION: "v1.34.2" + KUBERNETES_VERSION_UPGRADE_FROM: "v1.33.1" + KUBERNETES_VERSION_UPGRADE_TO: "v1.34.2" # NOTE: To see default images run kubeadm config images list (optionally with --kubernetes-version=vX.Y.Z) ETCD_VERSION_UPGRADE_TO: "3.5.21-0" COREDNS_VERSION_UPGRADE_TO: "v1.12.0" @@ -217,7 +220,8 @@ variables: WORKERS_MACHINE_TEMPLATE_UPGRADE_TO: "upgrade-to-md-0" CNI: "../../data/cni/calico.yaml" CCM: "../../data/ccm/cloud-controller-manager.yaml" - EXP_CLUSTER_RESOURCE_SET: "true" + EXP_CAPO_PRIORITY_QUEUE: "false" + IP_FAMILY: "ipv4" OPENSTACK_BASTION_IMAGE_NAME: "cirros-0.6.1-x86_64-disk" OPENSTACK_BASTION_IMAGE_URL: https://storage.googleapis.com/artifacts.k8s-staging-capi-openstack.appspot.com/test/cirros/2022-12-05/cirros-0.6.1-x86_64-disk.img OPENSTACK_BASTION_IMAGE_HASH: 0c839612eb3f2469420f2ccae990827f @@ -232,10 +236,10 @@ variables: OPENSTACK_DNS_NAMESERVERS: "8.8.8.8" OPENSTACK_FAILURE_DOMAIN: "testaz1" OPENSTACK_FAILURE_DOMAIN_ALT: "testaz2" - OPENSTACK_IMAGE_NAME: "ubuntu-2404-kube-v1.33.1" - OPENSTACK_IMAGE_URL: https://storage.googleapis.com/artifacts.k8s-staging-capi-openstack.appspot.com/test/ubuntu/ubuntu-2404-kube-v1.33.1 - OPENSTACK_IMAGE_NAME_UPGRADE_FROM: "ubuntu-2404-kube-v1.32.5" - OPENSTACK_IMAGE_URL_UPGRADE_FROM: https://storage.googleapis.com/artifacts.k8s-staging-capi-openstack.appspot.com/test/ubuntu/ubuntu-2404-kube-v1.32.5 + OPENSTACK_IMAGE_NAME: "ubuntu-2404-kube-latest" + OPENSTACK_IMAGE_URL: https://storage.googleapis.com/artifacts.k8s-staging-capi-openstack.appspot.com/test/ubuntu/ubuntu-2404-kube-v1.34.2 + OPENSTACK_IMAGE_NAME_UPGRADE_FROM: "ubuntu-2404-kube-previous" + OPENSTACK_IMAGE_URL_UPGRADE_FROM: https://storage.googleapis.com/artifacts.k8s-staging-capi-openstack.appspot.com/test/ubuntu/ubuntu-2404-kube-v1.33.1 OPENSTACK_NODE_MACHINE_FLAVOR: "m1.small" OPENSTACK_SSH_KEY_NAME: "cluster-api-provider-openstack-sigs-k8s-io" # The default external network created by devstack @@ -246,10 +250,11 @@ variables: E2E_IMAGE_URL: "http://10.0.3.15/capo-e2e-image.tar" # The default user for SSH connections from bastion to machines SSH_USER_MACHINE: "ubuntu" + CLUSTER_TOPOLOGY: "true" EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION: "true" # The Flatcar image produced by the image-builder - OPENSTACK_FLATCAR_IMAGE_NAME: "flatcar-stable-4152.2.3-kube-v1.33.1" - OPENSTACK_FLATCAR_IMAGE_URL: "https://storage.googleapis.com/artifacts.k8s-staging-capi-openstack.appspot.com/test/flatcar/flatcar-stable-4152.2.3-kube-v1.33.1" + OPENSTACK_FLATCAR_IMAGE_NAME: "flatcar-stable" + OPENSTACK_FLATCAR_IMAGE_URL: "https://storage.googleapis.com/artifacts.k8s-staging-capi-openstack.appspot.com/test/flatcar/flatcar-stable-4459.2.0-kube-v1.34.2" # A plain Flatcar from the Flatcar releases server FLATCAR_IMAGE_NAME: "flatcar_production_openstack_image" FLATCAR_IMAGE_URL: https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_openstack_image.img diff --git a/test/e2e/data/kustomize/components/common/kustomization.yaml b/test/e2e/data/kustomize/components/common/kustomization.yaml index 211dc0e4d..61582df59 100644 --- a/test/e2e/data/kustomize/components/common/kustomization.yaml +++ b/test/e2e/data/kustomize/components/common/kustomization.yaml @@ -1,7 +1,7 @@ apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component -resources: -- images.yaml +components: +- ../images patches: # Set AZ and enable bastion - target: diff --git a/test/e2e/data/kustomize/components/common/images.yaml b/test/e2e/data/kustomize/components/images/images.yaml similarity index 100% rename from test/e2e/data/kustomize/components/common/images.yaml rename to test/e2e/data/kustomize/components/images/images.yaml diff --git a/test/e2e/data/kustomize/components/images/kustomization.yaml b/test/e2e/data/kustomize/components/images/kustomization.yaml new file mode 100644 index 000000000..daa515fd8 --- /dev/null +++ b/test/e2e/data/kustomize/components/images/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component +resources: +- images.yaml diff --git a/test/e2e/data/kustomize/components/upgrade-patches/ci-hack-kcp.yaml b/test/e2e/data/kustomize/components/self-hosted/ci-hack-kcp.yaml similarity index 100% rename from test/e2e/data/kustomize/components/upgrade-patches/ci-hack-kcp.yaml rename to test/e2e/data/kustomize/components/self-hosted/ci-hack-kcp.yaml diff --git a/test/e2e/data/kustomize/components/upgrade-patches/ci-hack-kct.yaml b/test/e2e/data/kustomize/components/self-hosted/ci-hack-kct.yaml similarity index 100% rename from test/e2e/data/kustomize/components/upgrade-patches/ci-hack-kct.yaml rename to test/e2e/data/kustomize/components/self-hosted/ci-hack-kct.yaml diff --git a/test/e2e/data/kustomize/components/self-hosted/kustomization.yaml b/test/e2e/data/kustomize/components/self-hosted/kustomization.yaml new file mode 100644 index 000000000..fe2d5cbf3 --- /dev/null +++ b/test/e2e/data/kustomize/components/self-hosted/kustomization.yaml @@ -0,0 +1,17 @@ +# Modifications to release templates for self-hosted scenarios. +# This patch allows the nodes dowload the CAPO image from E2E_IMAGE_URL, +# and load it. It is useful when testing unpublished images (e.g. a PR). +# The image is downloaded only if DOWNLOAD_E2E_IMAGE is set to true +# when rendering the cluster template. +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: +- path: ci-hack-kcp.yaml + target: + kind: KubeadmControlPlane + name: \${CLUSTER_NAME}-control-plane +- path: ci-hack-kct.yaml + target: + kind: KubeadmConfigTemplate + name: \${CLUSTER_NAME}-md-0 diff --git a/test/e2e/data/kustomize/components/upgrade-patches/kustomization.yaml b/test/e2e/data/kustomize/components/upgrade-patches/kustomization.yaml deleted file mode 100644 index 678a6a6d7..000000000 --- a/test/e2e/data/kustomize/components/upgrade-patches/kustomization.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Modifications to release templates for clusterctl upgrade scenarios -apiVersion: kustomize.config.k8s.io/v1alpha1 -kind: Component - -patches: -- path: ci-hack-kcp.yaml - target: - kind: KubeadmControlPlane - name: \${CLUSTER_NAME}-control-plane -- path: ci-hack-kct.yaml - target: - kind: KubeadmConfigTemplate - name: \${CLUSTER_NAME}-md-0 diff --git a/test/e2e/data/kustomize/default/kustomization.yaml b/test/e2e/data/kustomize/default/kustomization.yaml index f2d25440f..544a9b7a5 100644 --- a/test/e2e/data/kustomize/default/kustomization.yaml +++ b/test/e2e/data/kustomize/default/kustomization.yaml @@ -7,4 +7,4 @@ resources: components: - ../components/cluster-resource-sets - ../components/common -- ../components/upgrade-patches +- ../components/self-hosted diff --git a/test/e2e/data/kustomize/multi-az/patch-machine-template-worker.yaml b/test/e2e/data/kustomize/multi-az/patch-machine-template-worker.yaml index 6d46c42e3..d4c357c51 100644 --- a/test/e2e/data/kustomize/multi-az/patch-machine-template-worker.yaml +++ b/test/e2e/data/kustomize/multi-az/patch-machine-template-worker.yaml @@ -16,8 +16,8 @@ volume: type: ${OPENSTACK_VOLUME_TYPE_ALT} availabilityZone: - name: ${OPENSTACK_FAILURE_DOMAIN} + name: ${OPENSTACK_FAILURE_DOMAIN} - name: etcd sizeGiB: 1 storage: - type: Local \ No newline at end of file + type: Local diff --git a/test/e2e/data/kustomize/topology/cluster.yaml b/test/e2e/data/kustomize/topology/cluster.yaml new file mode 100644 index 000000000..477715f26 --- /dev/null +++ b/test/e2e/data/kustomize/topology/cluster.yaml @@ -0,0 +1,40 @@ +# Rather than editing the cluster-template, which include already variables, +# we simply create a new cluster-template with the correct variables for e2e. +apiVersion: cluster.x-k8s.io/v1beta2 +kind: Cluster +metadata: + name: ${CLUSTER_NAME} +spec: + topology: + classRef: + name: dev-test + version: ${KUBERNETES_VERSION} + controlPlane: + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + workers: + machineDeployments: + - class: default-worker + name: md-0 + replicas: ${WORKER_MACHINE_COUNT} + variables: + - name: identityRef + value: + name: ${CLUSTER_NAME}-cloud-config + cloudName: ${OPENSTACK_CLOUD} + - name: imageRef + value: node-image + - name: addImageVersion + value: false + - name: injectIgnitionSysext + value: true + - name: allowedCIDRs + value: ${OPENSTACK_API_SERVER_ALLOWED_CIDRS:=[]} + - name: bastion + value: + enabled: ${OPENSTACK_BASTION_ENABLED:=false} + spec: + flavor: ${OPENSTACK_BASTION_FLAVOR:=m1.small} + image: + imageRef: + name: bastion-image + sshKeyName: ${OPENSTACK_SSH_KEY_NAME:=""} diff --git a/test/e2e/data/kustomize/topology/kustomization.yaml b/test/e2e/data/kustomize/topology/kustomization.yaml new file mode 100644 index 000000000..b2ad517cd --- /dev/null +++ b/test/e2e/data/kustomize/topology/kustomization.yaml @@ -0,0 +1,24 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- cluster.yaml +- secret.yaml + +components: +- ../components/images +- ../components/cluster-resource-sets + +patches: +- target: + group: openstack.k-orc.cloud + version: v1alpha1 + kind: Image + name: node-image + patch: |- + - op: replace + path: /spec/resource/name + value: ${FLATCAR_IMAGE_NAME} + - op: replace + path: /spec/resource/content/download/url + value: ${FLATCAR_IMAGE_URL} diff --git a/test/e2e/data/kustomize/topology/secret.yaml b/test/e2e/data/kustomize/topology/secret.yaml new file mode 100644 index 000000000..2d6641ada --- /dev/null +++ b/test/e2e/data/kustomize/topology/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +data: + cacert: ${OPENSTACK_CLOUD_CACERT_B64} + clouds.yaml: ${OPENSTACK_CLOUD_YAML_B64} +kind: Secret +metadata: + labels: + clusterctl.cluster.x-k8s.io/move: "true" + name: ${CLUSTER_NAME}-cloud-config diff --git a/test/e2e/data/shared/provider/errata/metadata.yaml b/test/e2e/data/shared/provider/errata/metadata.yaml new file mode 100644 index 000000000..9d59337bb --- /dev/null +++ b/test/e2e/data/shared/provider/errata/metadata.yaml @@ -0,0 +1,30 @@ +apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3 +kind: Metadata +releaseSeries: +- major: 0 + minor: 5 + contract: v1beta1 +- major: 0 + minor: 6 + contract: v1beta1 +- major: 0 + minor: 7 + contract: v1beta1 +- major: 0 + minor: 8 + contract: v1beta1 +- major: 0 + minor: 9 + contract: v1beta1 +- major: 0 + minor: 10 + contract: v1beta1 +- major: 0 + minor: 11 + contract: v1beta1 +- major: 0 + minor: 12 + contract: v1beta1 +- major: 0 + minor: 13 + contract: v1beta2 diff --git a/test/e2e/data/shared/provider/metadata.yaml b/test/e2e/data/shared/provider/metadata.yaml index 9d59337bb..5a950bf39 100644 --- a/test/e2e/data/shared/provider/metadata.yaml +++ b/test/e2e/data/shared/provider/metadata.yaml @@ -27,4 +27,4 @@ releaseSeries: contract: v1beta1 - major: 0 minor: 13 - contract: v1beta2 + contract: v1beta1 diff --git a/test/e2e/shared/cluster.go b/test/e2e/shared/cluster.go index 6373eefd5..8c9b0c9d4 100644 --- a/test/e2e/shared/cluster.go +++ b/test/e2e/shared/cluster.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "context" diff --git a/test/e2e/shared/common.go b/test/e2e/shared/common.go index 791e440fb..2e485955c 100644 --- a/test/e2e/shared/common.go +++ b/test/e2e/shared/common.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "context" @@ -169,7 +168,8 @@ func getOpenStackClusterFromMachine(ctx context.Context, client client.Client, m } key = types.NamespacedName{ - Name: cluster.Spec.InfrastructureRef.Name, + Name: cluster.Spec.InfrastructureRef.Name, + Namespace: cluster.Namespace, } openStackCluster := &infrav1.OpenStackCluster{} err = client.Get(ctx, key, openStackCluster) diff --git a/test/e2e/shared/context.go b/test/e2e/shared/context.go index 4711def1a..113726cc7 100644 --- a/test/e2e/shared/context.go +++ b/test/e2e/shared/context.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "context" diff --git a/test/e2e/shared/defaults.go b/test/e2e/shared/defaults.go index e14af4892..be83db089 100644 --- a/test/e2e/shared/defaults.go +++ b/test/e2e/shared/defaults.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "errors" @@ -37,6 +36,7 @@ import ( const ( DefaultSSHKeyPairName = "cluster-api-provider-openstack-sigs-k8s-io" KubeContext = "KUBE_CONTEXT" + KubernetesKindVersion = "KUBERNETES_KIND_VERSION" KubernetesVersion = "KUBERNETES_VERSION" CCMPath = "CCM" CCMResources = "CCM_RESOURCES" @@ -52,6 +52,7 @@ const ( OpenStackNodeMachineFlavor = "OPENSTACK_NODE_MACHINE_FLAVOR" SSHUserMachine = "SSH_USER_MACHINE" FlavorDefault = "" + FlavorTopology = "topology" FlavorNoBastion = "no-bastion" FlavorWithoutLB = "without-lb" FlavorMultiNetwork = "multi-network" diff --git a/test/e2e/shared/exec.go b/test/e2e/shared/exec.go index e26f9e3fd..158fcd013 100644 --- a/test/e2e/shared/exec.go +++ b/test/e2e/shared/exec.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "bytes" diff --git a/test/e2e/shared/exec_test.go b/test/e2e/shared/exec_test.go index 2e71b57b0..1768b0de2 100644 --- a/test/e2e/shared/exec_test.go +++ b/test/e2e/shared/exec_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "context" diff --git a/test/e2e/shared/images.go b/test/e2e/shared/images.go index 110412384..b5ffe0275 100644 --- a/test/e2e/shared/images.go +++ b/test/e2e/shared/images.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2024 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "context" diff --git a/test/e2e/shared/openstack.go b/test/e2e/shared/openstack.go index 7c7128b76..5ceecd1d6 100644 --- a/test/e2e/shared/openstack.go +++ b/test/e2e/shared/openstack.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "bytes" diff --git a/test/e2e/shared/openstack_test.go b/test/e2e/shared/openstack_test.go index 1f1703aa0..2df894679 100644 --- a/test/e2e/shared/openstack_test.go +++ b/test/e2e/shared/openstack_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "context" diff --git a/test/e2e/shared/suite.go b/test/e2e/shared/suite.go index 5d48d9501..2800273e7 100644 --- a/test/e2e/shared/suite.go +++ b/test/e2e/shared/suite.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -17,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shared +package e2eshared import ( "context" diff --git a/test/e2e/suites/conformance/conformance_suite_test.go b/test/e2e/suites/conformance/conformance_suite_test.go index 3971c55fa..216806db0 100644 --- a/test/e2e/suites/conformance/conformance_suite_test.go +++ b/test/e2e/suites/conformance/conformance_suite_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -27,7 +26,7 @@ import ( . "github.com/onsi/gomega" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" + shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" ) var e2eCtx *shared.E2EContext diff --git a/test/e2e/suites/conformance/conformance_test.go b/test/e2e/suites/conformance/conformance_test.go index 6f218349b..a2f3300bb 100644 --- a/test/e2e/suites/conformance/conformance_test.go +++ b/test/e2e/suites/conformance/conformance_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -34,7 +33,7 @@ import ( "sigs.k8s.io/cluster-api/test/framework/kubernetesversions" "sigs.k8s.io/cluster-api/test/framework/kubetest" - "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" + shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" ) var _ = Describe("conformance tests", func() { diff --git a/test/e2e/suites/e2e/clusterctl_upgrade_test.go b/test/e2e/suites/e2e/clusterctl_upgrade_test.go index 1af541d48..2942988e2 100644 --- a/test/e2e/suites/e2e/clusterctl_upgrade_test.go +++ b/test/e2e/suites/e2e/clusterctl_upgrade_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -27,7 +26,7 @@ import ( capi_e2e "sigs.k8s.io/cluster-api/test/e2e" "sigs.k8s.io/cluster-api/test/framework/clusterctl" - "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" + shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" ) var ( @@ -40,7 +39,6 @@ var ( var _ = Describe("When testing clusterctl upgrades for CAPO (v0.11=>current) and ORC (v1.0.2=>current) [clusterctl-upgrade]", func() { BeforeEach(func(ctx context.Context) { - setDownloadE2EImageEnvVar() // Note: This gives the version without the 'v' prefix, so we need to add it below. capoRelease011, err = clusterctl.ResolveRelease(ctx, "go://github.com/kubernetes-sigs/cluster-api-provider-openstack@v0.11") Expect(err).ToNot(HaveOccurred(), "failed to get stable release of CAPO") @@ -66,15 +64,15 @@ var _ = Describe("When testing clusterctl upgrades for CAPO (v0.11=>current) and InitWithControlPlaneProviders: []string{"kubeadm:" + capiRelease110}, MgmtFlavor: shared.FlavorDefault, WorkloadFlavor: shared.FlavorCapiV1Beta1, - InitWithKubernetesVersion: e2eCtx.E2EConfig.MustGetVariable(shared.KubernetesVersion), + InitWithKubernetesVersion: e2eCtx.E2EConfig.MustGetVariable(shared.KubernetesKindVersion), InitWithRuntimeExtensionProviders: []string{"openstack-resource-controller:v1.0.2"}, + UseKindForManagementCluster: true, } }) }) var _ = Describe("When testing clusterctl upgrades for CAPO (v0.12=>current) and ORC (v1.0.2=>current)[clusterctl-upgrade]", func() { BeforeEach(func(ctx context.Context) { - setDownloadE2EImageEnvVar() // Note: This gives the version without the 'v' prefix, so we need to add it below. capoRelease012, err = clusterctl.ResolveRelease(ctx, "go://github.com/kubernetes-sigs/cluster-api-provider-openstack@v0.12") Expect(err).ToNot(HaveOccurred(), "failed to get stable release of CAPO") @@ -100,15 +98,15 @@ var _ = Describe("When testing clusterctl upgrades for CAPO (v0.12=>current) and InitWithControlPlaneProviders: []string{"kubeadm:" + capiRelease110}, MgmtFlavor: shared.FlavorDefault, WorkloadFlavor: shared.FlavorCapiV1Beta1, - InitWithKubernetesVersion: e2eCtx.E2EConfig.MustGetVariable(shared.KubernetesVersion), + InitWithKubernetesVersion: e2eCtx.E2EConfig.MustGetVariable(shared.KubernetesKindVersion), InitWithRuntimeExtensionProviders: []string{"openstack-resource-controller:v1.0.2"}, + UseKindForManagementCluster: true, } }) }) var _ = Describe("When testing clusterctl upgrades for CAPO (v0.13=>current) and ORC (v1.0.2=>current)[clusterctl-upgrade]", func() { BeforeEach(func(ctx context.Context) { - setDownloadE2EImageEnvVar() // Note: This gives the version without the 'v' prefix, so we need to add it below. capoRelease013, err = clusterctl.ResolveRelease(ctx, "go://github.com/kubernetes-sigs/cluster-api-provider-openstack@v0.13") Expect(err).ToNot(HaveOccurred(), "failed to get stable release of CAPO") @@ -134,8 +132,9 @@ var _ = Describe("When testing clusterctl upgrades for CAPO (v0.13=>current) and InitWithControlPlaneProviders: []string{"kubeadm:" + capiRelease111}, MgmtFlavor: shared.FlavorDefault, WorkloadFlavor: shared.FlavorDefault, - InitWithKubernetesVersion: e2eCtx.E2EConfig.MustGetVariable(shared.KubernetesVersion), + InitWithKubernetesVersion: e2eCtx.E2EConfig.MustGetVariable(shared.KubernetesKindVersion), InitWithRuntimeExtensionProviders: []string{"openstack-resource-controller:v1.0.2"}, + UseKindForManagementCluster: true, } }) }) diff --git a/test/e2e/suites/e2e/e2e_suite_test.go b/test/e2e/suites/e2e/e2e_suite_test.go index a4fb17dad..b93db77e3 100644 --- a/test/e2e/suites/e2e/e2e_suite_test.go +++ b/test/e2e/suites/e2e/e2e_suite_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -35,7 +34,7 @@ import ( . "github.com/onsi/gomega" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" + shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" ) var ( diff --git a/test/e2e/suites/e2e/e2e_test.go b/test/e2e/suites/e2e/e2e_test.go index 3fbdd4862..105c0d4f7 100644 --- a/test/e2e/suites/e2e/e2e_test.go +++ b/test/e2e/suites/e2e/e2e_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -61,7 +60,7 @@ import ( infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1" "sigs.k8s.io/cluster-api-provider-openstack/internal/util/ssa" "sigs.k8s.io/cluster-api-provider-openstack/pkg/generated/applyconfiguration/api/v1beta1" - "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" + shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" ) const specName = "e2e" diff --git a/test/e2e/suites/e2e/quick_start_test.go b/test/e2e/suites/e2e/quick_start_test.go new file mode 100644 index 000000000..3d2491c7a --- /dev/null +++ b/test/e2e/suites/e2e/quick_start_test.go @@ -0,0 +1,42 @@ +//go:build e2e + +/* +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 e2e + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + "k8s.io/utils/ptr" + capi_e2e "sigs.k8s.io/cluster-api/test/e2e" + + shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" +) + +var _ = Describe("When following the Cluster API quick-start with ClusterClass [PR-Blocking] [ClusterClass]", func() { + capi_e2e.QuickStartSpec(context.TODO(), func() capi_e2e.QuickStartSpecInput { + return capi_e2e.QuickStartSpecInput{ + E2EConfig: e2eCtx.E2EConfig, + ClusterctlConfigPath: e2eCtx.Environment.ClusterctlConfigPath, + BootstrapClusterProxy: e2eCtx.Environment.BootstrapClusterProxy, + ArtifactFolder: e2eCtx.Settings.ArtifactFolder, + SkipCleanup: false, + Flavor: ptr.To(shared.FlavorTopology), + } + }) +}) diff --git a/test/e2e/suites/e2e/remediations_test.go b/test/e2e/suites/e2e/remediations_test.go index d8df1765d..c4c023143 100644 --- a/test/e2e/suites/e2e/remediations_test.go +++ b/test/e2e/suites/e2e/remediations_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2021 The Kubernetes Authors. @@ -26,7 +25,7 @@ import ( "k8s.io/utils/ptr" capi_e2e "sigs.k8s.io/cluster-api/test/e2e" - "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" + shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" ) var _ = Describe("When testing unhealthy machines remediation", func() { diff --git a/test/e2e/suites/e2e/self_hosted_test.go b/test/e2e/suites/e2e/self_hosted_test.go index 3ad151101..35dba0b08 100644 --- a/test/e2e/suites/e2e/self_hosted_test.go +++ b/test/e2e/suites/e2e/self_hosted_test.go @@ -1,5 +1,4 @@ //go:build e2e -// +build e2e /* Copyright 2022 The Kubernetes Authors. @@ -25,11 +24,13 @@ import ( . "github.com/onsi/ginkgo/v2" capie2e "sigs.k8s.io/cluster-api/test/e2e" - "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" + shared "sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared" ) var _ = Describe("When testing Cluster API provider Openstack working on [self-hosted] clusters", func() { BeforeEach(func() { + // The self-hosted cluster does not have the CAPO image loaded like the kind cluster does, + // so we download it from E2E_IMAGE_URL. setDownloadE2EImageEnvVar() })