Compare commits

..

7 Commits

Author SHA1 Message Date
Kanchan Dhamane
bc15e5b359 Feat: Semantic versioning support for Definitions (#6648)
Some checks failed
CodeQL / Analyze (go) (push) Failing after 1m43s
Definition-Lint / definition-doc (push) Failing after 6m13s
E2E MultiCluster Test / detect-noop (push) Successful in 24s
E2E Test / detect-noop (push) Successful in 17s
Go / detect-noop (push) Successful in 21s
license / Check for unapproved licenses (push) Failing after 2m38s
Registry / publish-core-images (push) Failing after 40s
Unit-Test / detect-noop (push) Successful in 20s
E2E MultiCluster Test / e2e-multi-cluster-tests (v1.29) (push) Failing after 1m55s
E2E Test / e2e-tests (v1.29) (push) Failing after 1m18s
Go / staticcheck (push) Successful in 18m35s
Go / lint (push) Failing after 19m38s
Go / check-diff (push) Failing after 15m7s
Go / check-core-image-build (push) Failing after 3m45s
Go / check-cli-image-build (push) Failing after 2m23s
Unit-Test / unit-tests (push) Failing after 12m43s
Go / check-windows (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Failing after 48s
* feature: Add Semantic versioning to KubeVela Definitions

Fixes https://github.com/kubevela/kubevela/issues/6435
Fixes https://github.com/kubevela/kubevela/issues/6534

Changes:
- Adds an optional "Version" field for all Definition Specs.
- Adds the following new validations to Webhooks for Definitions:
	- Validate the "Version" field follows Semantic versioning.
	- Dis-allow conflicting versioning fields ( Name annotation, Spec.Version)
- Adds the following new validations to Webhooks for Application:
	- Dis-allow the use of both the "publishVersion" & "autoUpdate" annotations.
- Enahnce "multiStageComponentApply" feature to support auto updates.

Boy Scout Changes:
- Fixes Plugin e2e tests broken by the fix for 6534.
- Fixes the dryRun and livediff commands to respect the "-n" namespace flag.
- Fixes the Application ValidationWebhook to respect the "-n" namespace flag.

Co-authored-by: Rahul Kumar <35751394+bugbounce@users.noreply.github.com>
Co-authored-by: Chaitanya Reddy <chaitanyareddy0702@gmail.com>
Co-authored-by: Vibhor Chinda <vibhorchinda@gmail.com>
Co-authored-by: Shivin Gopalani <gopalanishivin@gmail.com>

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>

* feature: Add KEP to define the proposal

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>

* fix: Rebase and fix merge conflicts

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>

* Fix: Adds unit test cases

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>

---------

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>
Co-authored-by: bugbounce <35751394+bugbounce@users.noreply.github.com>
2025-02-03 11:09:28 +08:00
Eko Simanjuntak
d0d7beb700 fix: return error before accesing mapping resource (#6660)
Some checks failed
CodeQL / Analyze (go) (push) Failing after 2m18s
Definition-Lint / definition-doc (push) Failing after 6m5s
E2E MultiCluster Test / detect-noop (push) Successful in 18s
E2E Test / detect-noop (push) Successful in 21s
Go / detect-noop (push) Successful in 19s
license / Check for unapproved licenses (push) Failing after 2m30s
Registry / publish-core-images (push) Failing after 49s
Unit-Test / detect-noop (push) Successful in 18s
E2E MultiCluster Test / e2e-multi-cluster-tests (v1.29) (push) Failing after 1m28s
E2E Test / e2e-tests (v1.29) (push) Failing after 1m17s
Go / staticcheck (push) Successful in 18m20s
Go / lint (push) Failing after 19m38s
Go / check-diff (push) Failing after 15m6s
Go / check-core-image-build (push) Failing after 4m1s
Go / check-cli-image-build (push) Failing after 3m2s
Unit-Test / unit-tests (push) Failing after 13m29s
Go / check-windows (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Failing after 41s
Signed-off-by: Eko Simanjuntak <ecojuntak@gmail.com>
2025-01-25 00:36:13 +05:30
dependabot[bot]
e63d8c33ec Chore: (deps): Bump github.com/go-git/go-git/v5 from 5.8.1 to 5.13.1 (#6668)
Some checks failed
CodeQL / Analyze (go) (push) Failing after 1m30s
Definition-Lint / definition-doc (push) Failing after 6m23s
E2E MultiCluster Test / detect-noop (push) Successful in 22s
E2E Test / detect-noop (push) Successful in 24s
Go / detect-noop (push) Successful in 27s
license / Check for unapproved licenses (push) Failing after 2m46s
Registry / publish-core-images (push) Failing after 1m5s
Scorecards supply-chain security / Scorecards analysis (push) Failing after 46s
Unit-Test / detect-noop (push) Successful in 26s
E2E MultiCluster Test / e2e-multi-cluster-tests (v1.29) (push) Failing after 3m12s
E2E Test / e2e-tests (v1.29) (push) Failing after 1m13s
Go / staticcheck (push) Successful in 19m3s
Go / lint (push) Failing after 19m42s
Go / check-diff (push) Failing after 11m44s
Go / check-core-image-build (push) Failing after 3m8s
Go / check-cli-image-build (push) Failing after 2m32s
Unit-Test / unit-tests (push) Failing after 12m55s
Go / check-windows (push) Has been cancelled
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.8.1 to 5.13.1.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.8.1...v5.13.1)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-22 01:56:32 +05:30
dependabot[bot]
3779f828ae Chore: (deps): Bump docker/setup-buildx-action from 3.0.0 to 3.8.0 (#6678)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.0.0 to 3.8.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](f95db51fdd...6524bf65af)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-22 01:54:37 +05:30
dependabot[bot]
4d744a35d4 Chore: (deps): Bump golang.org/x/crypto from 0.25.0 to 0.32.0 (#6672)
Some checks failed
CodeQL / Analyze (go) (push) Failing after 1m54s
Definition-Lint / definition-doc (push) Failing after 7m11s
E2E MultiCluster Test / detect-noop (push) Successful in 25s
E2E Test / detect-noop (push) Successful in 25s
Go / detect-noop (push) Successful in 21s
license / Check for unapproved licenses (push) Failing after 2m40s
Registry / publish-core-images (push) Failing after 38s
Unit-Test / detect-noop (push) Successful in 30s
E2E MultiCluster Test / e2e-multi-cluster-tests (v1.29) (push) Failing after 3m26s
E2E Test / e2e-tests (v1.29) (push) Failing after 1m13s
Go / staticcheck (push) Successful in 20m3s
Go / lint (push) Failing after 19m36s
Go / check-diff (push) Failing after 15m14s
Go / check-core-image-build (push) Failing after 3m22s
Go / check-cli-image-build (push) Failing after 2m16s
Unit-Test / unit-tests (push) Failing after 12m30s
Go / check-windows (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Failing after 53s
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.25.0 to 0.32.0.
- [Commits](https://github.com/golang/crypto/compare/v0.25.0...v0.32.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 03:09:33 +08:00
Thomas Schuetz
9f09436359 Feat: add securityContext and podSecurityContext traits (#6666)
Some checks failed
Definition-Lint / definition-doc (push) Failing after 1m45s
CodeQL / Analyze (go) (push) Failing after 1m49s
E2E MultiCluster Test / detect-noop (push) Successful in 18s
E2E Test / detect-noop (push) Successful in 26s
Go / detect-noop (push) Successful in 21s
Registry / publish-core-images (push) Failing after 1m24s
license / Check for unapproved licenses (push) Failing after 2m53s
Unit-Test / detect-noop (push) Successful in 26s
E2E MultiCluster Test / e2e-multi-cluster-tests (v1.29) (push) Failing after 1m56s
E2E Test / e2e-tests (v1.29) (push) Failing after 1m24s
Sync SDK / sync_sdk (push) Failing after 23m1s
Go / staticcheck (push) Successful in 19m14s
Go / check-diff (push) Failing after 15m19s
Go / check-core-image-build (push) Failing after 4m25s
Go / lint (push) Failing after 21m4s
Go / check-cli-image-build (push) Failing after 3m26s
Unit-Test / unit-tests (push) Failing after 8m53s
Scorecards supply-chain security / Scorecards analysis (push) Failing after 20s
Go / check-windows (push) Has been cancelled
* feat: add securityContext and podSecurityContext traits

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
Signed-off-by: Thomas Schütz <thomas.schuetz@karriere.at>

* Fix: broken runner config for workglow (#6669)

Signed-off-by: Anoop Gopalakrishnan <anoop2811@aol.in>
Signed-off-by: Thomas Schütz <thomas.schuetz@karriere.at>

* fix: fix spaces at for statements

Signed-off-by: Thomas Schütz <thomas.schuetz@karriere.at>

---------

Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
Signed-off-by: Thomas Schütz <thomas.schuetz@karriere.at>
Signed-off-by: Anoop Gopalakrishnan <anoop2811@aol.in>
Co-authored-by: Anoop Gopalakrishnan <2038273+anoop2811@users.noreply.github.com>
2025-01-10 22:24:22 +05:30
Anoop Gopalakrishnan
c6765c6ff0 Fix: broken runner config for workglow (#6669)
Some checks failed
CodeQL / Analyze (go) (push) Failing after 8m37s
Definition-Lint / definition-doc (push) Failing after 6m9s
E2E Test / detect-noop (push) Successful in 20s
Go / detect-noop (push) Successful in 25s
license / Check for unapproved licenses (push) Failing after 3m7s
E2E MultiCluster Test / detect-noop (push) Failing after 10m51s
Registry / publish-core-images (push) Failing after 1m5s
Scorecards supply-chain security / Scorecards analysis (push) Failing after 4m5s
Unit-Test / detect-noop (push) Successful in 20s
E2E Test / e2e-tests (v1.29) (push) Failing after 12m25s
Go / staticcheck (push) Successful in 18m25s
Go / lint (push) Failing after 20m3s
Go / check-core-image-build (push) Failing after 4m15s
Go / check-cli-image-build (push) Failing after 3m8s
Go / check-diff (push) Failing after 17m20s
Unit-Test / unit-tests (push) Failing after 13m22s
Go / check-windows (push) Has been cancelled
E2E MultiCluster Test / e2e-multi-cluster-tests (v1.29) (push) Has been cancelled
Signed-off-by: Anoop Gopalakrishnan <anoop2811@aol.in>
2025-01-06 13:37:59 +08:00
68 changed files with 3563 additions and 220 deletions

View File

@@ -39,7 +39,7 @@ jobs:
continue-on-error: true
e2e-multi-cluster-tests:
runs-on: self-hosted
runs-on: ubuntu-22.04
needs: [ detect-noop ]
if: needs.detect-noop.outputs.noop != 'true'
strategy:

View File

@@ -39,7 +39,7 @@ jobs:
continue-on-error: true
e2e-tests:
runs-on: self-hosted
runs-on: ubuntu-22.04
needs: [ detect-noop ]
if: needs.detect-noop.outputs.noop != 'true'
strategy:

View File

@@ -176,7 +176,7 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
- name: Build Test for vela core
uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # v6.3.0
with:
@@ -196,7 +196,7 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
- name: Build Test for CLI
uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # v6.3.0
with:

View File

@@ -43,7 +43,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
- uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
with:
driver-opts: image=moby/buildkit:master

View File

@@ -27,6 +27,9 @@ import (
// ComponentDefinitionSpec defines the desired state of ComponentDefinition
type ComponentDefinitionSpec struct {
// +optional
Version string `json:"version,omitempty"`
// Workload is a workload type descriptor
Workload common.WorkloadTypeDescriptor `json:"workload"`

View File

@@ -164,6 +164,9 @@ type TraitDefinitionSpec struct {
// pre-process and post-process respectively.
// +optional
Stage StageType `json:"stage,omitempty"`
// +optional
Version string `json:"version,omitempty"`
}
// StageType describes how the manifests should be dispatched.

View File

@@ -37,6 +37,9 @@ type PolicyDefinitionSpec struct {
// ManageHealthCheck means the policy will handle health checking and skip application controller
// built-in health checking.
ManageHealthCheck bool `json:"manageHealthCheck,omitempty"`
//+optional
Version string `json:"version,omitempty"`
}
// PolicyDefinitionStatus is the status of PolicyDefinition

View File

@@ -33,6 +33,9 @@ type WorkflowStepDefinitionSpec struct {
// Only CUE schematic is supported for now.
// +optional
Schematic *common.Schematic `json:"schematic,omitempty"`
// +optional
Version string `json:"version,omitempty"`
}
// WorkflowStepDefinitionStatus is the status of WorkflowStepDefinition

View File

@@ -1053,6 +1053,8 @@ spec:
for the abstraction
type: string
type: object
version:
type: string
workload:
description: Workload is a workload type descriptor
properties:
@@ -1356,6 +1358,8 @@ spec:
- configuration
type: object
type: object
version:
type: string
type: object
status:
description: PolicyDefinitionStatus is the status of PolicyDefinition
@@ -1651,6 +1655,8 @@ spec:
for the abstraction
type: string
type: object
version:
type: string
workloadRefPath:
description: WorkloadRefPath indicates where/if a trait
accepts a workloadRef object
@@ -2059,6 +2065,8 @@ spec:
- configuration
type: object
type: object
version:
type: string
type: object
status:
description: WorkflowStepDefinitionStatus is the status of WorkflowStepDefinition

View File

@@ -195,6 +195,8 @@ spec:
the abstraction
type: string
type: object
version:
type: string
workload:
description: Workload is a workload type descriptor
properties:

View File

@@ -241,6 +241,8 @@ spec:
for the abstraction
type: string
type: object
version:
type: string
workload:
description: Workload is a workload type descriptor
properties:
@@ -489,6 +491,8 @@ spec:
- configuration
type: object
type: object
version:
type: string
type: object
status:
description: PolicyDefinitionStatus is the status of PolicyDefinition
@@ -774,6 +778,8 @@ spec:
for the abstraction
type: string
type: object
version:
type: string
workloadRefPath:
description: WorkloadRefPath indicates where/if a trait accepts
a workloadRef object
@@ -992,6 +998,8 @@ spec:
- configuration
type: object
type: object
version:
type: string
type: object
status:
description: WorkflowStepDefinitionStatus is the status of WorkflowStepDefinition

View File

@@ -156,6 +156,8 @@ spec:
- configuration
type: object
type: object
version:
type: string
type: object
status:
description: PolicyDefinitionStatus is the status of PolicyDefinition

View File

@@ -225,6 +225,8 @@ spec:
the abstraction
type: string
type: object
version:
type: string
workloadRefPath:
description: WorkloadRefPath indicates where/if a trait accepts a
workloadRef object

View File

@@ -152,6 +152,8 @@ spec:
- configuration
type: object
type: object
version:
type: string
type: object
status:
description: WorkflowStepDefinitionStatus is the status of WorkflowStepDefinition

View File

@@ -14,13 +14,13 @@ webhooks:
service:
name: {{ template "kubevela.name" . }}-webhook
namespace: {{ .Release.Namespace }}
path: /validating-core-oam-dev-v1alpha2-traitdefinitions
path: /validating-core-oam-dev-v1beta1-traitdefinitions
{{- if .Values.admissionWebhooks.patch.enabled }}
failurePolicy: Ignore
{{- else }}
failurePolicy: {{ .Values.admissionWebhooks.failurePolicy }}
{{- end }}
name: validating.core.oam.dev.v1alpha2.traitdefinitions
name: validating.core.oam.dev.v1beta1.traitdefinitions
sideEffects: None
admissionReviewVersions:
- v1beta1
@@ -35,7 +35,6 @@ webhooks:
- UPDATE
resources:
- traitdefinitions
scope: Cluster
timeoutSeconds: 5
- clientConfig:
caBundle: Cg==

View File

@@ -0,0 +1,57 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/podsecuritycontext.cue
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Adds security context to the pod spec in path 'spec.template.spec.securityContext'.
name: podsecuritycontext
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
appliesToWorkloads:
- deployments.apps
- statefulsets.apps
- daemonsets.apps
- jobs.batch
podDisruptive: true
schematic:
cue:
template: |
patch: spec: template: spec: securityContext: {
if parameter.appArmorProfile != _|_ {
appArmorProfile: parameter.appArmorProfile
}
if parameter.fsGroup != _|_ {
fsGroup: parameter.fsGroup
}
if parameter.runAsGroup != _|_ {
runAsGroup: parameter.runAsGroup
}
if parameter.runAsUser != _|_ {
runAsUser: parameter.runAsUser
}
if parameter.seccompProfile != _|_ {
seccompProfile: parameter.seccompProfile
}
runAsNonRoot: parameter.runAsNonRoot
}
parameter: {
// +usage=Specify the AppArmor profile for the pod
appArmorProfile?: {
type: "RuntimeDefault" | "Unconfined" | "Localhost"
localhostProfile: string
}
fsGroup?: int
runAsGroup?: int
// +usage=Specify the UID to run the entrypoint of the container process
runAsUser?: int
// +usage=Specify if the container runs as a non-root user
runAsNonRoot: *true | bool
// +usage=Specify the seccomp profile for the pod
seccompProfile?: {
type: "RuntimeDefault" | "Unconfined" | "Localhost"
localhostProfile: string
}
}

View File

@@ -0,0 +1,107 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/securitycontext.cue
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Adds security context to the container spec in path 'spec.template.spec.containers.[].securityContext'.
name: securitycontext
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
appliesToWorkloads:
- deployments.apps
- statefulsets.apps
- daemonsets.apps
- jobs.batch
podDisruptive: true
schematic:
cue:
template: |
#PatchParams: {
// +usage=Specify the name of the target container, if not set, use the component name
containerName: *"" | string
addCapabilities?: [...string]
allowPrivilegeEscalation: *false | bool
dropCapabilities?: [...string]
privileged: *false | bool
readOnlyRootFilesystem: *false | bool
runAsNonRoot: *true | bool
runAsUser?: int
runAsGroup?: int
}
PatchContainer: {
_params: #PatchParams
name: _params.containerName
_baseContainers: context.output.spec.template.spec.containers
_matchContainers_: [for _container_ in _baseContainers if _container_.name == name {_container_}]
_baseContainer: *_|_ | {...}
if len(_matchContainers_) == 0 {
err: "container \(name) not found"
}
if len(_matchContainers_) > 0 {
securityContext: {
capabilities: {
if _params.addCapabilities != _|_ {
add: _params.addCapabilities
}
if _params.dropCapabilities != _|_ {
drop: _params.dropCapabilities
}
}
if _params.runAsUser != _|_ {
runAsUser: _params.runAsUser
}
if _params.runAsGroup != _|_ {
runAsGroup: _params.runAsGroup
}
allowPrivilegeEscalation: _params.allowPrivilegeEscalation
readOnlyRootFilesystem: _params.readOnlyRootFilesystem
privileged: _params.privileged
runAsNonRoot: _params.runAsNonRoot
}
}
}
patch: spec: template: spec: {
if parameter.containers == _|_ {
// +patchKey=name
containers: [{
PatchContainer & {_params: {
if parameter.containerName == "" {
containerName: context.name
}
if parameter.containerName != "" {
containerName: parameter.containerName
}
allowPrivilegeEscalation: parameter.allowPrivilegeEscalation
readOnlyRootFilesystem: parameter.readOnlyRootFilesystem
privileged: parameter.privileged
runAsNonRoot: parameter.runAsNonRoot
runAsUser: parameter.runAsUser
runAsGroup: parameter.runAsGroup
addCapabilities: parameter.addCapabilities
dropCapabilities: parameter.dropCapabilities
}}
}]
}
if parameter.containers != _|_ {
// +patchKey=name
containers: [for c in parameter.containers {
if c.containerName == "" {
err: "containerName must be set for containers"
}
if c.containerName != "" {
PatchContainer & {_params: c}
}
}]
}
}
parameter: #PatchParams | close({
// +usage=Specify the container image for multiple containers
containers: [...#PatchParams]
})
errs: [for c in patch.spec.template.spec.containers if c.err != _|_ {c.err}]

View File

@@ -296,4 +296,4 @@ authentication:
## @param sharding.schedulableShards The shards available for scheduling. If empty, dynamic discovery will be used.
sharding:
enabled: false
schedulableShards: ""
schedulableShards: ""

View File

@@ -0,0 +1,321 @@
# Versioning Support for KubeVela Definitions
<!-- toc -->
- [Versioning Support for KubeVela Definitions](#versioning-support-for-kubevela-definitions)
- [Summary](#summary)
- [Scope](#scope)
- [Motivation](#motivation)
- [Goals](#goals)
- [Non-Goals](#non-goals)
- [Acceptance Criteria](#acceptance-criteria)
- [Current Implementation](#current-implementation)
- [Versioning](#versioning)
- [Auto Upgrade](#auto-upgrade)
- [Reference:](#reference)
- [Proposal](#proposal)
- [Details](#details)
- [Issues](#issues)
- [Examples](#examples)
<!-- /toc -->
## Summary
Support Semantic versioning for KubeVela Components and a way to allow fine control over auto-upgrades of KubeVela Applications to new versions of a Component. The implementation should include support for consistent versioning across environments/clusters, meaning specific Revisions/Versions of a Component should have consistent behaviour.
## Scope
Although, this document limits the scope of discussion to ComponentDefinition Revisions/Versions, due to the current implementation, the changes will most likely apply to all [`Definition`](https://kubevela.io/docs/getting-started/definition/) types. These changes are planned to be explored and validated as part of the implementation.
## Motivation
OAM/KubeVela Definitions (referred to as ComponentDefinitions of Components in the rest
of the document) are the basic building blocks of the KubeVela platform. They
expose a contract similar to an API contract, which evolves from minor to major
versions. Applications are composed of Components that the KubeVela engine stitches
together.
KubeVela creates a `DefinitionRevision` for all changes in a Component `spec`.
Currently, Applications can refer to a particular Revision of a Component.
But, this versioning scheme has the following issues:
- The `DefinitionRevision` does not denote the type of the change (patch/bug, minor or major). This hinders automation of automatic upgrades.
- The current scheme also doesn't allow much control over automatic upgrades to new Component Revisions. KubeVela automatically upgrades/reconciles the Application to the
latest when no Component Revision is specified.
> While we don't ideally want Application developers to bother with such details, there are use cases where
> an automatic upgrade to the latest Component version is not desired.
### Goals
- Support Component versioning with Semantic Versions.
- Allow pinning specific and non-specific versions of a Component in the
KubeVela Application.
### Non-Goals
- Support for version range in Application. For eg. "type: my-component@>1.2.0"
## Acceptance Criteria
**User Story: Component version specification**
>**AS A** Component author\
>**I SHOULD** be able to publish every version of my Component with the Semantic Versioning scheme\
>**SO THAT** an Application developer can use a specific version of the Component.
**BDD Acceptance Criteria**
>**GIVEN** an updated ComponentDefinition Specification \
>**AND** a version denoted by the ComponentDefinition is set to V\
>**WHEN** the Component is applied to KubeVela\
>**THEN** `V` should be listed as one of the many versions in the DefinitionRevision list
**User Story: Application Component version specification**
>**AS AN** Application developer\
>**I SHOULD** be able to specify a version (complete or partial) for every Component used\
>**SO THAT** I can control which version are deployed.
**BDD Acceptance Criteria**
>Scenario 1: Use the version specified in the Application manifest when deploying the service\
>**GIVEN** a Component A with versions 1.2.2 | 1.2.3\
>**AND** a Component B with versions 4.4.2 | 4.5.6\
>**AND** an Application composed of A 1.2.2 and B 4.4.2\
>**WHEN** the Application is deployed\
>**THEN** it uses Component A 1.2.2 and B 4.4.2
>
>**Variant:** Use the latest version for the part of the SemVer that is not specified.\
>**GIVEN** component A latest version is 1.2.3
>**AND** Component B latest version is 4.5.6
>**AND** an Application composed of A 1.2 and B 4\
>**WHEN** the Application is deployed\
>**THEN** it uses Component A 1.2.3 and B 4.5.6
> Scenario 2: Behaviour when auto-upgrade is disabled \
> **GIVEN** a component A with version 1.2.3\
> **AND** an Application composed of A-1.2.3\
> **IF** Auto-upgrade is disabled\
> **WHEN** a new version of Component A (A-1.2.5) is released\
> **THEN** the Application should continue to use A-1.2.3
>
> **Variant** Behaviour when auto-upgrade is disabled and exact version is unavailable.\
> **GIVEN** a component A with version 1.2.3\
> **AND** a new Application composed of A-1.2.2\
> **IF** Auto-upgrade is disabled\
> **WHEN** the Application is applied\
> **THEN** the Application deployment should fail.
>
> **Variant** Behaviour when auto-upgrade is disabled and exact version is unavailable.\
> **GIVEN** a component A with version 1.2.3\
> **AND** a new Application composed of A-1.2\
> **IF** Auto-upgrade is disabled\
> **WHEN** the Application is applied\
> **THEN** the Application deployment should fail.
> Scenario 3: Behaviour when auto-upgrade is enabled \
> **GIVEN** a component A with version 1.2.3\
> **AND** an Application composed of A-1.2\
> **IF** Auto-upgrade is enabled\
> **THEN** the Application should use A-1.2.3
> **AND WHEN** a new version of Component A (A-1.2.5) is released\
> **THEN** the Application should update to use A-1.2.5
>
> **Variant** Behaviour when auto-upgrade is enabled and exact version is unavailable \
> **GIVEN** a component A with version 1.2.3\
> **AND** a new Application composed of A-1.2.2\
> **IF** Auto-upgrade is enabled\
> **WHEN** the Application is applied\
> **THEN** the Application deployment should fail.
>
> **Variant** Behaviour when auto-upgrade is enabled and exact version is unavailable \
> **GIVEN** a component A with version 1.2.3\
> **AND** a new Application composed of A-1.2\
> **IF** Auto-upgrade is enabled\
> **WHEN** the Application is applied\
> **THEN** the Application deployment should use A-1.2.3.
> Scenario 4: Expectations of consistent versioning across Environments/Clusters. \
> **GIVEN** a component A with versions 1.2.1|1.2.2|2.2.1\
> **AND** an Application composed of A-1.2.2\
> **IF** the Application needs to be deployed across Environments (Dev, Prod etc)\
> **OR** the Application needs to be deployed in multiple clusters managed independently\
> **WHEN** the Application is deployed across Environments/Clusters \
> **THEN** The Application should behave consistently, as in all the clusters A-1.2.2 map to the same ComponentDefinition changes.
## Current Implementation
### Versioning
Currently, KubeVela has some support for controlling Definition versions based on K8s annotations and DefinitionRevisions. The annotation `definitionrevision.oam.dev/name` can be used to version the ComponentDefinition. For example if the following annotation is added to a ComponentDefinition, it produces a new DefinitionRevision and names the ComponentDefinition as `component-name-v4.4` .
> definitionrevision.oam.dev/name: "4.4"
This Component can then be referred in the Application as follows:
>"component-name@v4.4" - `NamedDefinitionRevision`
Alternatively, since DefinitionRevisions are maintained even if a **"named"** Revision is not specified via the annotation `definitionrevision.oam.dev/name`, Applications can still refer to a particular Revision of a Component via the auto-incrementing Revision numbers.
>"component-name@v2" - `DefinitionRevision`
![version](./kubevela-version.png)
This versioning scheme, although convenient, has the following issues:
- Applications which do not explicitly specify a target Revision of a ComponentDefinition, the "latest" applied revision of the ComponentDefinition is used. In scenarios where a cluster has to be replicated or re-created, this means that the sequence in which revisions of a ComponentDefinition are applied becomes important. Implicitly, this also means that the Component maintainers need to keep all Revisions of a ComponentDefinition in their deployment pipeline.\
If `definitionrevision.oam.dev/name` annotation is not added to ComponentDefinitions, even if the Applications are explicit about a Component Revision, there is currently no guarantee that the Application behaviour will be consistent across Environments/Clusters. For example, a `Dev` environment will typically have more churn in Revisions than a `Prod` one and a reference to Component Revision `v3` in an Application will not be the same in both environments.
### Auto Upgrade
KubeVela utilises the annotation `app.oam.dev/autoUpdate` for automatic upgrade.
Application reconciliation behaviour when the `app.oam.dev/autoUpdate` annotation is specified in the Application:
- If a ComponentDefinition Revision is not specified, the Application will always use the latest available Revision.
- If a ComponentDefinition Revision is specified and a new Revision is released after the Application was created, the latest changes will not reflect in the Application.
Note: This feature is not documented in KubeVela documentation.
#### Reference:
- https://kubevela.io/docs/platform-engineers/x-def-version/
- [Auto Upgrade PR](https://github.com/kubevela/kubevela/pull/3217)
## Proposal
### Introduce `spec.version` as an optional field in the Definition
- Add an optional field `version` in the Definition `spec` and use it to generate the ComponentDefinition Revisions.
- Update the auto-upgrade behaviour to also allow limiting upgrades for an Application within a specified Definition version range. The existing annotation `app.oam.dev/autoUpdate` for enabling automatic updates will be used for this new behaviour and will maintain backward compatibility.
- Implement Validating webhook to:
- Ensure that the values of the annotation `definitionrevision.oam.dev/version`, `definitionrevision.oam.dev/name` or `spec.version` field adhere to semantic versioning.
- Ensure that the `definitionrevision.oam.dev/name` annotation and the `spec.version` field are not present together in the ComponentDefinition to avoid conflicts.
- Ensure that `app.oam.dev/publishVersion` and `app.oam.dev/autoUpdate` both annotation are not present in Application to avoid conflicts.
### Issues
The following issues assume adherence to strict backward compatibility, meaning the `definitionrevision.oam.dev/name` annotation should continue to work as is.
- It does not resolve inconsistent versioning behaviour across Environments/Clusters when explicit versions are not specified or named DefinitionRevisions are not used.
## Examples
1. Create a `configmap-component` ComponentDefinition with `1.2.5` version
```
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: configmap-component
namespace: vela-system
spec:
version: 1.2.5
schematic:
cue:
template: |
output: {
apiVersion: "v1"
kind: "ConfigMap"
metadata: {
name: "comptest"
}
data: {
version: "125"
}
}
workload:
definition:
apiVersion: v1
kind: ConfigMap
```
2. Create a `configmap-component` ComponentDefinition with `2.0.5` version
```apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: configmap-component
namespace: vela-system
spec:
version: 2.5.0
schematic:
cue:
template: |
output: {
apiVersion: "v1"
kind: "ConfigMap"
metadata: {
name: "comptest"
}
data: {
version: "250"
}
}
workload:
definition:
apiVersion: v1
kind: ConfigMap
```
3. List DefinitionRevisions
```
kubectl get definitionrevision -n vela-system | grep -i my-component
my-component-v1.2.5 1 1a4f3ac77e4fcfef Component
my-component-v2.5.0 2 e61e9b5e55b01c2b Component
```
4. Create Application using `configmap-component@v1.2` version and enable the Auto Update using `app.oam.dev/autoUpdate` annotation.
```apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: test-app
namespace: test
annotations:
app.oam.dev/autoUpdate: "true"
spec:
components:
- name: test
type: my-component@v1
```
Expected Behavior:
- Application will use `configmap-component@v1.2.5`, as `1.2.5` is highest version in specified range(`1`).
5. Create a `configmap-component` ComponentDefinition with `1.2.7` version
```
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: configmap-component
namespace: vela-system
spec:
version: 1.2.7
schematic:
cue:
template: |
output: {
apiVersion: "v1"
kind: "ConfigMap"
metadata: {
name: "comptest"
}
data: {
version: "127"
}
}
workload:
definition:
apiVersion: v1
kind: ConfigMap
```
Expected Behavior:
- After the Application is reconciled, it will use `configmap-component@v1.2.7`, as `1.2.7` is the latest version within the specified range (1).
6. List Definitionrevision
```kubectl get definitionrevision -n vela-system | grep -i my-component
my-component-v1.2.5 1 1a4f3ac77e4fcfef Component
my-component-v1.2.7 3 86d7fb1a36566dea Component
my-component-v2.5.0 2 e61e9b5e55b01c2b Component```

View File

@@ -0,0 +1,249 @@
/*
Copyright 2024 The KubeVela 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"
"fmt"
"math/rand"
"strconv"
"strings"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
oamcommon "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/e2e"
"github.com/oam-dev/kubevela/pkg/utils/common"
)
var _ = Describe("Application Auto update", Ordered, func() {
ctx := context.Background()
var k8sClient client.Client
var namespace string
var ns corev1.Namespace
var err error
var velaCommandPrefix string
BeforeEach(func() {
k8sClient, err = common.NewK8sClient()
Expect(err).NotTo(HaveOccurred())
By("Create namespace for app-autoupdate-e2e-test")
namespace = randomNamespaceName("app-autoupdate-e2e-test")
ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
k8sClient.Create(ctx, &ns)
velaCommandPrefix = fmt.Sprintf("vela -n %s", namespace)
})
AfterEach(func() {
By("Clean up resources after a test")
k8sClient.DeleteAllOf(ctx, &v1beta1.Application{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.ComponentDefinition{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.DefinitionRevision{}, client.InNamespace(namespace))
Expect(k8sClient.Delete(ctx, &ns)).Should(BeNil())
})
It("dry-run command", func() {
By("Create configmap-component with 1.2.0 version")
component := configMapComponent.DeepCopy()
component.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
By("Execute a dry-run for application having configmap-component@v1 component")
output, err := e2e.Exec(fmt.Sprintf("%s dry-run -f data/app.yaml", velaCommandPrefix))
Expect(err).NotTo(HaveOccurred())
Expect(output).To(ContainSubstring(fmt.Sprintf(dryRunResult1, namespace)))
By("Create application using configmap-component@v1 component")
_, err = e2e.Exec(fmt.Sprintf("%s up -f data/app.yaml", velaCommandPrefix))
Expect(err).NotTo(HaveOccurred())
By("Create configmap-component with 1.4.0 version")
updatedComponent := new(v1beta1.ComponentDefinition)
updatedComponentVersion := "1.4.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "configmap-component", Namespace: namespace}, updatedComponent)
if err != nil {
return err
}
updatedComponent.Spec.Schematic.CUE.Template = strings.Replace(configMapOutputTemplate, updatedComponent.Spec.Version, updatedComponentVersion, 1)
updatedComponent.Spec.Version = updatedComponentVersion
return k8sClient.Update(ctx, updatedComponent)
}, 15*time.Second, time.Second).Should(BeNil())
By("Execute a dry-run for application having configmap-component@v1 component")
output, err = e2e.Exec(fmt.Sprintf("%s dry-run -f data/app.yaml", velaCommandPrefix))
Expect(err).NotTo(HaveOccurred())
Expect(output).To(ContainSubstring(fmt.Sprintf(dryRunResult2, namespace, namespace)))
})
It("live-diff between application file and revision", func() {
By("Create configmap-component with 1.2.0 version")
component := configMapComponent.DeepCopy()
component.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
By("Create application using configmap-component@v1 component")
_, err = e2e.Exec(fmt.Sprintf("%s up -f data/app.yaml", velaCommandPrefix))
Expect(err).NotTo(HaveOccurred())
By("Create configmap-component with 1.4.0 version")
updatedComponent := new(v1beta1.ComponentDefinition)
updatedComponentVersion := "1.4.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "configmap-component", Namespace: namespace}, updatedComponent)
if err != nil {
return err
}
updatedComponent.Spec.Schematic.CUE.Template = strings.Replace(configMapOutputTemplate, updatedComponent.Spec.Version, updatedComponentVersion, 1)
updatedComponent.Spec.Version = updatedComponentVersion
return k8sClient.Update(ctx, updatedComponent)
}, 15*time.Second, time.Second).Should(BeNil())
By("Execute a live-diff command for application file and previous application")
output, err := e2e.Exec(fmt.Sprintf("%s live-diff -f data/app.yaml -r app-with-auto-update-v1", velaCommandPrefix))
Expect(err).NotTo(HaveOccurred())
Expect(output).To(ContainSubstring(liveDiffResult))
})
It("live-diff between revisions", func() {
By("Create configmap-component with 1.2.0 version")
component := configMapComponent.DeepCopy()
component.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
By("Create application using configmap-component@v1 component")
_, err = e2e.Exec(fmt.Sprintf("%s up -f data/app.yaml", velaCommandPrefix))
Expect(err).NotTo(HaveOccurred())
By("Create configmap-component with 1.4.0 version")
updatedComponent := new(v1beta1.ComponentDefinition)
updatedComponentVersion := "1.4.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "configmap-component", Namespace: namespace}, updatedComponent)
if err != nil {
return err
}
updatedComponent.Spec.Schematic.CUE.Template = strings.Replace(configMapOutputTemplate, updatedComponent.Spec.Version, updatedComponentVersion, 1)
updatedComponent.Spec.Version = updatedComponentVersion
return k8sClient.Update(ctx, updatedComponent)
}, 15*time.Second, time.Second).Should(BeNil())
By("Create application using configmap-component@v1 component")
_, err = e2e.Exec(fmt.Sprintf("%s up -f data/app.yaml", velaCommandPrefix))
Expect(err).NotTo(HaveOccurred())
By("Execute a live-diff command for previous two application versions")
output, err := e2e.Exec(fmt.Sprintf("%s live-diff --revision app-with-auto-update-v2,app-with-auto-update-v1", velaCommandPrefix))
Expect(err).NotTo(HaveOccurred())
Expect(output).To(ContainSubstring("Application (app-with-auto-update) has no change"))
})
})
var configMapComponent = &v1beta1.ComponentDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "ComponentDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "configmap-component",
},
Spec: v1beta1.ComponentDefinitionSpec{
Version: "1.2.0",
Schematic: &oamcommon.Schematic{
CUE: &oamcommon.CUE{
Template: configMapOutputTemplate,
},
},
},
}
var configMapOutputTemplate = `output: {
apiVersion: "v1"
kind: "ConfigMap"
metadata: name: "comptest"
data: {
expectedVersion: "1.2.0"
}
}`
func randomNamespaceName(basic string) string {
return fmt.Sprintf("%s-%s", basic, strconv.FormatInt(rand.Int63(), 16))
}
var dryRunResult1 = `---
# Application(app-with-auto-update) -- Component(test)
---
apiVersion: v1
data:
expectedVersion: 1.2.0
kind: ConfigMap
metadata:
annotations:
app.oam.dev/autoUpdate: "true"
labels:
app.oam.dev/appRevision: ""
app.oam.dev/component: test
app.oam.dev/name: app-with-auto-update
app.oam.dev/namespace: %[1]s
app.oam.dev/resourceType: WORKLOAD
workload.oam.dev/type: configmap-component-v1
name: comptest
namespace: %[1]s
---`
var dryRunResult2 = `---
# Application(app-with-auto-update) -- Component(test)
---
apiVersion: v1
data:
expectedVersion: 1.4.0
kind: ConfigMap
metadata:
annotations:
app.oam.dev/autoUpdate: "true"
labels:
app.oam.dev/appRevision: ""
app.oam.dev/component: test
app.oam.dev/name: app-with-auto-update
app.oam.dev/namespace: %[1]s
app.oam.dev/resourceType: WORKLOAD
workload.oam.dev/type: configmap-component-v1
name: comptest
namespace: %[1]s
---
`
var liveDiffResult = `
- expectedVersion: 1.2.0
+ expectedVersion: 1.4.0
`

View File

@@ -184,8 +184,10 @@ var ApplicationInitIntercativeCliContext = func(context string, appName string,
})
}
// debug test
var ApplicationDeleteWithWaitOptions = func(context string, appName string) bool {
return ginkgo.It(context+": should print successful deletion information", func() {
return ginkgo.It(context+": should print successful deletion information ", func() {
time.Sleep(1 * time.Minute)
cli := fmt.Sprintf("vela delete %s --wait -y", appName)
output, err := e2e.LongTimeExec(cli, 10*time.Second)
gomega.Expect(err).NotTo(gomega.HaveOccurred())

View File

@@ -0,0 +1,10 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: app-with-auto-update
annotations:
app.oam.dev/autoUpdate: "true"
spec:
components:
- name: test
type: configmap-component@v1

View File

@@ -56,7 +56,7 @@ func ExecAndTerminate(cli string) (string, error) {
if err != nil {
return string(output), err
}
time.Sleep(3 * time.Second)
time.Sleep(10 * time.Second)
s := session.Terminate()
return string(s.Out.Contents()) + string(s.Err.Contents()), nil
}

View File

@@ -462,24 +462,26 @@ spec:
- backend
podDisruptive: true
schematic:
kube:
template:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
cue:
template: |
output: {
apiVersion: "v1"
kind: "Service"
metadata: {
name: "my-service"
}
spec:{
ports: [{
protocol: "TCP"
port: 80
targetPort: 9376
parameters:
- name: targetPort
required: true
type: number
fieldPaths:
- "spec.template.spec.ports[0].targetPort"
description: "target port num for service provider."
targetPort: parameters.targetPort
}]
}
parameters:{
//+usage=target port num for service provider
targetPort: *9376 | int
}
}
`
var componentWithDeepCue = `

63
go.mod
View File

@@ -6,6 +6,7 @@ require (
cuelang.org/go v0.9.2
github.com/AlecAivazis/survey/v2 v2.1.1
github.com/FogDong/uitable v0.0.5
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/semver/v3 v3.2.1
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
@@ -24,7 +25,7 @@ require (
github.com/form3tech-oss/jwt-go v3.2.5+incompatible
github.com/gdamore/tcell/v2 v2.6.0
github.com/getkin/kin-openapi v0.118.0
github.com/go-git/go-git/v5 v5.8.1
github.com/go-git/go-git/v5 v5.13.1
github.com/go-logr/logr v1.4.1
github.com/go-resty/resty/v2 v2.8.0
github.com/golang/mock v1.6.0
@@ -48,8 +49,8 @@ require (
github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28
github.com/oam-dev/terraform-controller v0.8.0
github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/ginkgo/v2 v2.14.0
github.com/onsi/gomega v1.30.0
github.com/onsi/ginkgo/v2 v2.19.0
github.com/onsi/gomega v1.34.1
github.com/openkruise/kruise-api v1.4.0
github.com/openkruise/rollouts v0.3.0
github.com/pelletier/go-toml v1.9.5
@@ -60,19 +61,19 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
github.com/tidwall/gjson v1.14.4
github.com/wercker/stern v0.0.0-20190705090245-4fa46dd6987f
github.com/xanzy/go-gitlab v0.91.1
github.com/xlab/treeprint v1.2.0
go.uber.org/multierr v1.11.0
golang.org/x/crypto v0.25.0
golang.org/x/mod v0.17.0
golang.org/x/crypto v0.32.0
golang.org/x/mod v0.19.0
golang.org/x/oauth2 v0.20.0
golang.org/x/sync v0.7.0
golang.org/x/term v0.22.0
golang.org/x/text v0.16.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
golang.org/x/sync v0.10.0
golang.org/x/term v0.28.0
golang.org/x/text v0.21.0
golang.org/x/tools v0.23.0
gomodules.xyz/jsonpatch/v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.14.4
@@ -105,15 +106,13 @@ require (
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.12.2 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 // indirect
@@ -129,16 +128,14 @@ require (
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/containerd/containerd v1.7.24 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/containerd/containerd v1.7.14 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/creack/pty v1.1.18 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/cli v26.0.0+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v26.0.0+incompatible // indirect
@@ -163,7 +160,7 @@ require (
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-git/go-billy/v5 v5.6.1 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
@@ -171,7 +168,7 @@ require (
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gobuffalo/flect v1.0.2 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -182,7 +179,7 @@ require (
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 // indirect
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
@@ -248,9 +245,9 @@ require (
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rubenv/sql-migrate v1.5.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
@@ -264,27 +261,27 @@ require (
go.etcd.io/etcd/api/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/v3 v3.5.10 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect
go.opentelemetry.io/otel v1.21.0 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/sdk v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.starlark.net v0.0.0-20240329153429-e6e8e7ce1b7a // indirect
go.uber.org/automaxprocs v1.5.3 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
google.golang.org/grpc v1.63.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/evanphx/json-patch.v5 v5.9.0 // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect

151
go.sum
View File

@@ -53,8 +53,8 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.12.2 h1:AcXy+yfRvrx20g9v7qYaJv5Rh+8GaHOS6b8G6Wx/nKs=
github.com/Microsoft/hcsshim v0.12.2/go.mod h1:RZV12pcHCXQ42XnlQ3pz6FZfmrC1C+R4gaOHhRNML1g=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@@ -63,14 +63,12 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs=
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
@@ -136,7 +134,6 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
@@ -157,7 +154,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@@ -178,16 +174,14 @@ github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u9
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA=
github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N0GNPJwA=
github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -215,8 +209,8 @@ github.com/crossplane/crossplane-runtime v1.16.0 h1:lz+l0wEB3qowdTmN7t0PZkfuNSvf
github.com/crossplane/crossplane-runtime v1.16.0/go.mod h1:Pz2tdGVMF6KDGzHZOkvKro0nKc8EzK0sb/nSA7pH4Dc=
github.com/cue-exp/kubevelafix v0.0.0-20220922150317-aead819d979d h1:VNJA1nSKA8Xna5wjUIMItHlWmEej8Bb9fZ3vCNtIAX0=
github.com/cue-exp/kubevelafix v0.0.0-20220922150317-aead819d979d/go.mod h1:SyTryzw/zYJIogw3H2IRcYdV5gsSoVMJiKGElcQK09I=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/dave/jennifer v1.6.1 h1:T4T/67t6RAA5AIV6+NP8Uk/BIsXgDoqEowgycdQQLuk=
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -226,8 +220,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
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/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v24.0.9+incompatible h1:OxbimnP/z+qVjDLpq9wbeFU3Nc30XhSe+LkwYQisD50=
github.com/docker/cli v24.0.9+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
@@ -251,8 +245,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
@@ -330,18 +324,18 @@ github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BH
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
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.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A=
github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo=
github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
@@ -393,8 +387,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
@@ -491,8 +485,8 @@ 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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 h1:WzfWbQz/Ze8v6l++GGbGNFZnUShVpP/0xffCPLL+ax8=
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI=
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
@@ -683,8 +677,6 @@ github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY
github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -743,8 +735,6 @@ github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
@@ -803,8 +793,8 @@ github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8Ay
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=
github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY=
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
@@ -815,8 +805,8 @@ github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ
github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
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 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@@ -919,8 +909,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
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/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -932,8 +922,8 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.8.1/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/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -980,8 +970,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -1032,8 +1022,8 @@ github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8=
go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k=
@@ -1070,8 +1060,8 @@ go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUz
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.0/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg=
@@ -1080,8 +1070,8 @@ go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzox
go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU=
go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM=
go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw=
@@ -1094,22 +1084,22 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S2
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I=
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A=
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI=
go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk=
go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4=
go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM=
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
@@ -1159,16 +1149,14 @@ golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1189,8 +1177,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1238,11 +1226,10 @@ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1268,8 +1255,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1341,8 +1328,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1352,11 +1339,10 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -1366,11 +1352,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1408,8 +1393,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1501,8 +1486,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=

View File

@@ -24,13 +24,14 @@ e2e-setup-core-wo-auth:
--namespace vela-system \
--set image.pullPolicy=IfNotPresent \
--set image.repository=vela-core-test \
--set applicationRevisionLimit=5 \
--set applicationRevisionLimit=5 \
--set controllerArgs.reSyncPeriod=1m \
--set optimize.disableComponentRevision=false \
--set image.tag=$(GIT_COMMIT) \
--set multicluster.clusterGateway.image.repository=ghcr.io/oam-dev/cluster-gateway \
--set admissionWebhooks.patch.image.repository=ghcr.io/oam-dev/kube-webhook-certgen/kube-webhook-certgen \
--set multicluster.clusterGateway.image.repository=ghcr.io/oam-dev/cluster-gateway \
--set admissionWebhooks.patch.image.repository=ghcr.io/oam-dev/kube-webhook-certgen/kube-webhook-certgen \
--wait kubevela ./charts/vela-core \
--debug
--debug
.PHONY: e2e-setup-core-w-auth
e2e-setup-core-w-auth:
@@ -109,4 +110,4 @@ end-e2e-core-shards: end-e2e-core
.PHONY: end-e2e
end-e2e:
sh ./hack/e2e/end_e2e.sh
sh ./hack/e2e/end_e2e.sh

View File

@@ -37,6 +37,7 @@ import (
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile"
"github.com/oam-dev/kubevela/pkg/oam"
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
)
// NewLiveDiffOption creates a live-diff option
@@ -118,9 +119,11 @@ func (l *LiveDiffOption) RenderlessDiff(ctx context.Context, base, comparor Live
switch {
case obj.Application != nil:
app = obj.Application.DeepCopy()
ctx = context.WithValue(ctx, oamutil.AppDefinitionNamespace, app.Namespace)
af, err = l.Parser.GenerateAppFileFromApp(ctx, obj.Application)
case obj.ApplicationRevision != nil:
app = obj.ApplicationRevision.Spec.Application.DeepCopy()
ctx = context.WithValue(ctx, oamutil.AppDefinitionNamespace, app.Namespace)
af, err = l.Parser.GenerateAppFileFromRevision(obj.ApplicationRevision)
default:
err = errors.Errorf("either application or application revision should be set for LiveDiffObject")

View File

@@ -109,9 +109,15 @@ func (d *Option) ValidateApp(ctx context.Context, filename string) error {
if err != nil {
return err
}
if len(app.GetNamespace()) == 0 {
namespace := oamutil.GetDefinitionNamespaceWithCtx(ctx)
if namespace != "" {
app.SetNamespace(namespace)
} else if len(app.GetNamespace()) == 0 {
app.SetNamespace(corev1.NamespaceDefault)
}
app2 := app.DeepCopy()
err = d.Client.Get(ctx, client.ObjectKey{Namespace: app.GetNamespace(), Name: app.GetName()}, app2)

View File

@@ -52,11 +52,11 @@ import (
)
// TemplateLoaderFn load template of a capability definition
type TemplateLoaderFn func(context.Context, client.Client, string, types.CapType) (*Template, error)
type TemplateLoaderFn func(context.Context, client.Client, string, types.CapType, map[string]string) (*Template, error)
// LoadTemplate load template of a capability definition
func (fn TemplateLoaderFn) LoadTemplate(ctx context.Context, c client.Client, capName string, capType types.CapType) (*Template, error) {
return fn(ctx, c, capName, capType)
func (fn TemplateLoaderFn) LoadTemplate(ctx context.Context, c client.Client, capName string, capType types.CapType, annotations map[string]string) (*Template, error) {
return fn(ctx, c, capName, capType, annotations)
}
// Parser is an application parser
@@ -253,7 +253,8 @@ func (p *Parser) parseWorkflowStepsForLegacyRevision(ctx context.Context, af *Ap
continue
}
def := &v1beta1.WorkflowStepDefinition{}
if err := util.GetCapabilityDefinition(ctx, p.client, def, workflowStep.Type); err != nil {
if err := util.GetCapabilityDefinition(ctx, p.client, def, workflowStep.Type, af.app.Annotations); err != nil {
return errors.Wrapf(err, "failed to get workflow step definition %s", workflowStep.Type)
}
af.RelatedWorkflowStepDefinitions[workflowStep.Type] = def
@@ -383,7 +384,7 @@ func (p *Parser) parsePolicies(ctx context.Context, af *Appfile) (err error) {
af.RelatedTraitDefinitions[def.Name] = def
}
default:
w, err := p.makeComponent(ctx, policy.Name, policy.Type, types.TypePolicy, policy.Properties)
w, err := p.makeComponent(ctx, policy.Name, policy.Type, types.TypePolicy, policy.Properties, af.app.Annotations)
if err != nil {
return err
}
@@ -471,15 +472,15 @@ func (p *Parser) fetchAndSetWorkflowStepDefinition(ctx context.Context, af *Appf
return nil
}
def := &v1beta1.WorkflowStepDefinition{}
if err := util.GetCapabilityDefinition(ctx, p.client, def, workflowStepType); err != nil {
if err := util.GetCapabilityDefinition(ctx, p.client, def, workflowStepType, af.AppAnnotations); err != nil {
return errors.Wrapf(err, "failed to get workflow step definition %s", workflowStepType)
}
af.RelatedWorkflowStepDefinitions[workflowStepType] = def
return nil
}
func (p *Parser) makeComponent(ctx context.Context, name, typ string, capType types.CapType, props *runtime.RawExtension) (*Component, error) {
templ, err := p.tmplLoader.LoadTemplate(ctx, p.client, typ, capType)
func (p *Parser) makeComponent(ctx context.Context, name, typ string, capType types.CapType, props *runtime.RawExtension, annotations map[string]string) (*Component, error) {
templ, err := p.tmplLoader.LoadTemplate(ctx, p.client, typ, capType, annotations)
if err != nil {
return nil, errors.WithMessagef(err, "fetch component/policy type of %s", name)
}
@@ -519,7 +520,7 @@ func (p *Parser) convertTemplate2Component(name, typ string, props *runtime.RawE
func (p *Parser) parseComponents(ctx context.Context, af *Appfile) error {
var comps []*Component
for _, c := range af.app.Spec.Components {
comp, err := p.parseComponent(ctx, c)
comp, err := p.parseComponent(ctx, c, af.app.Annotations)
if err != nil {
return err
}
@@ -568,25 +569,25 @@ func setComponentDefinitionsFromRevision(af *Appfile) {
// parseComponent resolve an ApplicationComponent and generate a Component
// containing ALL information required by an Appfile.
func (p *Parser) parseComponent(ctx context.Context, comp common.ApplicationComponent) (*Component, error) {
workload, err := p.makeComponent(ctx, comp.Name, comp.Type, types.TypeComponentDefinition, comp.Properties)
func (p *Parser) parseComponent(ctx context.Context, comp common.ApplicationComponent, annotations map[string]string) (*Component, error) {
workload, err := p.makeComponent(ctx, comp.Name, comp.Type, types.TypeComponentDefinition, comp.Properties, annotations)
if err != nil {
return nil, err
}
if err = p.parseTraits(ctx, workload, comp); err != nil {
if err = p.parseTraits(ctx, workload, comp, annotations); err != nil {
return nil, err
}
return workload, nil
}
func (p *Parser) parseTraits(ctx context.Context, workload *Component, comp common.ApplicationComponent) error {
func (p *Parser) parseTraits(ctx context.Context, workload *Component, comp common.ApplicationComponent, annotations map[string]string) error {
for _, traitValue := range comp.Traits {
properties, err := util.RawExtension2Map(traitValue.Properties)
if err != nil {
return errors.Errorf("fail to parse properties of %s for %s", traitValue.Type, comp.Name)
}
trait, err := p.parseTrait(ctx, traitValue.Type, properties)
trait, err := p.parseTrait(ctx, traitValue.Type, properties, annotations)
if err != nil {
return errors.WithMessagef(err, "component(%s) parse trait(%s)", comp.Name, traitValue.Type)
}
@@ -649,7 +650,7 @@ func (p *Parser) parseTraitsFromRevision(comp common.ApplicationComponent, appRe
func (p *Parser) ParseComponentFromRevisionAndClient(ctx context.Context, c common.ApplicationComponent, appRev *v1beta1.ApplicationRevision) (*Component, error) {
comp, err := p.makeComponentFromRevision(c.Name, c.Type, types.TypeComponentDefinition, c.Properties, appRev)
if IsNotFoundInAppRevision(err) {
comp, err = p.makeComponent(ctx, c.Name, c.Type, types.TypeComponentDefinition, c.Properties)
comp, err = p.makeComponent(ctx, c.Name, c.Type, types.TypeComponentDefinition, c.Properties, appRev.Annotations)
}
if err != nil {
return nil, err
@@ -662,7 +663,7 @@ func (p *Parser) ParseComponentFromRevisionAndClient(ctx context.Context, c comm
}
trait, err := p.parseTraitFromRevision(traitValue.Type, properties, appRev)
if IsNotFoundInAppRevision(err) {
trait, err = p.parseTrait(ctx, traitValue.Type, properties)
trait, err = p.parseTrait(ctx, traitValue.Type, properties, appRev.Annotations)
}
if err != nil {
return nil, errors.WithMessagef(err, "component(%s) parse trait(%s)", c.Name, traitValue.Type)
@@ -674,8 +675,8 @@ func (p *Parser) ParseComponentFromRevisionAndClient(ctx context.Context, c comm
return comp, nil
}
func (p *Parser) parseTrait(ctx context.Context, name string, properties map[string]interface{}) (*Trait, error) {
templ, err := p.tmplLoader.LoadTemplate(ctx, p.client, name, types.TypeTrait)
func (p *Parser) parseTrait(ctx context.Context, name string, properties map[string]interface{}, annotations map[string]string) (*Trait, error) {
templ, err := p.tmplLoader.LoadTemplate(ctx, p.client, name, types.TypeTrait, annotations)
if kerrors.IsNotFound(err) {
return nil, errors.Errorf("trait definition of %s not found", name)
}

View File

@@ -559,7 +559,7 @@ func TestParser_parseTraits(t *testing.T) {
},
},
},
mockTemplateLoaderFn: func(context.Context, client.Client, string, types.CapType) (*Template, error) {
mockTemplateLoaderFn: func(context.Context, client.Client, string, types.CapType, map[string]string) (*Template, error) {
return nil, fmt.Errorf("unsupported key not found")
},
wantErr: assert.Error,
@@ -580,7 +580,7 @@ func TestParser_parseTraits(t *testing.T) {
workload: &Component{},
},
wantErr: assert.NoError,
mockTemplateLoaderFn: func(ctx context.Context, reader client.Client, s string, capType types.CapType) (*Template, error) {
mockTemplateLoaderFn: func(ctx context.Context, reader client.Client, s string, capType types.CapType, annotations map[string]string) (*Template, error) {
return &Template{
TemplateStr: "template",
CapabilityCategory: "network",
@@ -598,7 +598,8 @@ func TestParser_parseTraits(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p.tmplLoader = tt.mockTemplateLoaderFn
err := p.parseTraits(context.Background(), tt.args.workload, tt.args.comp)
annotations := make(map[string]string)
err := p.parseTraits(context.Background(), tt.args.workload, tt.args.comp, annotations)
tt.wantErr(t, err, fmt.Sprintf("parseTraits(%v, %v)", tt.args.workload, tt.args.comp))
if tt.validateFunc != nil {
assert.True(t, tt.validateFunc(tt.args.workload))

View File

@@ -66,13 +66,13 @@ type Template struct {
// LoadTemplate gets the capability definition from cluster and resolve it.
// It returns a helper struct, Template, which will be used for further
// processing.
func LoadTemplate(ctx context.Context, cli client.Client, capName string, capType types.CapType) (*Template, error) {
func LoadTemplate(ctx context.Context, cli client.Client, capName string, capType types.CapType, annotations map[string]string) (*Template, error) {
ctx = multicluster.WithCluster(ctx, multicluster.Local)
// Application Controller only loads template from ComponentDefinition and TraitDefinition
switch capType {
case types.TypeComponentDefinition, types.TypeWorkload:
cd := new(v1beta1.ComponentDefinition)
err := oamutil.GetCapabilityDefinition(ctx, cli, cd, capName)
err := oamutil.GetCapabilityDefinition(ctx, cli, cd, capName, annotations)
if err != nil {
if kerrors.IsNotFound(err) {
wd := new(v1beta1.WorkloadDefinition)
@@ -108,7 +108,7 @@ func LoadTemplate(ctx context.Context, cli client.Client, capName string, capTyp
case types.TypeTrait:
td := new(v1beta1.TraitDefinition)
err := oamutil.GetCapabilityDefinition(ctx, cli, td, capName)
err := oamutil.GetCapabilityDefinition(ctx, cli, td, capName, annotations)
if err != nil {
return nil, errors.WithMessagef(err, "load template from trait definition [%s] ", capName)
}
@@ -119,7 +119,7 @@ func LoadTemplate(ctx context.Context, cli client.Client, capName string, capTyp
return tmpl, nil
case types.TypePolicy:
d := new(v1beta1.PolicyDefinition)
err := oamutil.GetCapabilityDefinition(ctx, cli, d, capName)
err := oamutil.GetCapabilityDefinition(ctx, cli, d, capName, annotations)
if err != nil {
return nil, errors.WithMessagef(err, "load template from policy definition [%s] ", capName)
}
@@ -130,7 +130,7 @@ func LoadTemplate(ctx context.Context, cli client.Client, capName string, capTyp
return tmpl, nil
case types.TypeWorkflowStep:
d := new(v1beta1.WorkflowStepDefinition)
err := oamutil.GetCapabilityDefinition(ctx, cli, d, capName)
err := oamutil.GetCapabilityDefinition(ctx, cli, d, capName, annotations)
if err != nil {
return nil, errors.WithMessagef(err, "load template from workflow step definition [%s] ", capName)
}
@@ -252,7 +252,7 @@ func verifyRevisionName(capName string, capType types.CapType, apprev *v1beta1.A
// LoadTemplate, but load template from provided ones before loading from
// cluster through LoadTemplate
func DryRunTemplateLoader(defs []*unstructured.Unstructured) TemplateLoaderFn {
return func(ctx context.Context, r client.Client, capName string, capType types.CapType) (*Template, error) {
return func(ctx context.Context, r client.Client, capName string, capType types.CapType, annotations map[string]string) (*Template, error) {
// retrieve provided cap definitions
for _, def := range defs {
if def.GetKind() == v1beta1.ComponentDefinitionKind &&
@@ -282,7 +282,7 @@ func DryRunTemplateLoader(defs []*unstructured.Unstructured) TemplateLoaderFn {
}
// not found in provided cap definitions
// then try to retrieve from cluster
tmpl, err := LoadTemplate(ctx, r, capName, capType)
tmpl, err := LoadTemplate(ctx, r, capName, capType, annotations)
if err != nil {
return nil, errors.WithMessagef(err, "cannot load template %q from cluster and provided ones", capName)
}

View File

@@ -111,8 +111,8 @@ spec:
return nil
},
}
temp, err := LoadTemplate(context.TODO(), &tclient, "worker", types.TypeComponentDefinition)
var annotations = make(map[string]string)
temp, err := LoadTemplate(context.TODO(), &tclient, "worker", types.TypeComponentDefinition, annotations)
if err != nil {
t.Error(err)
@@ -219,8 +219,8 @@ spec:
return nil
},
}
temp, err := LoadTemplate(context.TODO(), &tclient, "ingress", types.TypeTrait)
var annotations = make(map[string]string)
temp, err := LoadTemplate(context.TODO(), &tclient, "ingress", types.TypeTrait, annotations)
if err != nil {
t.Error(err)
@@ -362,8 +362,9 @@ spec:
TraitDefinition: traitDef,
}
var annotations = make(map[string]string)
dryRunLoadTemplate := DryRunTemplateLoader([]*unstructured.Unstructured{unstrctCompDef, unstrctTraitDef})
compTmpl, err := dryRunLoadTemplate(nil, nil, "myworker", types.TypeComponentDefinition)
compTmpl, err := dryRunLoadTemplate(nil, nil, "myworker", types.TypeComponentDefinition, annotations)
if err != nil {
t.Error("failed load template of component defintion", err)
}
@@ -371,7 +372,7 @@ spec:
t.Fatal("failed load template of component defintion", diff)
}
traitTmpl, err := dryRunLoadTemplate(nil, nil, "myingress", types.TypeTrait)
traitTmpl, err := dryRunLoadTemplate(nil, nil, "myingress", types.TypeTrait, annotations)
if err != nil {
t.Error("failed load template of component defintion", err)
}

View File

@@ -58,17 +58,17 @@ var _ = Describe("Test dispatch stage", func() {
},
},
}
stage, err := getTraitDispatchStage(k8sClient, "kruise-rollout", &appRev)
var annotations = make(map[string]string)
stage, err := getTraitDispatchStage(k8sClient, "kruise-rollout", &appRev, annotations)
Expect(err).Should(BeNil())
Expect(stage).Should(BeEquivalentTo(PreDispatch))
stage, err = getTraitDispatchStage(k8sClient, "gateway", &appRev)
stage, err = getTraitDispatchStage(k8sClient, "gateway", &appRev, annotations)
Expect(err).Should(BeNil())
Expect(stage).Should(BeEquivalentTo(PostDispatch))
stage, err = getTraitDispatchStage(k8sClient, "hpa", &appRev)
stage, err = getTraitDispatchStage(k8sClient, "hpa", &appRev, annotations)
Expect(err).Should(BeNil())
Expect(stage).Should(BeEquivalentTo(DefaultDispatch))
stage, err = getTraitDispatchStage(k8sClient, "not-exist", &appRev)
stage, err = getTraitDispatchStage(k8sClient, "not-exist", &appRev, annotations)
Expect(err).ShouldNot(BeNil())
Expect(stage).Should(BeEquivalentTo(DefaultDispatch))
})

View File

@@ -112,7 +112,7 @@ type manifestDispatcher struct {
healthCheck func(ctx context.Context, c *appfile.Component, appRev *v1beta1.ApplicationRevision) (bool, error)
}
func (h *AppHandler) generateDispatcher(appRev *v1beta1.ApplicationRevision, readyWorkload *unstructured.Unstructured, readyTraits []*unstructured.Unstructured, overrideNamespace string) ([]*manifestDispatcher, error) {
func (h *AppHandler) generateDispatcher(appRev *v1beta1.ApplicationRevision, readyWorkload *unstructured.Unstructured, readyTraits []*unstructured.Unstructured, overrideNamespace string, annotations map[string]string) ([]*manifestDispatcher, error) {
dispatcherGenerator := func(options DispatchOptions) *manifestDispatcher {
assembleManifestFn := func(skipApplyWorkload bool) (bool, []*unstructured.Unstructured) {
manifests := options.Traits
@@ -138,7 +138,13 @@ func (h *AppHandler) generateDispatcher(appRev *v1beta1.ApplicationRevision, rea
}
dispatcher.run = func(ctx context.Context, comp *appfile.Component, appRev *v1beta1.ApplicationRevision, clusterName string) (bool, error) {
skipWorkload, dispatchManifests := assembleManifestFn(comp.SkipApplyWorkload)
if isHealth, err := dispatcher.healthCheck(ctx, comp, appRev); !isHealth || err != nil {
var isAutoUpdateEnabled bool
if annotations[oam.AnnotationAutoUpdate] == "true" {
isAutoUpdateEnabled = true
}
if isHealth, err := dispatcher.healthCheck(ctx, comp, appRev); !isHealth || err != nil || (!comp.SkipApplyWorkload && isAutoUpdateEnabled) {
if err := h.Dispatch(ctx, h.Client, clusterName, common.WorkflowResourceCreator, dispatchManifests...); err != nil {
return false, errors.WithMessage(err, "Dispatch")
}
@@ -179,7 +185,7 @@ func (h *AppHandler) generateDispatcher(appRev *v1beta1.ApplicationRevision, rea
traitType = splitName
}
}
stageType, err = getTraitDispatchStage(h.Client, traitType, appRev)
stageType, err = getTraitDispatchStage(h.Client, traitType, appRev, annotations)
if err != nil {
return nil, err
}
@@ -210,11 +216,11 @@ func (h *AppHandler) generateDispatcher(appRev *v1beta1.ApplicationRevision, rea
return manifestDispatchers, nil
}
func getTraitDispatchStage(client client.Client, traitType string, appRev *v1beta1.ApplicationRevision) (StageType, error) {
func getTraitDispatchStage(client client.Client, traitType string, appRev *v1beta1.ApplicationRevision, annotations map[string]string) (StageType, error) {
trait, ok := appRev.Spec.TraitDefinitions[traitType]
if !ok {
trait = &v1beta1.TraitDefinition{}
err := oamutil.GetCapabilityDefinition(context.Background(), client, trait, traitType)
err := oamutil.GetCapabilityDefinition(context.Background(), client, trait, traitType, annotations)
if err != nil {
return DefaultDispatch, err
}

View File

@@ -372,7 +372,7 @@ func (h *AppHandler) applyComponentFunc(appParser *appfile.Parser, af *appfile.A
isHealth := true
if utilfeature.DefaultMutableFeatureGate.Enabled(features.MultiStageComponentApply) {
manifestDispatchers, err := h.generateDispatcher(appRev, readyWorkload, readyTraits, overrideNamespace)
manifestDispatchers, err := h.generateDispatcher(appRev, readyWorkload, readyTraits, overrideNamespace, af.AppAnnotations)
if err != nil {
return nil, nil, false, errors.WithMessage(err, "generateDispatcher")
}

View File

@@ -194,6 +194,18 @@ var _ = Describe("Test DefinitionRevision created by ComponentDefinition", func(
Expect(defRev1.Spec.RevisionHash).Should(Equal(defRev2.Spec.RevisionHash))
})
It("Test ComponentDefinition with name specified in spec.version, Should create definitaion with specified name", func() {
cd := cdWithNoTemplate.DeepCopy()
cd.Name = "test-cd-with-custom-version"
cd.Spec.Version = "1.3.0"
cd.Spec.Schematic.CUE.Template = fmt.Sprintf(cdTemplate, "test-defrev")
defRev, _, err := coredef.GenerateDefinitionRevision(ctx, r.Client, cd)
Expect(err).Should(BeNil())
Expect(defRev.Name).Should(Equal("test-cd-with-custom-version-v1.3.0"))
})
It("Test only update ComponentDefinition Labels, Shouldn't create new revision", func() {
cd := cdWithNoTemplate.DeepCopy()
cdName := "test-cd"
@@ -258,6 +270,29 @@ var _ = Describe("Test DefinitionRevision created by ComponentDefinition", func(
By("check the DefinitionRevision's RevisionNum")
Expect(cdRev.Spec.Revision).Should(Equal(int64(1)))
})
It("Test specified DefinitionRevision name in spec.version", func() {
cdName := "test-specified-defrev1-name"
req := reconcile.Request{NamespacedName: client.ObjectKey{Name: cdName, Namespace: namespace}}
cd := cdWithNoTemplate.DeepCopy()
cd.Name = cdName
cd.Spec.Schematic.CUE.Template = fmt.Sprintf(cdTemplate, "test")
cd.Spec.Version = "1.1.3"
By("create componentDefinition")
Expect(k8sClient.Create(ctx, cd)).Should(SatisfyAll(BeNil()))
testutil.ReconcileRetry(&r, req)
By("check whether DefinitionRevision is created")
cdRevName := fmt.Sprintf("%s-v1.1.3", cdName)
var cdRev v1beta1.DefinitionRevision
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: cdRevName}, &cdRev)
}, 10*time.Second, time.Second).Should(BeNil())
By("check the DefinitionRevision's RevisionNum")
Expect(cdRev.Spec.Revision).Should(Equal(int64(1)))
})
})
Context("Test ComponentDefinition Controller clean up", func() {
@@ -381,7 +416,7 @@ var _ = Describe("Test DefinitionRevision created by ComponentDefinition", func(
}, time.Second*30, time.Microsecond*300).Should(BeNil())
})
It("Test clean up definitionRevision contains definitionRevision with custom name", func() {
It("Test clean up definitionRevision contains definitionRevision with custom name using annotation", func() {
var revKey client.ObjectKey
var defRev v1beta1.DefinitionRevision
revisionNames := []string{"1.3.1", "", "1.3.3", "", "prod"}

View File

@@ -32,6 +32,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
coredef "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1beta1/core"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/testutil"
"github.com/oam-dev/kubevela/pkg/oam/util"
@@ -128,6 +129,17 @@ var _ = Describe("Test DefinitionRevision created by PolicyDefinition", func() {
newRevKey := client.ObjectKey{Namespace: namespace, Name: newDefRevName}
Expect(k8sClient.Get(ctx, newRevKey, &defRev)).Should(HaveOccurred())
})
It("Test Policy Definition with name specified in spec.version, Should create definitaion with specified name", func() {
def := defWithNoTemplate.DeepCopy()
def.Name = "test-policy-def-custom-version"
def.Spec.Version = "1.3.0"
def.Spec.Schematic.CUE.Template = fmt.Sprintf(defTemplate, "test-defrev")
defRev, _, err := coredef.GenerateDefinitionRevision(ctx, r.Client, def)
Expect(err).Should(BeNil())
Expect(defRev.Name).Should(Equal("test-policy-def-custom-version-v1.3.0"))
})
})
Context("Test PolicyDefinition Controller clean up", func() {

View File

@@ -22,6 +22,7 @@ import (
"sort"
"strings"
"github.com/Masterminds/semver"
"github.com/crossplane/crossplane-runtime/pkg/event"
"github.com/pkg/errors"
apiequality "k8s.io/apimachinery/pkg/api/equality"
@@ -44,12 +45,20 @@ import (
// GenerateDefinitionRevision will generate a definition revision the generated revision
// will be compare with the last revision to see if there's any difference.
func GenerateDefinitionRevision(ctx context.Context, cli client.Client, def runtime.Object) (*v1beta1.DefinitionRevision, bool, error) {
isNamedRev, defRevNamespacedName, err := isNamedRevision(def)
isSpecVersion, defRevNamespacedName, err := isSpecVersionRevision(def)
if err != nil {
return nil, false, err
}
if isSpecVersion {
return generateDefinitionRevision(ctx, cli, def, defRevNamespacedName)
}
isNamedRev, defRevNamespacedName, err := isNameAnnotationRevision(def)
if err != nil {
return nil, false, err
}
if isNamedRev {
return generateNamedDefinitionRevision(ctx, cli, def, defRevNamespacedName)
return generateDefinitionRevision(ctx, cli, def, defRevNamespacedName)
}
defRev, lastRevision, err := GatherRevisionInfo(def)
@@ -68,7 +77,9 @@ func GenerateDefinitionRevision(ctx context.Context, cli client.Client, def runt
return defRev, isNewRev, nil
}
func isNamedRevision(def runtime.Object) (bool, types.NamespacedName, error) {
// isNameAnnotationRevision is for Definition Version specified in the
// Definition's "definitionrevision.oam.dev/name" annotation.
func isNameAnnotationRevision(def runtime.Object) (bool, types.NamespacedName, error) {
defMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(def)
if err != nil {
return false, types.NamespacedName{}, err
@@ -86,7 +97,42 @@ func isNamedRevision(def runtime.Object) (bool, types.NamespacedName, error) {
return true, types.NamespacedName{Name: defRevName, Namespace: defNs}, nil
}
func generateNamedDefinitionRevision(ctx context.Context, cli client.Client, def runtime.Object, defRevNamespacedName types.NamespacedName) (*v1beta1.DefinitionRevision, bool, error) {
// isSpecVersionRevision is for Definition Version specified in the Definition spec.Version
func isSpecVersionRevision(def runtime.Object) (bool, types.NamespacedName, error) {
var definitionVersion, definitionNamespace, definitionName string
switch definition := def.(type) {
case *v1beta1.ComponentDefinition:
definitionVersion = definition.Spec.Version
definitionNamespace = definition.Namespace
definitionName = definition.Name
case *v1beta1.TraitDefinition:
definitionVersion = definition.Spec.Version
definitionNamespace = definition.Namespace
definitionName = definition.Name
case *v1beta1.PolicyDefinition:
definitionVersion = definition.Spec.Version
definitionNamespace = definition.Namespace
definitionName = definition.Name
case *v1beta1.WorkflowStepDefinition:
definitionVersion = definition.Spec.Version
definitionNamespace = definition.Namespace
definitionName = definition.Name
}
if definitionVersion == "" {
return false, types.NamespacedName{}, nil
}
semVersion, err := semver.NewVersion(definitionVersion)
if err != nil {
return false, types.NamespacedName{}, err
}
definitionRevisionName := ConstructDefinitionRevisionName(definitionName, semVersion.String())
return true, types.NamespacedName{Name: definitionRevisionName, Namespace: definitionNamespace}, nil
}
func generateDefinitionRevision(ctx context.Context, cli client.Client, def runtime.Object, defRevNamespacedName types.NamespacedName) (*v1beta1.DefinitionRevision, bool, error) {
oldDefRev := new(v1beta1.DefinitionRevision)
// definitionRevision is immutable, if the requested definitionRevision already exists, return directly.
@@ -230,24 +276,27 @@ func DeepEqualDefRevision(old, new *v1beta1.DefinitionRevision) bool {
return true
}
func getDefNextRevision(defRev *v1beta1.DefinitionRevision, lastRevision *common.Revision) (string, int64) {
func getDefNextRevision(definitionRevision *v1beta1.DefinitionRevision, lastRevision *common.Revision) (string, int64) {
var nextRevision int64 = 1
var definitionRevisionName string
if lastRevision != nil {
nextRevision = lastRevision.Revision + 1
}
var name string
switch defRev.Spec.DefinitionType {
switch definitionRevision.Spec.DefinitionType {
case common.ComponentType:
name = defRev.Spec.ComponentDefinition.Name
name = definitionRevision.Spec.ComponentDefinition.Name
case common.TraitType:
name = defRev.Spec.TraitDefinition.Name
name = definitionRevision.Spec.TraitDefinition.Name
case common.PolicyType:
name = defRev.Spec.PolicyDefinition.Name
name = definitionRevision.Spec.PolicyDefinition.Name
case common.WorkflowStepType:
name = defRev.Spec.WorkflowStepDefinition.Name
name = definitionRevision.Spec.WorkflowStepDefinition.Name
}
defRevName := strings.Join([]string{name, fmt.Sprintf("v%d", nextRevision)}, "-")
return defRevName, nextRevision
definitionRevisionName = strings.Join([]string{name, fmt.Sprintf("v%v", nextRevision)}, "-")
return definitionRevisionName, nextRevision
}
// ConstructDefinitionRevisionName construct the name of DefinitionRevision.

View File

@@ -32,6 +32,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
coredef "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1beta1/core"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/testutil"
"github.com/oam-dev/kubevela/pkg/oam/util"
@@ -128,6 +129,17 @@ var _ = Describe("Test DefinitionRevision created by TraitDefinition", func() {
newRevKey := client.ObjectKey{Namespace: namespace, Name: newDefRevName}
Expect(k8sClient.Get(ctx, newRevKey, &defRev)).Should(HaveOccurred())
})
It("Test Trait Definition with name specified in spec.version, Should create definitaion with specified name", func() {
td := tdWithNoTemplate.DeepCopy()
td.Name = "test-trait-def-custom-version"
td.Spec.Version = "1.3.0"
td.Spec.Schematic.CUE.Template = fmt.Sprintf(tdTemplate, fmt.Sprintf("test-v%d", 1))
defRev, _, err := coredef.GenerateDefinitionRevision(ctx, r.Client, td)
Expect(err).Should(BeNil())
Expect(defRev.Name).Should(Equal("test-trait-def-custom-version-v1.3.0"))
})
})
Context("Test TraitDefinition Controller clean up", func() {

View File

@@ -32,6 +32,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
coredef "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1beta1/core"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/testutil"
"github.com/oam-dev/kubevela/pkg/oam/util"
@@ -128,6 +129,16 @@ var _ = Describe("Test DefinitionRevision created by WorkflowStepDefinition", fu
newRevKey := client.ObjectKey{Namespace: namespace, Name: newDefRevName}
Expect(k8sClient.Get(ctx, newRevKey, &defRev)).Should(HaveOccurred())
})
It("Test WorkflowStep Definition with name specified in spec.version, Should create definitaion with specified name", func() {
def := defWithNoTemplate.DeepCopy()
def.Name = "test-workflow-def-custom-version"
def.Spec.Version = "1.3.0"
def.Spec.Schematic.CUE.Template = fmt.Sprintf(defTemplate, fmt.Sprintf("test-v%d", 1))
defRev, _, err := coredef.GenerateDefinitionRevision(ctx, r.Client, def)
Expect(err).Should(BeNil())
Expect(defRev.Name).Should(Equal("test-workflow-def-custom-version-v1.3.0"))
})
})
Context("Test WorkflowStepDefinition Controller clean up", func() {

View File

@@ -22,6 +22,7 @@ import (
"fmt"
"hash"
"hash/fnv"
"sort"
"strconv"
"strings"
@@ -39,6 +40,8 @@ import (
"k8s.io/apimachinery/pkg/util/validation"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/Masterminds/semver"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/condition"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
@@ -122,6 +125,14 @@ const (
XDefinitionNamespace
)
// DefinitionKindToNameLabel records DefinitionRevision types and labels to search its name
var DefinitionKindToNameLabel = map[common.DefinitionType]string{
common.ComponentType: oam.LabelComponentDefinitionName,
common.TraitType: oam.LabelTraitDefinitionName,
common.PolicyType: oam.LabelPolicyDefinitionName,
common.WorkflowStepType: oam.LabelWorkflowStepDefinitionName,
}
// A ConditionedObject is an Object type with condition field
type ConditionedObject interface {
client.Object
@@ -192,6 +203,7 @@ func GetDefinition(ctx context.Context, cli client.Reader, definition client.Obj
}
return err
}
return nil
}
@@ -213,12 +225,17 @@ func GetDefinitionFromNamespace(ctx context.Context, cli client.Reader, definiti
// GetCapabilityDefinition can get different versions of ComponentDefinition/TraitDefinition
func GetCapabilityDefinition(ctx context.Context, cli client.Reader, definition client.Object,
definitionName string) error {
isLatestRevision, defRev, err := fetchDefinitionRev(ctx, cli, definitionName)
definitionName string, annotations map[string]string) error {
definitionType, err := getDefinitionType(definition)
if err != nil {
return err
}
isLatestRevision, defRev, err := fetchDefinitionRevision(ctx, cli, definitionName, definitionType, annotations)
if err != nil {
return err
}
if isLatestRevision {
return GetDefinition(ctx, cli, definition, definitionName)
}
switch def := definition.(type) {
@@ -235,7 +252,24 @@ func GetCapabilityDefinition(ctx context.Context, cli client.Reader, definition
return nil
}
func fetchDefinitionRev(ctx context.Context, cli client.Reader, definitionName string) (bool, *v1beta1.DefinitionRevision, error) {
func getDefinitionType(definition client.Object) (common.DefinitionType, error) {
var definitionType common.DefinitionType
switch definition.(type) {
case *v1beta1.ComponentDefinition:
definitionType = common.ComponentType
case *v1beta1.TraitDefinition:
definitionType = common.TraitType
case *v1beta1.PolicyDefinition:
definitionType = common.PolicyType
case *v1beta1.WorkflowStepDefinition:
definitionType = common.WorkflowStepType
default:
return definitionType, fmt.Errorf("invalid definition type for %v", definition.GetName())
}
return definitionType, nil
}
func fetchDefinitionRevision(ctx context.Context, cli client.Reader, definitionName string, definitionType common.DefinitionType, annotations map[string]string) (bool, *v1beta1.DefinitionRevision, error) {
// if the component's type doesn't contain '@' means user want to use the latest Definition.
if !strings.Contains(definitionName, "@") {
return true, nil, nil
@@ -245,13 +279,93 @@ func fetchDefinitionRev(ctx context.Context, cli client.Reader, definitionName s
if err != nil {
return false, nil, err
}
defName := strings.Split(definitionName, "@")[0]
autoUpdate, ok := annotations[oam.AnnotationAutoUpdate]
if ok && autoUpdate == "true" {
latestRevisionName, err := GetLatestDefinitionRevisionName(ctx, cli.(client.Client), defName, defRevName, definitionType)
if err != nil {
return false, nil, err
}
defRevName = latestRevisionName
}
defRev := new(v1beta1.DefinitionRevision)
if err := GetDefinition(ctx, cli, defRev, defRevName); err != nil {
return false, nil, err
}
return false, defRev, nil
}
// GetLatestDefinitionRevisionName returns the latest definition revision name in specified version range.
func GetLatestDefinitionRevisionName(ctx context.Context, cli client.Client, definitionName, revisionName string, definitionType common.DefinitionType) (string, error) {
for _, ns := range []string{GetDefinitionNamespaceWithCtx(ctx), oam.SystemDefinitionNamespace} {
revisionListForDefinition, err := fetchAllRevisionsForDefinitionName(ctx, cli, ns, definitionName, definitionType)
if err != nil {
return "", err
}
matchedDefinitionRevision, err := getMatchingDefinitionRevision(revisionName, definitionName, revisionListForDefinition, definitionType)
if err == nil && matchedDefinitionRevision != "" {
return matchedDefinitionRevision, nil
}
}
return "", fmt.Errorf("error finding definition revision for Name: %v, Type: %v", definitionName, definitionType)
}
func fetchAllRevisionsForDefinitionName(ctx context.Context, cli client.Client, ns, definitionName string, definitionType common.DefinitionType) (*v1beta1.DefinitionRevisionList, error) {
var listOptions []client.ListOption
listOptions = append(listOptions, client.InNamespace(ns),
client.MatchingLabels{
DefinitionKindToNameLabel[definitionType]: definitionName,
})
revisionList := v1beta1.DefinitionRevisionList{}
revisionList.SetGroupVersionKind(schema.GroupVersionKind{
Group: v1beta1.Group,
Version: v1beta1.Version,
Kind: v1beta1.DefinitionRevisionKind,
})
err := cli.List(ctx, &revisionList, listOptions...)
return &revisionList, err
}
func getMatchingDefinitionRevision(exactRevisionName, definitionName string, revisionList *v1beta1.DefinitionRevisionList, definitionType common.DefinitionType) (string, error) {
var definitionVersions []*semver.Version
revisionPrefix := exactRevisionName + "."
orignalVersions := make(map[string]string)
for _, revision := range revisionList.Items {
if definitionType != "" && definitionType != revision.Spec.DefinitionType {
continue
}
if revision.Name == exactRevisionName {
return exactRevisionName, nil
}
// Only get the revisions that the user expects
if strings.HasPrefix(revision.Name, revisionPrefix) {
version := strings.Split(revision.Name, definitionName+"-")[1]
v, err := semver.NewVersion(version)
orignalVersions[v.String()] = version
if err != nil {
return "", err
}
definitionVersions = append(definitionVersions, v)
}
}
if len(definitionVersions) == 0 {
return "", nil
}
sort.Sort(semver.Collection(definitionVersions))
latestVersion := definitionVersions[len(definitionVersions)-1]
return definitionName + "-" + orignalVersions[latestVersion.String()], nil
}
// ConvertDefinitionRevName can help convert definition type defined in Application to DefinitionRevision Name
// e.g., worker@v1.3.1 will be convert to worker-v1.3.1
func ConvertDefinitionRevName(definitionName string) (string, error) {

View File

@@ -980,3 +980,339 @@ func TestXDefinitionNamespaceInCtx(t *testing.T) {
assert.Equal(t, tc.expectedNamespace, ns)
}
}
func TestGetLatestDefinitionRevisionName(t *testing.T) {
componetListCli := test.MockClient{MockList: func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
defRevisionList := getComponentDefRevisionList()
defRevisionList.DeepCopyInto(list.(*v1beta1.DefinitionRevisionList))
return nil
}}
traitListCli := test.MockClient{MockList: func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
defRevisionList := getTraitDefRevisionList()
defRevisionList.DeepCopyInto(list.(*v1beta1.DefinitionRevisionList))
return nil
}}
testcases := []struct {
name string
inputRevisionName string
definitionName string
definitionType string
expectedDefRevisionName string
revisionList *v1beta1.DefinitionRevisionList
client client.Client
err error
}{
{
name: "Complete Component version name specified",
inputRevisionName: "configmap-component-v1.3.0",
definitionName: "configmap-component",
definitionType: "Component",
expectedDefRevisionName: "configmap-component-v1.3.0",
client: &componetListCli,
err: nil,
}, {
name: "Partial Component version name specified",
inputRevisionName: "configmap-component-v1.2",
definitionName: "configmap-component",
definitionType: "Component",
expectedDefRevisionName: "configmap-component-v1.2.4",
client: &componetListCli,
err: nil,
}, {
name: "Component version not present",
inputRevisionName: "configmap-component-v1.6",
definitionName: "configmap-component",
definitionType: "Component",
expectedDefRevisionName: "",
client: &componetListCli,
err: fmt.Errorf("error finding definition revision for Name: configmap-component, Type: Component"),
}, {
name: "Complete Trait version name specified",
inputRevisionName: "scaler-trait-v1.3.0",
definitionName: "scaler-trait",
definitionType: "Trait",
expectedDefRevisionName: "scaler-trait-v1.3.0",
client: &traitListCli,
err: nil,
},
{
name: "Partial Trait version name specified",
inputRevisionName: "scaler-trait-v1.2",
definitionName: "scaler-trait",
definitionType: "Trait",
expectedDefRevisionName: "scaler-trait-v1.2.4",
client: &traitListCli,
err: nil,
}, {
name: "Trait version name not present",
inputRevisionName: "scaler-trait-v1.5",
definitionName: "scaler-trait",
definitionType: "Trait",
expectedDefRevisionName: "",
client: &traitListCli,
err: fmt.Errorf("error finding definition revision for Name: scaler-trait, Type: Trait"),
},
}
ctx := context.Background()
for _, tc := range testcases {
defRevisionName, err := util.GetLatestDefinitionRevisionName(ctx, tc.client, tc.definitionName, tc.inputRevisionName, common.DefinitionType(tc.definitionType))
assert.Equal(t, defRevisionName, tc.expectedDefRevisionName)
assert.Equal(t, err, tc.err)
}
}
func TestGetCapabilityDefinitionComponentAutoUpdateEnabled(t *testing.T) {
cli := test.MockClient{MockList: func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
defRevisionList := getComponentDefRevisionList()
defRevisionList.DeepCopyInto(list.(*v1beta1.DefinitionRevisionList))
return nil
}, MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
componentDefinitionRevision.DeepCopyInto(obj.(*v1beta1.DefinitionRevision))
return nil
}}
annotations := make(map[string]string)
annotations[oam.AnnotationAutoUpdate] = "true"
definitionName := "configmap-component@v1"
ctx := context.Background()
definition := new(v1beta1.ComponentDefinition)
err := util.GetCapabilityDefinition(ctx, &cli, definition, definitionName, annotations)
assert.Equal(t, err, nil)
assert.Equal(t, definition.Spec.Version, "1.0.0")
}
func TestGetCapabilityDefinitionOfTraitAutoUpdateEnabled(t *testing.T) {
cli := test.MockClient{MockList: func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
defRevisionList := getTraitDefRevisionList()
defRevisionList.DeepCopyInto(list.(*v1beta1.DefinitionRevisionList))
return nil
}, MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
traitDefinitionRevision.DeepCopyInto(obj.(*v1beta1.DefinitionRevision))
return nil
}}
annotations := make(map[string]string)
annotations[oam.AnnotationAutoUpdate] = "true"
definitionName := "scaler-trait@v1"
ctx := context.Background()
definition := new(v1beta1.TraitDefinition)
err := util.GetCapabilityDefinition(ctx, &cli, definition, definitionName, annotations)
assert.Equal(t, err, nil)
assert.Equal(t, definition.Spec.Version, "1.0.0")
}
func TestGetCapabilityDefinitionComponentAutoUpdateDisabled(t *testing.T) {
cli := test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
componentDefinitionRevision.Spec.ComponentDefinition.DeepCopyInto(obj.(*v1beta1.ComponentDefinition))
return nil
}}
annotations := make(map[string]string)
annotations[oam.AnnotationAutoUpdate] = "false"
definitionName := "configmap-component"
ctx := context.Background()
definition := new(v1beta1.ComponentDefinition)
err := util.GetCapabilityDefinition(ctx, &cli, definition, definitionName, annotations)
assert.Equal(t, err, nil)
assert.Equal(t, definition.Spec.Version, "1.0.0")
}
func TestGetCapabilityDefinitionPolicyAutoUpdateDisabled(t *testing.T) {
cli := test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
policydefinition := &v1beta1.PolicyDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mock-policy-definition",
Namespace: "vela-system",
},
}
policydefinition.DeepCopyInto(obj.(*v1beta1.PolicyDefinition))
return nil
}}
annotations := make(map[string]string)
annotations[oam.AnnotationAutoUpdate] = "false"
definitionName := "mock-policy-definition"
ctx := context.Background()
definition := new(v1beta1.PolicyDefinition)
err := util.GetCapabilityDefinition(ctx, &cli, definition, definitionName, annotations)
assert.Equal(t, err, nil)
}
func TestGetCapabilityDefinitionWorkflowStepAutoUpdateDisabled(t *testing.T) {
cli := test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
workflowStepDefinition := &v1beta1.WorkflowStepDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mock-workflow-definition",
Namespace: "vela-system",
},
}
workflowStepDefinition.DeepCopyInto(obj.(*v1beta1.WorkflowStepDefinition))
return nil
}}
annotations := make(map[string]string)
annotations[oam.AnnotationAutoUpdate] = "false"
definitionName := "mock-workflow-definition"
ctx := context.Background()
definition := new(v1beta1.WorkflowStepDefinition)
err := util.GetCapabilityDefinition(ctx, &cli, definition, definitionName, annotations)
assert.Equal(t, err, nil)
}
func TestGetCapabilityDefinitionInvalidDefinition(t *testing.T) {
cli := test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
workflowStepDefinition := &v1beta1.WorkloadDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mock-workload-definition",
Namespace: "vela-system",
},
}
workflowStepDefinition.DeepCopyInto(obj.(*v1beta1.WorkloadDefinition))
return nil
}}
annotations := make(map[string]string)
annotations[oam.AnnotationAutoUpdate] = "false"
definitionName := "mock-workload-definition"
ctx := context.Background()
definition := new(v1beta1.WorkloadDefinition)
definition.ObjectMeta.Name = "mock-workload-definition"
err := util.GetCapabilityDefinition(ctx, &cli, definition, definitionName, annotations)
assert.Equal(t, err.Error(), "invalid definition type for mock-workload-definition")
}
func TestGetCapabilityDefinitionOfTraitAutoUpdateDisabled(t *testing.T) {
cli := test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
o := new(v1beta1.TraitDefinition)
*o = traitDefinitionRevision.Spec.TraitDefinition
return nil
}}
annotations := make(map[string]string)
annotations[oam.AnnotationAutoUpdate] = "false"
definitionName := "scaler-trait"
ctx := context.Background()
definition := new(v1beta1.TraitDefinition)
err := util.GetCapabilityDefinition(ctx, &cli, definition, definitionName, annotations)
assert.Equal(t, err, nil)
}
func getComponentDefRevisionList() v1beta1.DefinitionRevisionList {
compDefRevision1 := componentDefinitionRevision.DeepCopy()
compDefRevision1.Spec.ComponentDefinition.Spec.Version = "1.2.0"
compDefRevision1.Name = "configmap-component-v1.2.0"
compDefRevision2 := componentDefinitionRevision.DeepCopy()
compDefRevision2.Spec.ComponentDefinition.Spec.Version = "1.2.4"
compDefRevision2.Name = "configmap-component-v1.2.4"
compDefRevision3 := componentDefinitionRevision.DeepCopy()
compDefRevision3.Spec.ComponentDefinition.Spec.Version = "1.3.0"
compDefRevision3.Name = "configmap-component-v1.3.0"
compRevisionList := v1beta1.DefinitionRevisionList{
TypeMeta: metav1.TypeMeta{
Kind: "DefinitionRevision",
APIVersion: "core.oam.dev/v1beta1",
},
Items: []v1beta1.DefinitionRevision{
*compDefRevision1, *compDefRevision2, *compDefRevision3,
},
}
return compRevisionList
}
func getTraitDefRevisionList() v1beta1.DefinitionRevisionList {
traitDefRevision1 := traitDefinitionRevision.DeepCopy()
traitDefRevision1.Spec.TraitDefinition.Spec.Version = "1.2.0"
traitDefRevision1.Name = "scaler-trait-v1.2.0"
traitDefRevision2 := traitDefinitionRevision.DeepCopy()
traitDefRevision2.Spec.TraitDefinition.Spec.Version = "1.2.4"
traitDefRevision2.Name = "scaler-trait-v1.2.4"
traitDefRevision3 := traitDefinitionRevision.DeepCopy()
traitDefRevision3.Spec.TraitDefinition.Spec.Version = "1.3.0"
traitDefRevision3.Name = "scaler-trait-v1.3.0"
traitRevisionList := v1beta1.DefinitionRevisionList{
TypeMeta: metav1.TypeMeta{
Kind: "DefinitionRevision",
APIVersion: "core.oam.dev/v1beta1",
},
Items: []v1beta1.DefinitionRevision{
*traitDefRevision1, *traitDefRevision2, *traitDefRevision3,
},
}
return traitRevisionList
}
var traitDefinitionRevision = v1beta1.DefinitionRevision{
TypeMeta: metav1.TypeMeta{
Kind: "DefinitionRevision",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "scaler-trait",
Labels: map[string]string{
"trait.oam.dev/name": "scaler-trait",
},
},
Spec: v1beta1.DefinitionRevisionSpec{
Revision: 1,
RevisionHash: "5ceecfbe58dde83a",
DefinitionType: "Trait",
TraitDefinition: v1beta1.TraitDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "TraitDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "scaler-trait",
},
Spec: v1beta1.TraitDefinitionSpec{
Version: "1.0.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: "",
},
},
},
},
},
}
// debug unit tests
var componentDefinitionRevision = v1beta1.DefinitionRevision{
TypeMeta: metav1.TypeMeta{
Kind: "DefinitionRevision",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "configmap-component",
Labels: map[string]string{
"componentdefinition.oam.dev/name": "configmap-component",
},
},
Spec: v1beta1.DefinitionRevisionSpec{
Revision: 1,
RevisionHash: "5ceecfbe58dde83a",
DefinitionType: "Component",
ComponentDefinition: v1beta1.ComponentDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "ComponentDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "configmap-component",
},
Spec: v1beta1.ComponentDefinitionSpec{
Version: "1.0.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: "",
},
},
},
},
},
}

View File

@@ -25,6 +25,7 @@ import (
"github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev/v1beta1/componentdefinition"
"github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev/v1beta1/policydefinition"
"github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev/v1beta1/traitdefinition"
"github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev/v1beta1/workflowstepdefinition"
)
// Register will be called in main and register all validation handlers
@@ -35,6 +36,7 @@ func Register(mgr manager.Manager, args controller.Args) {
componentdefinition.RegisterValidatingHandler(mgr)
traitdefinition.RegisterValidatingHandler(mgr, args)
policydefinition.RegisterValidatingHandler(mgr)
workflowstepdefinition.RegisterValidatingHandler(mgr)
server := mgr.GetWebhookServer()
server.Register("/convert", conversion.NewWebhookHandler(mgr.GetScheme()))
}

View File

@@ -66,6 +66,10 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
if err := h.Decoder.Decode(req, app); err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
if req.Namespace != "" {
app.Namespace = req.Namespace
}
ctx = util.SetNamespaceInCtx(ctx, app.Namespace)
switch req.Operation {
case admissionv1.Create:

View File

@@ -207,4 +207,52 @@ var _ = Describe("Test Application Validator", func() {
resp := handler.Handle(ctx, req)
Expect(resp.Allowed).Should(BeFalse())
})
It("Test Application with PublishVersion and Autoupdate annotations", func() {
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1alpha2", Resource: "applications"},
Object: runtime.RawExtension{
Raw: []byte(`
{"apiVersion":"core.oam.dev/v1beta1","kind":"Application","metadata":{"name":"workflow-timeout","namespace":"default","annotations":{"app.oam.dev/publishVersion":"v1.0.0","app.oam.dev/autoUpdate":"true"}},"spec":{"components":[{"name":"comp","type":"worker","properties":{"image":"crccheck/hello-world"}}],"workflow":{"steps":[{"name":"group","type":"suspend","timeout":"1s"}]}}}
`),
},
},
}
resp := handler.Handle(ctx, req)
Expect(resp.Allowed).Should(BeFalse())
})
It("Test Application Publishversion Annotation", func() {
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1alpha2", Resource: "applications"},
Object: runtime.RawExtension{
Raw: []byte(`
{"apiVersion":"core.oam.dev/v1beta1","kind":"Application","metadata":{"name":"workflow-timeout","namespace":"default","annotations":{"app.oam.dev/publishVersion":"v1.0.0"}},"spec":{"components":[{"name":"comp","type":"worker","properties":{"image":"crccheck/hello-world"}}],"workflow":{"steps":[{"name":"group","type":"suspend","timeout":"1s"}]}}}
`),
},
},
}
resp := handler.Handle(ctx, req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test Application Autoupdate Annotation", func() {
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1alpha2", Resource: "applications"},
Object: runtime.RawExtension{
Raw: []byte(`
{"apiVersion":"core.oam.dev/v1beta1","kind":"Application","metadata":{"name":"workflow-timeout","namespace":"default","annotations":{"app.oam.dev/autoUpdate":"true"}},"spec":{"components":[{"name":"comp","type":"worker","properties":{"image":"crccheck/hello-world"}}],"workflow":{"steps":[{"name":"group","type":"suspend","timeout":"1s"}]}}}
`),
},
},
}
resp := handler.Handle(ctx, req)
Expect(resp.Allowed).Should(BeTrue())
})
})

View File

@@ -30,6 +30,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/appfile"
"github.com/oam-dev/kubevela/pkg/features"
"github.com/oam-dev/kubevela/pkg/oam"
)
// ValidateWorkflow validates the Application workflow
@@ -107,10 +108,24 @@ func (h *ValidatingHandler) ValidateComponents(ctx context.Context, app *v1beta1
return componentErrs
}
// ValidateAnnotations validates whether the application has both autoupdate and publish version annotations
func (h *ValidatingHandler) ValidateAnnotations(_ context.Context, app *v1beta1.Application) field.ErrorList {
var annotationsErrs field.ErrorList
hasPublishVersion := app.Annotations[oam.AnnotationPublishVersion]
hasAutoUpdate := app.Annotations[oam.AnnotationAutoUpdate]
if hasAutoUpdate == "true" && hasPublishVersion != "" {
annotationsErrs = append(annotationsErrs, field.Invalid(field.NewPath("metadata", "annotations"), app,
"Application has both autoUpdate and publishVersion annotations. Only one can be present"))
}
return annotationsErrs
}
// ValidateCreate validates the Application on creation
func (h *ValidatingHandler) ValidateCreate(ctx context.Context, app *v1beta1.Application) field.ErrorList {
var errs field.ErrorList
errs = append(errs, h.ValidateAnnotations(ctx, app)...)
errs = append(errs, h.ValidateWorkflow(ctx, app)...)
errs = append(errs, h.ValidateComponents(ctx, app)...)
return errs

View File

@@ -46,7 +46,7 @@ type ValidatingHandler struct {
var _ admission.Handler = &ValidatingHandler{}
// Handle validate component definition
// Handle validate ComponentDefinition Spec here
func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
obj := &v1beta1.ComponentDefinition{}
if req.Resource.String() != componentDefGVR.String() {
@@ -71,6 +71,13 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
}
}
if obj.Spec.Version != "" {
err = webhookutils.ValidateSemanticVersion(obj.Spec.Version)
if err != nil {
return admission.Denied(err.Error())
}
}
revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
if len(revisionName) != 0 {
defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
@@ -79,6 +86,12 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
return admission.Denied(err.Error())
}
}
version := obj.Spec.Version
err = webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind)
if err != nil {
return admission.Denied(err.Error())
}
}
return admission.ValidationResponse(true, "")
}

View File

@@ -234,5 +234,172 @@ var _ = Describe("Test ComponentDefinition validating handler", func() {
Expect(resp.Result.Reason).Should(Equal(metav1.StatusReason(http.StatusText(http.StatusForbidden))))
Expect(resp.Result.Message).Should(ContainSubstring("hello: reference \"world\" not found"))
})
It("Test Version field validation passed", func() {
cd := v1beta1.ComponentDefinition{}
cd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
cd.SetName("CorrectCd")
cd.Spec = v1beta1.ComponentDefinitionSpec{
Version: "1.10.0",
Workload: common.WorkloadTypeDescriptor{
Type: "deployments.apps",
Definition: common.WorkloadGVK{
APIVersion: "apps/v1",
Kind: "Deployment",
},
},
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
cdRaw, _ := json.Marshal(cd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: cdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test Version field validation failed", func() {
wrongCd := v1beta1.ComponentDefinition{}
wrongCd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
wrongCd.SetName("wrongCd")
wrongCd.Spec = v1beta1.ComponentDefinitionSpec{
Version: "1.10..0",
Workload: common.WorkloadTypeDescriptor{
Type: "deployments.apps",
Definition: common.WorkloadGVK{
APIVersion: "apps/v1",
Kind: "Deployment",
},
},
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
correctCdRaw, _ := json.Marshal(wrongCd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: correctCdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
Expect(string(resp.Result.Message)).Should(ContainSubstring("Not a valid version"))
})
It("Test ComponentDefintion has both spec.version and revision name annotation", func() {
wrongCd := v1beta1.ComponentDefinition{}
wrongCd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
wrongCd.SetName("wrongCd")
annotations := map[string]string{
"definitionrevision.oam.dev/name": "1.0.0",
}
wrongCd.SetAnnotations(annotations)
wrongCd.SetNamespace("default")
wrongCd.Spec = v1beta1.ComponentDefinitionSpec{
Version: "1.10.0",
Workload: common.WorkloadTypeDescriptor{
Type: "deployments.apps",
Definition: common.WorkloadGVK{
APIVersion: "apps/v1",
Kind: "Deployment",
},
},
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wrongCdRaw, _ := json.Marshal(wrongCd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wrongCdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
Expect(string(resp.Result.Message)).Should(ContainSubstring("Only one can be present"))
})
It("Test ComponentDefintion with spec.version and without revision name annotation", func() {
cd := v1beta1.ComponentDefinition{}
cd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
cd.SetName("cd")
cd.Spec = v1beta1.ComponentDefinitionSpec{
// Version: "1.10.0",
Workload: common.WorkloadTypeDescriptor{
Type: "deployments.apps",
Definition: common.WorkloadGVK{
APIVersion: "apps/v1",
Kind: "Deployment",
},
},
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
cdRaw, _ := json.Marshal(cd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: cdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test ComponentDefintion with revision name annotation and wihout spec.version", func() {
cd := v1beta1.ComponentDefinition{}
cd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
cd.SetName("cd")
annotations := map[string]string{
"definitionrevision.oam.dev/name": "1.0.0",
}
cd.SetAnnotations(annotations)
cd.SetNamespace("default")
cd.Spec = v1beta1.ComponentDefinitionSpec{
Workload: common.WorkloadTypeDescriptor{
Type: "deployments.apps",
Definition: common.WorkloadGVK{
APIVersion: "apps/v1",
Kind: "Deployment",
},
},
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
cdRaw, _ := json.Marshal(cd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: cdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
})
})

View File

@@ -64,6 +64,13 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
}
}
if obj.Spec.Version != "" {
err = webhookutils.ValidateSemanticVersion(obj.Spec.Version)
if err != nil {
return admission.Denied(err.Error())
}
}
revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
if len(revisionName) != 0 {
defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
@@ -72,6 +79,12 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
return admission.Denied(err.Error())
}
}
version := obj.Spec.Version
err = webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind)
if err != nil {
return admission.Denied(err.Error())
}
}
return admission.ValidationResponse(true, "")
}

View File

@@ -19,7 +19,10 @@ package policydefinition
import (
"context"
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
@@ -29,6 +32,9 @@ import (
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
@@ -39,8 +45,10 @@ var decoder *admission.Decoder
var pd v1beta1.PolicyDefinition
var pdRaw []byte
var scheme = runtime.NewScheme()
var testEnv *envtest.Environment
var validCueTemplate string
var inValidCueTemplate string
var cfg *rest.Config
func TestPolicydefinition(t *testing.T) {
RegisterFailHandler(Fail)
@@ -54,19 +62,37 @@ var _ = BeforeSuite(func() {
pd = v1beta1.PolicyDefinition{}
pd.SetGroupVersionKind(v1beta1.PolicyDefinitionGroupVersionKind)
decoder = admission.NewDecoder(scheme)
var err error
var yamlPath string
if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
yamlPath = "../../../../../test/compatibility-test/testdata"
} else {
yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
}
testEnv = &envtest.Environment{
ControlPlaneStartTimeout: time.Minute,
ControlPlaneStopTimeout: time.Minute,
CRDDirectoryPaths: []string{yamlPath},
}
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
})
var _ = Describe("Test PolicyDefinition validating handler", func() {
BeforeEach(func() {
cli, err := client.New(cfg, client.Options{})
Expect(err).Should(BeNil())
reqResource = metav1.GroupVersionResource{
Group: v1beta1.Group,
Version: v1beta1.Version,
Resource: "policydefinitions"}
handler = ValidatingHandler{
Decoder: decoder,
Client: cli,
}
})
It("Test wrong resource of admission request", func() {
@@ -138,5 +164,139 @@ var _ = Describe("Test PolicyDefinition validating handler", func() {
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
})
It("Test Version field validation passed", func() {
pd := v1beta1.PolicyDefinition{}
pd.SetGroupVersionKind(v1beta1.PolicyDefinitionGroupVersionKind)
pd.SetName("CorrectPd")
pd.Spec = v1beta1.PolicyDefinitionSpec{
Version: "1.10.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
pdRaw, _ := json.Marshal(pd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: pdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test Version field validation failed", func() {
wrongPd := v1beta1.PolicyDefinition{}
wrongPd.SetGroupVersionKind(v1beta1.PolicyDefinitionGroupVersionKind)
wrongPd.SetName("WrongPd")
wrongPd.Spec = v1beta1.PolicyDefinitionSpec{
Version: "1.10..0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wrongPdRaw, _ := json.Marshal(wrongPd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wrongPdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
Expect(string(resp.Result.Message)).Should(ContainSubstring("Not a valid version"))
})
It("Test PolicyDefintion has both spec.version and revision name annotation", func() {
wrongPd := v1beta1.PolicyDefinition{}
wrongPd.SetGroupVersionKind(v1beta1.PolicyDefinitionGroupVersionKind)
wrongPd.SetName("wrongPd")
annotations := map[string]string{
"definitionrevision.oam.dev/name": "v1.0.0",
}
wrongPd.SetAnnotations(annotations)
wrongPd.SetNamespace("default")
wrongPd.Spec = v1beta1.PolicyDefinitionSpec{
Version: "1.10.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wrongPdRaw, _ := json.Marshal(wrongPd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wrongPdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
Expect(string(resp.Result.Message)).Should(ContainSubstring("Only one can be present"))
})
It("Test PolicyDefintion with spec.version and without revision name annotation", func() {
pd := v1beta1.PolicyDefinition{}
pd.SetGroupVersionKind(v1beta1.PolicyDefinitionGroupVersionKind)
pd.SetName("pd")
pd.Spec = v1beta1.PolicyDefinitionSpec{
Version: "1.10.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
pdRaw, _ := json.Marshal(pd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: pdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test PolicyDefintion without spec.version and with revision name annotation", func() {
pd := v1beta1.PolicyDefinition{}
pd.SetGroupVersionKind(v1beta1.PolicyDefinitionGroupVersionKind)
pd.SetName("pd")
annotations := map[string]string{
"definitionrevision.oam.dev/name": "v1.0.0",
}
pd.SetAnnotations(annotations)
pd.SetNamespace("default")
pd.Spec = v1beta1.PolicyDefinitionSpec{
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
pdRaw, _ := json.Marshal(pd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: pdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
})
})

View File

@@ -97,6 +97,13 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
}
}
if obj.Spec.Version != "" {
err = webhookutils.ValidateSemanticVersion(obj.Spec.Version)
if err != nil {
return admission.Denied(err.Error())
}
}
revisionName := obj.GetAnnotations()[oam.AnnotationDefinitionRevisionName]
if len(revisionName) != 0 {
defRevName := fmt.Sprintf("%s-v%s", obj.Name, revisionName)
@@ -105,6 +112,12 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
return admission.Denied(err.Error())
}
}
version := obj.Spec.Version
err = webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind)
if err != nil {
return admission.Denied(err.Error())
}
klog.Info("validation passed ", " name: ", obj.Name, " operation: ", string(req.Operation))
}
return admission.ValidationResponse(true, "")
@@ -113,7 +126,7 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
// RegisterValidatingHandler will register TraitDefinition validation to webhook
func RegisterValidatingHandler(mgr manager.Manager, _ controller.Args) {
server := mgr.GetWebhookServer()
server.Register("/validating-core-oam-dev-v1alpha2-traitdefinitions", &webhook.Admission{Handler: &ValidatingHandler{
server.Register("/validating-core-oam-dev-v1beta1-traitdefinitions", &webhook.Admission{Handler: &ValidatingHandler{
Client: mgr.GetClient(),
Decoder: admission.NewDecoder(mgr.GetScheme()),
Validators: []TraitDefValidator{

View File

@@ -20,7 +20,10 @@ import (
"context"
"encoding/json"
"net/http"
"os"
"path/filepath"
"testing"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -28,6 +31,9 @@ import (
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
@@ -41,8 +47,10 @@ var decoder *admission.Decoder
var td v1beta1.TraitDefinition
var tdRaw []byte
var scheme = runtime.NewScheme()
var testEnv *envtest.Environment
var validCueTemplate string
var inValidCueTemplate string
var cfg *rest.Config
func TestTraitdefinition(t *testing.T) {
RegisterFailHandler(Fail)
@@ -58,16 +66,37 @@ var _ = BeforeSuite(func() {
td.SetGroupVersionKind(v1beta1.TraitDefinitionGroupVersionKind)
decoder = admission.NewDecoder(scheme)
var err error
var yamlPath string
if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
yamlPath = "../../../../../test/compatibility-test/testdata"
} else {
yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
}
testEnv = &envtest.Environment{
ControlPlaneStartTimeout: time.Minute,
ControlPlaneStopTimeout: time.Minute,
CRDDirectoryPaths: []string{yamlPath},
}
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
decoder = admission.NewDecoder(scheme)
Expect(err).Should(BeNil())
})
var _ = Describe("Test TraitDefinition validating handler", func() {
BeforeEach(func() {
cli, err := client.New(cfg, client.Options{})
Expect(err).Should(BeNil())
reqResource = metav1.GroupVersionResource{
Group: v1beta1.Group,
Version: v1beta1.Version,
Resource: "traitdefinitions"}
handler = ValidatingHandler{
Decoder: decoder,
Client: cli,
}
})
@@ -181,5 +210,136 @@ var _ = Describe("Test TraitDefinition validating handler", func() {
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
})
It("Test Version field validation passed", func() {
td := v1beta1.TraitDefinition{}
td.SetGroupVersionKind(v1beta1.TraitDefinitionGroupVersionKind)
td.SetName("Correcttd")
td.Spec = v1beta1.TraitDefinitionSpec{
Version: "1.10.1",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
tdRaw, _ := json.Marshal(td)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: tdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test Version validation failed", func() {
wrongtd := v1beta1.TraitDefinition{}
wrongtd.SetGroupVersionKind(v1beta1.TraitDefinitionGroupVersionKind)
wrongtd.SetName("Wrongtd")
wrongtd.Spec = v1beta1.TraitDefinitionSpec{
Version: "a.b.c",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wrongtdRaw, _ := json.Marshal(wrongtd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wrongtdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
Expect(string(resp.Result.Message)).Should(ContainSubstring("Not a valid version"))
})
It("Test TraitDefintion has both spec.version and revision name annotation", func() {
wrongtd := v1beta1.TraitDefinition{}
wrongtd.SetGroupVersionKind(v1beta1.TraitDefinitionGroupVersionKind)
wrongtd.SetName("Wrongtd")
annotations := map[string]string{
"definitionrevision.oam.dev/name": "v1.0.0",
}
wrongtd.SetAnnotations(annotations)
wrongtd.SetNamespace("default")
wrongtd.Spec = v1beta1.TraitDefinitionSpec{
Version: "1.10.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wrongtdRaw, _ := json.Marshal(wrongtd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wrongtdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
Expect(string(resp.Result.Message)).Should(ContainSubstring("Only one can be present"))
})
It("Test TraitDefintion with spec.version and without revision name annotation", func() {
td := v1beta1.TraitDefinition{}
td.SetGroupVersionKind(v1beta1.TraitDefinitionGroupVersionKind)
td.SetName("td")
td.Spec = v1beta1.TraitDefinitionSpec{
Version: "1.10.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
tdRaw, _ := json.Marshal(td)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: tdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test TraitDefintion without spec.version and with revision name annotation", func() {
td := v1beta1.TraitDefinition{}
td.SetGroupVersionKind(v1beta1.TraitDefinitionGroupVersionKind)
td.SetName("td")
annotations := map[string]string{
"definitionrevision.oam.dev/name": "v1.0.0",
}
td.SetAnnotations(annotations)
td.SetNamespace("default")
td.Spec = v1beta1.TraitDefinitionSpec{
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
tdRaw, _ := json.Marshal(td)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: tdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
})
})

View File

@@ -0,0 +1,89 @@
/*
Copyright 2024 The KubeVela 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 workflowstepdefinition
import (
"context"
"fmt"
"net/http"
admissionv1 "k8s.io/api/admission/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam"
webhookutils "github.com/oam-dev/kubevela/pkg/webhook/utils"
)
var workflowStepDefGVR = v1beta1.SchemeGroupVersion.WithResource("workflowstepdefinitions")
// ValidatingHandler handles validation of workflow step definition
type ValidatingHandler struct {
// Decoder decodes object
Decoder *admission.Decoder
Client client.Client
}
// InjectClient injects the client into the ValidatingHandler
func (h *ValidatingHandler) InjectClient(c client.Client) error {
h.Client = c
return nil
}
// InjectDecoder injects the decoder into the ValidatingHandler
func (h *ValidatingHandler) InjectDecoder(d *admission.Decoder) error {
h.Decoder = d
return nil
}
// Handle validate WorkflowStepDefinition Spec here
func (h *ValidatingHandler) Handle(_ context.Context, req admission.Request) admission.Response {
obj := &v1beta1.WorkflowStepDefinition{}
if req.Resource.String() != workflowStepDefGVR.String() {
return admission.Errored(http.StatusBadRequest, fmt.Errorf("expect resource to be %s", workflowStepDefGVR))
}
if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update {
err := h.Decoder.Decode(req, obj)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
if obj.Spec.Version != "" {
err = webhookutils.ValidateSemanticVersion(obj.Spec.Version)
if err != nil {
return admission.Denied(err.Error())
}
}
revisionName := obj.Annotations[oam.AnnotationDefinitionRevisionName]
version := obj.Spec.Version
err = webhookutils.ValidateMultipleDefVersionsNotPresent(version, revisionName, obj.Kind)
if err != nil {
return admission.Denied(err.Error())
}
}
return admission.ValidationResponse(true, "")
}
// RegisterValidatingHandler will register WorkflowStepDefinition validation to webhook
func RegisterValidatingHandler(mgr manager.Manager) {
server := mgr.GetWebhookServer()
server.Register("/validating-core-oam-dev-v1beta1-workflowstepdefinitions", &webhook.Admission{Handler: &ValidatingHandler{}})
}

View File

@@ -0,0 +1,233 @@
/*
Copyright 2024 The KubeVela 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 workflowstepdefinition
import (
"context"
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
core "github.com/oam-dev/kubevela/apis/core.oam.dev"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
)
var handler ValidatingHandler
var reqResource metav1.GroupVersionResource
var decoder *admission.Decoder
var td v1beta1.WorkflowStepDefinition
var validCueTemplate string
var inValidCueTemplate string
var cfg *rest.Config
var testScheme = runtime.NewScheme()
var testEnv *envtest.Environment
func TestWorkflowStepDefinition(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Traitdefinition Suite")
}
var _ = BeforeSuite(func() {
validCueTemplate = "{hello: 'world'}"
inValidCueTemplate = "{hello: world}"
var yamlPath string
if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
yamlPath = "../../../../../test/compatibility-test/testdata"
} else {
yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
}
testEnv = &envtest.Environment{
ControlPlaneStartTimeout: time.Minute,
ControlPlaneStopTimeout: time.Minute,
CRDDirectoryPaths: []string{yamlPath},
}
err := core.AddToScheme(testScheme)
Expect(err).Should(BeNil())
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
decoder = admission.NewDecoder(testScheme)
Expect(err).Should(BeNil())
td = v1beta1.WorkflowStepDefinition{}
td.SetGroupVersionKind(v1beta1.WorkflowStepDefinitionGroupVersionKind)
})
var _ = Describe("Test workflowstepdefinition validating handler", func() {
BeforeEach(func() {
cli, err := client.New(cfg, client.Options{})
Expect(err).Should(BeNil())
reqResource = metav1.GroupVersionResource{
Group: v1beta1.Group,
Version: v1beta1.Version,
Resource: "workflowstepdefinitions"}
handler = ValidatingHandler{Client: cli}
handler.InjectDecoder(decoder)
})
Context("Test create/update operation admission request", func() {
It("Test Version validation passed", func() {
wsd := v1beta1.WorkflowStepDefinition{}
wsd.SetGroupVersionKind(v1beta1.WorkflowStepDefinitionGroupVersionKind)
wsd.SetName("Correctwsd")
wsd.Spec = v1beta1.WorkflowStepDefinitionSpec{
Reference: common.DefinitionReference{Name: "testname", Version: "1"},
Version: "1.10.1",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wsdRaw, _ := json.Marshal(wsd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wsdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test Version validation passed", func() {
wrongWsd := v1beta1.WorkflowStepDefinition{}
wrongWsd.SetGroupVersionKind(v1beta1.WorkflowStepDefinitionGroupVersionKind)
wrongWsd.SetName("wrongwsd")
wrongWsd.Spec = v1beta1.WorkflowStepDefinitionSpec{
Version: "1.B.1",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wrongWsdRaw, _ := json.Marshal(wrongWsd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wrongWsdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
Expect(string(resp.Result.Message)).Should(ContainSubstring("Not a valid version"))
})
It("Test workflowstepdefinition has both spec.version and revision name annotation", func() {
wrongWsd := v1beta1.WorkflowStepDefinition{}
wrongWsd.SetGroupVersionKind(v1beta1.WorkflowStepDefinitionGroupVersionKind)
wrongWsd.SetName("wrongwsd")
annotations := map[string]string{
"definitionrevision.oam.dev/name": "v1.0.0",
}
wrongWsd.SetAnnotations(annotations)
wrongWsd.SetNamespace("default")
wrongWsd.Spec = v1beta1.WorkflowStepDefinitionSpec{
Version: "1.10.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wrongWsdRaw, _ := json.Marshal(wrongWsd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wrongWsdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
Expect(string(resp.Result.Message)).Should(ContainSubstring("Only one can be present"))
})
It("Test workflowstepdefinition without spec.version and with revision name annotation", func() {
wsd := v1beta1.WorkflowStepDefinition{}
wsd.SetGroupVersionKind(v1beta1.WorkflowStepDefinitionGroupVersionKind)
wsd.SetName("wsd")
annotations := map[string]string{
"definitionrevision.oam.dev/name": "v1.0.0",
}
wsd.SetAnnotations(annotations)
wsd.SetNamespace("default")
wsd.Spec = v1beta1.WorkflowStepDefinitionSpec{
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wsdRaw, _ := json.Marshal(wsd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wsdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
It("Test workflowstepdefinition with spec.version and without revision name annotation", func() {
wsd := v1beta1.WorkflowStepDefinition{}
wsd.SetGroupVersionKind(v1beta1.WorkflowStepDefinitionGroupVersionKind)
wsd.SetName("wsd")
wsd.Spec = v1beta1.WorkflowStepDefinitionSpec{
Version: "1.10.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: validCueTemplate,
},
},
}
wsdRaw, _ := json.Marshal(wsd)
req := admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: wsdRaw},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeTrue())
})
})
})

View File

@@ -18,7 +18,9 @@ package utils
import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"cuelang.org/go/cue/cuecontext"
@@ -41,7 +43,6 @@ func ValidateDefinitionRevision(ctx context.Context, cli client.Client, def runt
if errs := validation.IsQualifiedName(defRevNamespacedName.Name); len(errs) != 0 {
return errors.Errorf("invalid definitionRevision name %s:%s", defRevNamespacedName.Name, strings.Join(errs, ","))
}
defRev := new(v1beta1.DefinitionRevision)
if err := cli.Get(ctx, defRevNamespacedName, defRev); err != nil {
return client.IgnoreNotFound(err)
@@ -84,3 +85,29 @@ func checkError(err error) error {
}
return nil
}
// ValidateSemanticVersion validates if a Definition's version includes all of
// major,minor & patch version values.
func ValidateSemanticVersion(version string) error {
if version != "" {
versionParts := strings.Split(version, ".")
if len(versionParts) != 3 {
return errors.New("Not a valid version")
}
for _, versionPart := range versionParts {
if _, err := strconv.Atoi(versionPart); err != nil {
return errors.New("Not a valid version")
}
}
}
return nil
}
// ValidateMultipleDefVersionsNotPresent validates that both Name Annotation Revision and Spec.Version are not present
func ValidateMultipleDefVersionsNotPresent(version, revisionName, objectType string) error {
if version != "" && revisionName != "" {
return fmt.Errorf("%s has both spec.version and revision name annotation. Only one can be present", objectType)
}
return nil
}

View File

@@ -17,11 +17,13 @@ limitations under the License.
package utils
import (
"fmt"
"testing"
"cuelang.org/go/cue/errors"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
)
func TestValidateCueTemplate(t *testing.T) {
@@ -67,3 +69,69 @@ func TestValidateCueTemplate(t *testing.T) {
})
}
}
func TestValidateSemanticVersion(t *testing.T) {
cases := map[string]struct {
version string
want error
}{
"validVersion": {
version: "1.2.3",
want: nil,
},
"versionWithAlphabets": {
version: "1.2.3-alpha",
want: errors.New("Not a valid version"),
},
"invalidVersion": {
version: "1.2",
want: errors.New("Not a valid version"),
},
}
for caseName, cs := range cases {
t.Run(caseName, func(t *testing.T) {
err := ValidateSemanticVersion(cs.version)
if cs.want != nil {
assert.Equal(t, err.Error(), cs.want.Error())
} else {
assert.Equal(t, err, cs.want)
}
})
}
}
func TestValidateMultipleDefVersionsNotPresent(t *testing.T) {
cases := map[string]struct {
version string
revisionName string
want error
}{
"versionPresent": {
version: "1.2.3",
revisionName: "",
want: nil,
},
"revisionNamePresent": {
version: "",
revisionName: "2.3",
want: nil,
},
"versionAndRevisionNamePresent": {
version: "1.2.3",
revisionName: "2.3",
want: fmt.Errorf("ComponentDefinition has both spec.version and revision name annotation. Only one can be present"),
},
}
for caseName, cs := range cases {
t.Run(caseName, func(t *testing.T) {
err := ValidateMultipleDefVersionsNotPresent(cs.version, cs.revisionName, "ComponentDefinition")
if cs.want != nil {
assert.Equal(t, err.Error(), cs.want.Error())
} else {
assert.Equal(t, err, cs.want)
}
})
}
}

View File

@@ -79,10 +79,10 @@ func (loader *WorkflowStepLoader) LoadTemplate(ctx context.Context, name string)
}
// NewWorkflowStepTemplateLoader create a task template loader.
func NewWorkflowStepTemplateLoader(client client.Client) template.Loader {
func NewWorkflowStepTemplateLoader(client client.Client, annotations map[string]string) template.Loader {
return &WorkflowStepLoader{
loadCapabilityDefinition: func(ctx context.Context, capName string) (*appfile.Template, error) {
return appfile.LoadTemplate(ctx, client, capName, types.TypeWorkflowStep)
return appfile.LoadTemplate(ctx, client, capName, types.TypeWorkflowStep, annotations)
},
}
}

View File

@@ -49,7 +49,7 @@ func TestLoad(t *testing.T) {
return nil
},
}
loader := NewWorkflowStepTemplateLoader(cli)
loader := NewWorkflowStepTemplateLoader(cli, make(map[string]string))
tmpl, err := loader.LoadTemplate(context.Background(), "builtin-apply-component")
assert.NoError(t, err)

View File

@@ -25,6 +25,8 @@ import (
wfv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
@@ -272,6 +274,11 @@ var _ = Describe("Testing dry-run", func() {
c := common2.Args{}
c.SetConfig(cfg)
c.SetClient(k8sClient)
ns := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: appNamespace}}
err := k8sClient.Create(context.Background(), &ns)
Expect(err).Should(BeNil())
opt := DryRunCmdOptions{ApplicationFiles: []string{"test-data/dry-run/testing-dry-run-1.yaml"}, OfflineMode: false}
buff, err := DryRunApplication(&opt, c, appNamespace)
Expect(err).Should(BeNil())

View File

@@ -199,6 +199,9 @@ func (o *LiveDiffCmdOptions) renderlessDiff(cli client.Client, option *dryrun.Li
if err := cli.Get(ctx, client.ObjectKey{Name: o.AppName, Namespace: o.Namespace}, app); err != nil {
return buf, errors.Wrapf(err, "cannot get application %s/%s", o.Namespace, o.AppName)
}
if app.Namespace == "" {
app.SetNamespace(o.Namespace)
}
base = dryrun.LiveDiffObject{Application: app}
if o.Revision == "" {
if app.Status.LatestRevision == nil {
@@ -211,12 +214,18 @@ func (o *LiveDiffCmdOptions) renderlessDiff(cli client.Client, option *dryrun.Li
if err := cli.Get(ctx, client.ObjectKey{Name: o.Revision, Namespace: o.Namespace}, rev); err != nil {
return buf, errors.Wrapf(err, "cannot get application revision %s/%s", o.Namespace, o.Revision)
}
if rev.Namespace == "" {
rev.SetNamespace(o.Namespace)
}
if o.SecondaryRevision == "" {
comparor = dryrun.LiveDiffObject{ApplicationRevision: rev}
} else {
if err := cli.Get(ctx, client.ObjectKey{Name: o.SecondaryRevision, Namespace: o.Namespace}, secondaryRev); err != nil {
return buf, errors.Wrapf(err, "cannot get application revision %s/%s", o.Namespace, o.SecondaryRevision)
}
if secondaryRev.Namespace == "" {
secondaryRev.SetNamespace(o.Namespace)
}
base = dryrun.LiveDiffObject{ApplicationRevision: rev}
comparor = dryrun.LiveDiffObject{ApplicationRevision: secondaryRev}
}

View File

@@ -0,0 +1,24 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: podtato-head
spec:
components:
- name: podtato-head-frontend
type: webservice
properties:
image: ghcr.io/podtato-head/podtato-server:v0.3.1
ports:
- port: 8080
expose: true
cpu: "0.1"
memory: "32Mi"
traits:
- type: podsecuritycontext
properties:
# runs pod as non-root user
runAsNonRoot: true
# runs the pod as user with uid 65532
runAsUser: 65532
```

View File

@@ -0,0 +1,29 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: podtato-head
spec:
components:
- name: podtato-head-frontend
type: webservice
properties:
image: ghcr.io/podtato-head/podtato-server:v0.3.1
ports:
- port: 8080
expose: true
cpu: "0.1"
memory: "32Mi"
traits:
- type: securitycontext
properties:
# drops all capabilities
dropCapabilities:
- ALL
# runs container as non-root user
runAsNonRoot: true
# ensures that the container runs unprivileged
privileged: false
# runs container in read-only mode
readOnlyRootFilesystem: false
```

View File

@@ -701,9 +701,9 @@ func GetBaseResourceKinds(cueStr string, mapper meta.RESTMapper) (string, error)
GroupAndVersion = append([]string{""}, GroupAndVersion...)
}
mapping, err := mapper.RESTMapping(schema.GroupKind{Group: GroupAndVersion[0], Kind: kind}, GroupAndVersion[1])
gvr := mapping.Resource
if err != nil {
return "", err
}
gvr := mapping.Resource
return fmt.Sprintf("- %s.%s", gvr.Resource, gvr.Group), nil
}

View File

@@ -0,0 +1,664 @@
/*
Copyright 2024 The KubeVela 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 controllers_test
import (
"context"
"fmt"
"strings"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
var _ = Describe("Application AutoUpdate", func() {
ctx := context.Background()
var namespace string
var ns corev1.Namespace
var reconcileSleepTime = 70 * time.Second
var sleepTime = 5 * time.Second
BeforeEach(func() {
By("Create namespace for app-autoupdate-e2e-test")
namespace = randomNamespaceName("app-autoupdate-e2e-test")
ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
Eventually(func() error {
return k8sClient.Create(ctx, &ns)
}, time.Second*3, time.Microsecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
})
AfterEach(func() {
By("Clean up resources after a test")
k8sClient.DeleteAllOf(ctx, &v1beta1.Application{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.ComponentDefinition{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.TraitDefinition{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.DefinitionRevision{}, client.InNamespace(namespace))
Expect(k8sClient.Delete(ctx, &ns)).Should(BeNil())
})
Context("Enabled", func() {
It("When specified exact component version available. App should use exact specified version.", func() {
By("Create configmap-component with 1.0.0 version")
componentVersion := "1.0.0"
componentType := "configmap-component"
component := createComponent(componentVersion, namespace, componentType)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
By("Create configmap-component with 1.2.0 version")
updatedComponent := new(v1beta1.ComponentDefinition)
updatedComponentVersion := "1.2.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: componentType, Namespace: namespace}, updatedComponent)
if err != nil {
return err
}
updatedComponent.Spec.Version = updatedComponentVersion
updatedComponent.Spec.Schematic.CUE.Template = createOutputConfigMap(updatedComponentVersion)
return k8sClient.Update(ctx, updatedComponent)
}, 15*time.Second, time.Second).Should(BeNil())
time.Sleep(sleepTime)
By("Create application using configmap-component@1.2.0")
app := updateAppComponent(appTemplate, "app1", namespace, componentType, "first-component", componentVersion)
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
cm := new(corev1.ConfigMap)
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "comptest", Namespace: namespace}, cm)
if err != nil {
return err
}
return nil
}, 15*time.Second, time.Second).Should(BeNil())
Expect(cm.Data["expectedVersion"]).To(BeEquivalentTo(componentVersion))
By("Create configmap-component with 1.4.0 version")
updatedComponentVersion = "1.4.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: componentType, Namespace: namespace}, updatedComponent)
if err != nil {
return err
}
updatedComponent.Spec.Version = updatedComponentVersion
updatedComponent.Spec.Schematic.CUE.Template = createOutputConfigMap(componentVersion)
return k8sClient.Update(ctx, updatedComponent)
}, 15*time.Second, time.Second).Should(BeNil())
By("Wait for application to reconcile")
time.Sleep(reconcileSleepTime)
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "comptest", Namespace: namespace}, cm)
if err != nil {
return err
}
return nil
}, 15*time.Second, time.Second).Should(BeNil())
Expect(cm.Data["expectedVersion"]).To(BeEquivalentTo(componentVersion))
})
It("When speicified component version is unavailable. App should use latest version in specified range.", func() {
By("Create configmap-component with 1.4.5 version")
componentVersion := "1.4.5"
componentType := "configmap-component"
component := createComponent(componentVersion, namespace, componentType)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
time.Sleep(sleepTime)
By("Create application using configmap-component@1.4")
app := updateAppComponent(appTemplate, "app1", namespace, componentType, "first-component", "1.4")
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
cm := new(corev1.ConfigMap)
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "comptest", Namespace: namespace}, cm)
if err != nil {
return err
}
return nil
}, 15*time.Second, time.Second).Should(BeNil())
Expect(cm.Data["expectedVersion"]).To(BeEquivalentTo(componentVersion))
})
It("When new component version release after app creation, app should use new version during reconciliation", func() {
By("Create configmap-component with 2.2.0 version")
componentVersion := "2.2.0"
componentType := "configmap-component"
component := createComponent(componentVersion, namespace, componentType)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
By("Create configmap-component with 2.3.0 version")
updatedComponent := new(v1beta1.ComponentDefinition)
updatedComponentVersion := "2.3.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: componentType, Namespace: namespace}, updatedComponent)
if err != nil {
return err
}
updatedComponent.Spec.Version = updatedComponentVersion
updatedComponent.Spec.Schematic.CUE.Template = createOutputConfigMap(updatedComponentVersion)
return k8sClient.Update(ctx, updatedComponent)
}, 15*time.Second, time.Second).Should(BeNil())
time.Sleep(sleepTime)
By("Create application using configmap-component@2")
app := updateAppComponent(appTemplate, "app1", namespace, componentType, "first-component", "2")
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
cm := new(corev1.ConfigMap)
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "comptest", Namespace: namespace}, cm)
if err != nil {
return err
}
return nil
}, 15*time.Second, time.Second).Should(BeNil())
Expect(cm.Data["expectedVersion"]).To(BeEquivalentTo(updatedComponentVersion))
By("Create configmap-component with 2.4.0 version")
updatedComponentVersion = "2.4.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: componentType, Namespace: namespace}, updatedComponent)
if err != nil {
return err
}
updatedComponent.Spec.Version = updatedComponentVersion
updatedComponent.Spec.Schematic.CUE.Template = createOutputConfigMap(updatedComponentVersion)
return k8sClient.Update(ctx, updatedComponent)
}, 15*time.Second, time.Second).Should(BeNil())
By("Wait for application to reconcile")
time.Sleep(reconcileSleepTime)
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "comptest", Namespace: namespace}, cm)
if err != nil {
return err
}
return nil
}, 15*time.Second, time.Second).Should(BeNil())
Expect(cm.Data["expectedVersion"]).To(BeEquivalentTo(updatedComponentVersion))
})
It("When speicified version is available for one component and unavailable for other, app should use autoupdate the latter", func() {
By("Create configmap-component with 1.4.5 version")
componentVersion := "1.4.5"
componentType := "configmap-component"
component := createComponent(componentVersion, namespace, componentType)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
time.Sleep(sleepTime)
By("Create application using configmap-component@1.4 version")
app := updateAppComponent(appWithTwoComponentTemplate, "app1", namespace, componentType, "first-component", "1.4")
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
cm := new(corev1.ConfigMap)
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "comptest", Namespace: namespace}, cm)
if err != nil {
return err
}
return nil
}, 15*time.Second, time.Second).Should(BeNil())
Expect(cm.Data["expectedVersion"]).To(BeEquivalentTo(componentVersion))
pods := new(corev1.PodList)
opts := []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
oam.LabelAppName: "app1",
},
}
Expect(k8sClient.List(ctx, pods, opts...)).To(BeNil())
Expect(len(pods.Items)).To(BeEquivalentTo(1))
})
It("When specified exact trait version available, app should use exact version", func() {
By("Create scaler-trait with 1.0.0 version and 1 replica")
traitVersion := "1.0.0"
traitType := "scaler-trait"
trait := createTrait(traitVersion, namespace, traitType, "1")
trait.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, trait)).Should(Succeed())
By("Create scaler-trait with 1.2.0 version and 2 replicas")
updatedTrait := new(v1beta1.TraitDefinition)
updatedTraitVersion := "1.2.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: traitType, Namespace: namespace}, updatedTrait)
if err != nil {
return err
}
updatedTrait.Spec.Version = updatedTraitVersion
updatedTrait.Spec.Schematic.CUE.Template = createScalerTraitOutput("2")
return k8sClient.Update(ctx, updatedTrait)
}, 15*time.Second, time.Second).Should(BeNil())
time.Sleep(sleepTime)
app := updateAppTrait(traitApp, "app1", namespace, traitType, updatedTraitVersion)
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
pods := new(corev1.PodList)
opts := []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
oam.LabelAppName: "app1",
},
}
time.Sleep(sleepTime)
Expect(k8sClient.List(ctx, pods, opts...)).To(BeNil())
Expect(len(pods.Items)).To(BeEquivalentTo(2))
By("Create scaler-trait with 1.4.0 version")
updatedTraitVersion = "1.4.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: traitType, Namespace: namespace}, updatedTrait)
if err != nil {
return err
}
updatedTrait.Spec.Version = updatedTraitVersion
updatedTrait.Spec.Schematic.CUE.Template = createScalerTraitOutput("3")
return k8sClient.Update(ctx, updatedTrait)
}, 15*time.Second, time.Second).Should(BeNil())
By("Wait for application to reconcile")
time.Sleep(reconcileSleepTime)
pods = new(corev1.PodList)
opts = []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
oam.LabelAppName: "app1",
},
}
time.Sleep(sleepTime)
Expect(k8sClient.List(ctx, pods, opts...)).To(BeNil())
Expect(len(pods.Items)).To(BeEquivalentTo(2))
})
It("When specified trait version is unavailable, app should use latest version specified in range", func() {
By("Create scaler-trait with 1.4.5 version and 4 replica")
traitVersion := "1.4.5"
traitType := "scaler-trait"
trait := createTrait(traitVersion, namespace, traitType, "4")
Expect(k8sClient.Create(ctx, trait)).Should(Succeed())
time.Sleep(sleepTime)
By("Create application using scaler-trait@v1.4")
app := updateAppTrait(traitApp, "app1", namespace, traitType, "1.4")
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
pods := new(corev1.PodList)
opts := []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
oam.LabelAppName: "app1",
},
}
time.Sleep(sleepTime)
Expect(k8sClient.List(ctx, pods, opts...)).To(BeNil())
Expect(len(pods.Items)).To(BeEquivalentTo(4))
})
It("When new trait version is created after app creation, app should use new version during reconciliation", func() {
By("Create scaler-trait with 1.4.5 version and 4 replica")
traitVersion := "1.4.5"
traitType := "scaler-trait"
trait := createTrait(traitVersion, namespace, traitType, "4")
Expect(k8sClient.Create(ctx, trait)).Should(Succeed())
time.Sleep(sleepTime)
By("Create application using scaler-trait@v1.4")
app := updateAppTrait(traitApp, "app1", namespace, traitType, "1.4")
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
pods := new(corev1.PodList)
opts := []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
oam.LabelAppName: "app1",
},
}
time.Sleep(sleepTime)
Expect(k8sClient.List(ctx, pods, opts...)).To(BeNil())
Expect(len(pods.Items)).To(BeEquivalentTo(4))
By("Create scaler-trait with 1.4.8 version and 2 replicas")
updatedTrait := new(v1beta1.TraitDefinition)
updatedTraitVersion := "1.4.8"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: traitType, Namespace: namespace}, updatedTrait)
if err != nil {
return err
}
updatedTrait.Spec.Version = updatedTraitVersion
updatedTrait.Spec.Schematic.CUE.Template = createScalerTraitOutput("2")
return k8sClient.Update(ctx, updatedTrait)
}, 15*time.Second, time.Second).Should(BeNil())
By("Wait for application to reconcile")
time.Sleep(reconcileSleepTime)
pods = new(corev1.PodList)
opts = []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
oam.LabelAppName: "app1",
},
}
time.Sleep(sleepTime)
Expect(k8sClient.List(ctx, pods, opts...)).To(BeNil())
Expect(len(pods.Items)).To(BeEquivalentTo(2))
})
It("When Autoupdate and Publish version annotation are specified in application, app creation should fail", func() {
By("Create configmap-component with 1.4.5 version")
componentVersion := "1.4.5"
componentType := "configmap-component"
component := createComponent(componentVersion, namespace, componentType)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
time.Sleep(sleepTime)
By("Create application using configmap-component@1.4.5")
app := updateAppComponent(appTemplate, "app1", namespace, componentType, "first-component", "1.4.5")
app.ObjectMeta.Annotations[oam.AnnotationPublishVersion] = "alpha"
err := k8sClient.Create(ctx, app)
Expect(err).ShouldNot(BeNil())
Expect(err.Error()).Should(ContainSubstring("Application has both autoUpdate and publishVersion annotations. Only one can be present"))
})
})
Context("Disabled", func() {
It("When specified component version is available, app should use specified version", func() {
By("Create configmap-component with 1.0.0 version")
componentVersion := "1.0.0"
componentType := "configmap-component"
component := createComponent(componentVersion, namespace, componentType)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
By("Create configmap-component with 1.2.0 version")
updatedComponent := new(v1beta1.ComponentDefinition)
updatedComponentVersion := "1.2.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: componentType, Namespace: namespace}, updatedComponent)
if err != nil {
return err
}
updatedComponent.Spec.Version = updatedComponentVersion
updatedComponent.Spec.Schematic.CUE.Template = createOutputConfigMap(componentVersion)
return k8sClient.Update(ctx, updatedComponent)
}, 15*time.Second, time.Second).Should(BeNil())
time.Sleep(sleepTime)
By("Create application using configmap-component@1.0.0 version")
app := updateAppComponent(appTemplate, "app1", namespace, componentType, "first-component", componentVersion)
app.ObjectMeta.Annotations[oam.AnnotationAutoUpdate] = "false"
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
cm := new(corev1.ConfigMap)
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: "comptest", Namespace: namespace}, cm)
if err != nil {
return err
}
return nil
}, 15*time.Second, time.Second).Should(BeNil())
Expect(cm.Data["expectedVersion"]).To(BeEquivalentTo(componentVersion))
})
It("When specified component version is unavailable, app creation should fail", func() {
By("Create configmap-component with 1.2.0 version")
componentVersion := "1.2.0"
componentType := "configmap-component"
component := createComponent(componentVersion, namespace, componentType)
Expect(k8sClient.Create(ctx, component)).Should(Succeed())
time.Sleep(sleepTime)
By("Create application using configmap-component@1 version")
app := updateAppComponent(appTemplate, "app1", namespace, componentType, "first-component", "1")
app.ObjectMeta.Annotations[oam.AnnotationAutoUpdate] = "false"
Expect(k8sClient.Create(ctx, app)).ShouldNot(Succeed())
cm := new(corev1.ConfigMap)
time.Sleep(reconcileSleepTime)
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "comptest", Namespace: namespace}, cm)).ShouldNot(BeNil())
configmaps := new(corev1.ConfigMapList)
opts := []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
oam.LabelAppName: "app1",
},
}
Expect(k8sClient.List(ctx, configmaps, opts...)).To(BeNil())
Expect(len(configmaps.Items)).To(BeEquivalentTo(0))
})
It("When specified trait version is available, app should specified trait version", func() {
By("Create scaler-trait with 1.0.0 version and 1 replica")
traitVersion := "1.0.0"
traitType := "scaler-trait"
trait := createTrait(traitVersion, namespace, traitType, "1")
Expect(k8sClient.Create(ctx, trait)).Should(Succeed())
By("Create scaler-trait with 1.2.0 version and 2 replica")
updatedTrait := new(v1beta1.TraitDefinition)
updatedTraitVersion := "1.2.0"
Eventually(func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Name: traitType, Namespace: namespace}, updatedTrait)
if err != nil {
return err
}
updatedTrait.Spec.Version = updatedTraitVersion
updatedTrait.Spec.Schematic.CUE.Template = createScalerTraitOutput("2")
return k8sClient.Update(ctx, updatedTrait)
}, 15*time.Second, time.Second).Should(BeNil())
time.Sleep(sleepTime)
By("Create application using scaler-trait@1.0.0 version")
app := updateAppTrait(traitApp, "app1", namespace, traitType, traitVersion)
app.ObjectMeta.Annotations[oam.AnnotationAutoUpdate] = "false"
Expect(k8sClient.Create(ctx, app)).Should(Succeed())
By("Wait for application to be created")
time.Sleep(reconcileSleepTime)
pods := new(corev1.PodList)
opts := []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
oam.LabelAppName: "app1",
},
}
time.Sleep(5 * time.Second)
Expect(k8sClient.List(ctx, pods, opts...)).To(BeNil())
Expect(len(pods.Items)).To(BeEquivalentTo(1))
})
})
})
// TODO Add test cases for policydefinition and worflowstepdefinition
func updateAppComponent(appTemplate v1beta1.Application, appName, namespace, typeName, componentName, componentVersion string) *v1beta1.Application {
app := appTemplate.DeepCopy()
app.ObjectMeta.Name = appName
app.SetNamespace(namespace)
app.Spec.Components[0].Type = fmt.Sprintf("%s@v%s", typeName, componentVersion)
app.Spec.Components[0].Name = componentName
return app
}
func updateAppTrait(traitApp v1beta1.Application, appName, namespace, typeName, traitVersion string) *v1beta1.Application {
app := traitApp.DeepCopy()
app.ObjectMeta.Name = appName
app.SetNamespace(namespace)
app.Spec.Components[0].Traits[0].Type = fmt.Sprintf("%s@v%s", typeName, traitVersion)
return app
}
func createComponent(componentVersion, namespace, name string) *v1beta1.ComponentDefinition {
component := configMapComponent.DeepCopy()
component.ObjectMeta.Name = name
component.Spec.Version = componentVersion
component.Spec.Schematic.CUE.Template = createOutputConfigMap(componentVersion)
component.SetNamespace(namespace)
return component
}
func createTrait(traitVersion, namespace, name, replicas string) *v1beta1.TraitDefinition {
trait := scalerTrait.DeepCopy()
trait.ObjectMeta.Name = name
trait.Spec.Version = traitVersion
trait.Spec.Schematic.CUE.Template = createScalerTraitOutput(replicas)
trait.SetNamespace(namespace)
return trait
}
func createScalerTraitOutput(replicas string) string {
return strings.Replace(scalerTraitOutputTemplate, "1", replicas, 1)
}
func createOutputConfigMap(toVersion string) string {
return strings.Replace(configMapOutputTemplate, "1.0.0", toVersion, 1)
}
var configMapComponent = &v1beta1.ComponentDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "ComponentDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "configmap-component",
},
Spec: v1beta1.ComponentDefinitionSpec{
Version: "1.0.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: "",
},
},
},
}
var configMapOutputTemplate = `output: {
apiVersion: "v1"
kind: "ConfigMap"
metadata: name: "comptest"
data: {
expectedVersion: "1.0.0"
}
}`
var appTemplate = v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "Name",
Namespace: "Namespace",
Annotations: map[string]string{
oam.AnnotationAutoUpdate: "true",
},
},
Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: "comp1Name",
Type: "type",
},
},
},
}
var appWithTwoComponentTemplate = v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "Name",
Namespace: "Namespace",
Annotations: map[string]string{
oam.AnnotationAutoUpdate: "true",
},
},
Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: "first-component",
Type: "configmap-component",
},
{
Name: "second-component",
Type: "webservice@v1",
Properties: util.Object2RawExtension(map[string]interface{}{
"image": "nginx",
}),
},
},
},
}
var scalerTraitOutputTemplate = `patch: spec: replicas: 1`
var scalerTrait = &v1beta1.TraitDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "TraitDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "scaler-trait",
Annotations: map[string]string{},
},
Spec: v1beta1.TraitDefinitionSpec{
Version: "1.0.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: "",
},
},
},
}
var traitApp = v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "app-with-trait",
Namespace: "Namespace",
Annotations: map[string]string{
oam.AnnotationAutoUpdate: "true",
},
},
Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: "webservice-component",
Type: "webservice",
Properties: &runtime.RawExtension{Raw: []byte(`{"image": "busybox"}`)},
Traits: []common.ApplicationTrait{
{
Type: "scaler-trait",
},
},
},
},
},
}

View File

@@ -122,6 +122,79 @@ var _ = Describe("Test application of the specified definition version", func()
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
})
It("Test tries to deploy component which has both spec.version and revision name annotation", func() {
workerV1 := workerWithNoTemplate.DeepCopy()
workerV1.Spec.Workload = common.WorkloadTypeDescriptor{
Definition: common.WorkloadGVK{
APIVersion: "batch/v1",
Kind: "Job",
},
}
workerV1.ObjectMeta.Annotations[oam.AnnotationDefinitionRevisionName] = "1.0.0"
workerV1.Spec.Version = "1.0.0"
workerV1.Spec.Schematic.CUE.Template = workerV1Template
workerV1.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, workerV1)).ShouldNot(Succeed())
})
It("Test tries to deploy component which has spec.version and but no revision name annotation", func() {
workerV1 := workerWithNoTemplate.DeepCopy()
workerV1.Spec.Workload = common.WorkloadTypeDescriptor{
Definition: common.WorkloadGVK{
APIVersion: "batch/v1",
Kind: "Job",
},
}
workerV1.Spec.Version = "1.0.0"
workerV1.Spec.Schematic.CUE.Template = workerV1Template
workerV1.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, workerV1)).Should(Succeed())
})
It("Test tries to deploy trait which has both spec.version and revision name annotation", func() {
traitV1 := scalerTrait.DeepCopy()
traitV1.Spec.Schematic.CUE.Template = scalerTraitOutputTemplate
traitV1.ObjectMeta.Annotations[oam.AnnotationDefinitionRevisionName] = "1.0.0"
// traitV1.Spec.Version = "1.0.0"
traitV1.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, traitV1)).ShouldNot(Succeed())
})
It("Test tries to deploy trait which has spec.version and but no revision name annotation", func() {
traitV1 := scalerTrait.DeepCopy()
traitV1.Spec.Schematic.CUE.Template = scalerTraitOutputTemplate
traitV1.Spec.Version = "1.0.0"
traitV1.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, traitV1)).Should(Succeed())
})
It("Test tries to deploy policy which has both spec.version and revision name annotation", func() {
policyV1 := policyDef.DeepCopy()
policyV1.ObjectMeta.Annotations[oam.AnnotationDefinitionRevisionName] = "1.0.0"
policyV1.Spec.Version = "1.0.0"
policyV1.Spec.Schematic.CUE.Template = workerV1Template
policyV1.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, policyV1)).ShouldNot(Succeed())
})
It("Test tries to deploy policy which has spec.version and but no revision name annotation", func() {
policyV1 := policyDef.DeepCopy()
policyV1.Spec.Version = "1.0.0"
policyV1.Spec.Schematic.CUE.Template = workerV1Template
policyV1.SetNamespace(namespace)
Expect(k8sClient.Create(ctx, policyV1)).Should(Succeed())
})
It("Test deploy application which containing cue rendering module", func() {
var (
appName = "test-website-app"
@@ -465,7 +538,8 @@ var workerWithNoTemplate = &v1beta1.ComponentDefinition{
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "worker",
Name: "worker",
Annotations: map[string]string{},
},
Spec: v1beta1.ComponentDefinitionSpec{
Schematic: &common.Schematic{
@@ -502,6 +576,27 @@ var jobComponentDef = &v1beta1.ComponentDefinition{
},
}
var policyDefOutputTemplate = `properties: enable: true`
var policyDef = &v1beta1.PolicyDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "PolicyDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "policy-apply-once",
Annotations: map[string]string{},
},
Spec: v1beta1.PolicyDefinitionSpec{
Version: "1.0.0",
Schematic: &common.Schematic{
CUE: &common.CUE{
Template: policyDefOutputTemplate,
},
},
},
}
var KUBEWorker = &v1beta1.ComponentDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "ComponentDefinition",

View File

@@ -0,0 +1,51 @@
podsecuritycontext: {
type: "trait"
annotations: {}
description: "Adds security context to the pod spec in path 'spec.template.spec.securityContext'."
attributes: {
podDisruptive: true
appliesToWorkloads: ["deployments.apps", "statefulsets.apps", "daemonsets.apps", "jobs.batch"]
}
}
template: {
patch: spec: template: spec: {
securityContext: {
if parameter.appArmorProfile != _|_ {
appArmorProfile: parameter.appArmorProfile
}
if parameter.fsGroup != _|_ {
fsGroup: parameter.fsGroup
}
if parameter.runAsGroup != _|_ {
runAsGroup: parameter.runAsGroup
}
if parameter.runAsUser != _|_ {
runAsUser: parameter.runAsUser
}
if parameter.seccompProfile != _|_ {
seccompProfile: parameter.seccompProfile
}
runAsNonRoot: parameter.runAsNonRoot
}
}
parameter: {
// +usage=Specify the AppArmor profile for the pod
appArmorProfile?: {
type: "RuntimeDefault" | "Unconfined" | "Localhost"
localhostProfile: string
}
fsGroup?: int
runAsGroup?: int
// +usage=Specify the UID to run the entrypoint of the container process
runAsUser?: int
// +usage=Specify if the container runs as a non-root user
runAsNonRoot: *true | bool
// +usage=Specify the seccomp profile for the pod
seccompProfile?: {
type: "RuntimeDefault" | "Unconfined" | "Localhost"
localhostProfile: string
}
}
}

View File

@@ -0,0 +1,99 @@
securitycontext: {
type: "trait"
annotations: {}
description: "Adds security context to the container spec in path 'spec.template.spec.containers.[].securityContext'."
attributes: {
podDisruptive: true
appliesToWorkloads: ["deployments.apps", "statefulsets.apps", "daemonsets.apps", "jobs.batch"]
}
}
template: {
#PatchParams: {
// +usage=Specify the name of the target container, if not set, use the component name
containerName: *"" | string
addCapabilities?: [...string]
allowPrivilegeEscalation: *false | bool
dropCapabilities?: [...string]
privileged: *false | bool
readOnlyRootFilesystem: *false | bool
runAsNonRoot: *true | bool
runAsUser?: int
runAsGroup?: int
}
PatchContainer: {
_params: #PatchParams
name: _params.containerName
_baseContainers: context.output.spec.template.spec.containers
_matchContainers_: [for _container_ in _baseContainers if _container_.name == name {_container_}]
_baseContainer: *_|_ | {...}
if len(_matchContainers_) == 0 {
err: "container \(name) not found"
}
if len(_matchContainers_) > 0 {
securityContext: {
capabilities: {
if _params.addCapabilities != _|_ {
add: _params.addCapabilities
}
if _params.dropCapabilities != _|_ {
drop: _params.dropCapabilities
}
}
if _params.runAsUser != _|_ {
runAsUser: _params.runAsUser
}
if _params.runAsGroup != _|_ {
runAsGroup: _params.runAsGroup
}
allowPrivilegeEscalation: _params.allowPrivilegeEscalation
readOnlyRootFilesystem: _params.readOnlyRootFilesystem
privileged: _params.privileged
runAsNonRoot: _params.runAsNonRoot
}
}
}
patch: spec: template: spec: {
if parameter.containers == _|_ {
// +patchKey=name
containers: [{
PatchContainer & {_params: {
if parameter.containerName == "" {
containerName: context.name
}
if parameter.containerName != "" {
containerName: parameter.containerName
}
allowPrivilegeEscalation: parameter.allowPrivilegeEscalation
readOnlyRootFilesystem: parameter.readOnlyRootFilesystem
privileged: parameter.privileged
runAsNonRoot: parameter.runAsNonRoot
runAsUser: parameter.runAsUser
runAsGroup: parameter.runAsGroup
addCapabilities: parameter.addCapabilities
dropCapabilities: parameter.dropCapabilities
}}
}]
}
if parameter.containers != _|_ {
// +patchKey=name
containers: [for c in parameter.containers {
if c.containerName == "" {
err: "containerName must be set for containers"
}
if c.containerName != "" {
PatchContainer & {_params: c}
}
}]
}
}
parameter: #PatchParams | close({
// +usage=Specify the container image for multiple containers
containers: [...#PatchParams]
})
errs: [for c in patch.spec.template.spec.containers if c.err != _|_ {c.err}]
}