mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-23 14:23:54 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bc014bc8c | ||
|
|
bb9f5402b3 | ||
|
|
37bd952486 | ||
|
|
5c33598fe9 | ||
|
|
54e806a94c | ||
|
|
740b85c6e1 | ||
|
|
8ceb64d16c | ||
|
|
5bf11d4142 | ||
|
|
0229ac775b | ||
|
|
52e3893bf3 | ||
|
|
419afd27b5 | ||
|
|
52550bb7b5 | ||
|
|
d6f3f995cd | ||
|
|
dc6ab6b8d6 | ||
|
|
613f1f9b53 | ||
|
|
eefc72b0c6 | ||
|
|
1ac66bb3e4 | ||
|
|
ac6d6d1a73 | ||
|
|
46f7472bd5 | ||
|
|
d872430b08 | ||
|
|
9d2cf16141 | ||
|
|
8c9fab1aa1 | ||
|
|
e213779235 | ||
|
|
e4bf16b618 | ||
|
|
9c64afa1f6 |
47
.github/workflows/go.yml
vendored
47
.github/workflows/go.yml
vendored
@@ -7,7 +7,9 @@ on:
|
||||
- release-*
|
||||
workflow_dispatch: {}
|
||||
pull_request:
|
||||
branches: [master]
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
@@ -62,6 +64,45 @@ jobs:
|
||||
flags: unittests
|
||||
name: codecov-umbrella
|
||||
|
||||
compatibility-test:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Set up Go 1.14
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Cache Go Dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .work/pkg
|
||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-pkg-
|
||||
|
||||
- name: Install ginkgo
|
||||
run: |
|
||||
sudo apt-get install -y golang-ginkgo-dev
|
||||
|
||||
- name: Setup Kind Cluster
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
|
||||
- name: install Kubebuilder
|
||||
uses: wonderflow/kubebuilder-action@v1.1
|
||||
|
||||
- name: Run Make compatibility-test
|
||||
run: make compatibility-test
|
||||
|
||||
- name: Clean up testdata
|
||||
run: make compatibility-testdata-cleanup
|
||||
|
||||
e2e-tests:
|
||||
runs-on: aliyun
|
||||
steps:
|
||||
@@ -114,6 +155,10 @@ jobs:
|
||||
- name: Run e2e tests
|
||||
run: make e2e-test
|
||||
|
||||
- name: Cleanup image
|
||||
if: ${{ always() }}
|
||||
run: make image-cleanup
|
||||
|
||||
staticcheck:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
|
||||
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@@ -58,20 +58,6 @@ jobs:
|
||||
asset_path: ./_bin/vela-linux-amd64.zip
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-linux-amd64.zip
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload Linux arm64 tar.gz
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.get_release.outputs.upload_url }}
|
||||
asset_path: ./_bin/vela-linux-arm64.tar.gz
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-linux-arm64.tar.gz
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload Linux arm64 zip
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
upload_url: ${{ steps.get_release.outputs.upload_url }}
|
||||
asset_path: ./_bin/vela-linux-arm64.zip
|
||||
asset_name: vela-${{ steps.get_version.outputs.VERSION }}-linux-arm64.zip
|
||||
asset_content_type: binary/octet-stream
|
||||
- name: Upload MacOS tar.gz
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
with:
|
||||
|
||||
20
Makefile
20
Makefile
@@ -8,7 +8,7 @@ VELA_GITVERSION_VAR := github.com/oam-dev/kubevela/version.GitRevision
|
||||
LDFLAGS ?= "-X $(VELA_VERSION_VAR)=$(VELA_VERSION) -X $(VELA_GITVERSION_VAR)=$(GIT_COMMIT)"
|
||||
|
||||
GOX = go run github.com/mitchellh/gox
|
||||
TARGETS := darwin/amd64 linux/amd64 windows/amd64 linux/arm64
|
||||
TARGETS := darwin/amd64 linux/amd64 windows/amd64
|
||||
DIST_DIRS := find * -type d -exec
|
||||
|
||||
TIME_LONG = `date +%Y-%m-%d' '%H:%M:%S`
|
||||
@@ -144,10 +144,28 @@ e2e-test:
|
||||
CGO_ENABLED=0 go test -timeout 1h -count=1 -v -tags 'integration' ./test/integration
|
||||
@$(OK) tests pass
|
||||
|
||||
compatibility-test: vet lint staticcheck generate-compatibility-testdata
|
||||
# Run compatibility test with old crd
|
||||
COMPATIBILITY_TEST=TRUE go test -race ./pkg/...
|
||||
@$(OK) compatibility-test pass
|
||||
|
||||
generate-compatibility-testdata:
|
||||
mkdir -p ./test/compatibility-test/testdata
|
||||
go run ./test/compatibility-test/convert/main.go ./charts/vela-core/crds ./test/compatibility-test/testdata
|
||||
|
||||
compatibility-testdata-cleanup:
|
||||
rm -f ./test/compatibility-test/testdata/*
|
||||
|
||||
e2e-cleanup:
|
||||
# Clean up
|
||||
rm -rf ~/.vela
|
||||
|
||||
image-cleanup:
|
||||
# Delete Docker image
|
||||
ifneq ($(shell docker images -q vela-core-test:$(GIT_COMMIT)),)
|
||||
docker image rm -f vela-core-test:$(GIT_COMMIT)
|
||||
endif
|
||||
|
||||
# load docker image to the kind cluster
|
||||
kind-load:
|
||||
docker build -t vela-core-test:$(GIT_COMMIT) .
|
||||
|
||||
@@ -101,7 +101,7 @@ type Status struct {
|
||||
// is used to validate the schema of the workload when it is embedded in an OAM
|
||||
// Component.
|
||||
// +kubebuilder:printcolumn:JSONPath=".spec.definitionRef.name",name=DEFINITION-NAME,type=string
|
||||
// +kubebuilder:resource:scope=Cluster,categories={crossplane,oam}
|
||||
// +kubebuilder:resource:scope=Namespaced,categories={crossplane,oam}
|
||||
type WorkloadDefinition struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
@@ -177,7 +177,7 @@ type TraitDefinitionSpec struct {
|
||||
// to validate the schema of the trait when it is embedded in an OAM
|
||||
// ApplicationConfiguration.
|
||||
// +kubebuilder:printcolumn:JSONPath=".spec.definitionRef.name",name=DEFINITION-NAME,type=string
|
||||
// +kubebuilder:resource:scope=Cluster,categories={crossplane,oam}
|
||||
// +kubebuilder:resource:scope=Namespaced,categories={crossplane,oam}
|
||||
type TraitDefinition struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
@@ -219,7 +219,7 @@ type ScopeDefinitionSpec struct {
|
||||
// to validate the schema of the scope when it is embedded in an OAM
|
||||
// ApplicationConfiguration.
|
||||
// +kubebuilder:printcolumn:JSONPath=".spec.definitionRef.name",name=DEFINITION-NAME,type=string
|
||||
// +kubebuilder:resource:scope=Cluster,categories={crossplane,oam}
|
||||
// +kubebuilder:resource:scope=Namespaced,categories={crossplane,oam}
|
||||
type ScopeDefinition struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
@@ -412,6 +412,16 @@ type WorkloadTrait struct {
|
||||
|
||||
// Message will allow controller to leave some additional information for this trait
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// AppliedGeneration indicates the generation observed by the appConfig controller.
|
||||
// The same field is also recorded in the annotations of traits.
|
||||
// A trait is possible to be deleted from cluster after created.
|
||||
// This field is useful to track the observed generation of traits after they are
|
||||
// deleted.
|
||||
AppliedGeneration int64 `json:"appliedGeneration,omitempty"`
|
||||
|
||||
// DependencyUnsatisfied notify does the trait has dependency unsatisfied
|
||||
DependencyUnsatisfied bool `json:"dependencyUnsatisfied,omitempty"`
|
||||
}
|
||||
|
||||
// A ScopeStatus represents the state of a scope.
|
||||
@@ -439,12 +449,11 @@ type WorkloadStatus struct {
|
||||
// ComponentRevisionName of current component
|
||||
ComponentRevisionName string `json:"componentRevisionName,omitempty"`
|
||||
|
||||
// ObservedGeneration indicates the generation observed by the appconfig controller.
|
||||
// The same field is also recorded in the annotations of workloads.
|
||||
// A workload is possible to be deleted from cluster after created.
|
||||
// This field is useful to track the observed generation of workloads after they are
|
||||
// deleted.
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
// DependencyUnsatisfied notify does the workload has dependency unsatisfied
|
||||
DependencyUnsatisfied bool `json:"dependencyUnsatisfied,omitempty"`
|
||||
|
||||
// AppliedComponentRevision indicates the applied component revision name of this workload
|
||||
AppliedComponentRevision string `json:"appliedComponentRevision,omitempty"`
|
||||
|
||||
// Reference to a workload created by an ApplicationConfiguration.
|
||||
Reference runtimev1alpha1.TypedReference `json:"workloadRef,omitempty"`
|
||||
|
||||
@@ -380,16 +380,18 @@ spec:
|
||||
items:
|
||||
description: A WorkloadStatus represents the status of a workload.
|
||||
properties:
|
||||
appliedComponentRevision:
|
||||
description: AppliedComponentRevision indicates the applied component revision name of this workload
|
||||
type: string
|
||||
componentName:
|
||||
description: ComponentName that produced this workload.
|
||||
type: string
|
||||
componentRevisionName:
|
||||
description: ComponentRevisionName of current component
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: ObservedGeneration indicates the generation observed by the appconfig controller. The same field is also recorded in the annotations of workloads. A workload is possible to be deleted from cluster after created. This field is useful to track the observed generation of workloads after they are deleted.
|
||||
format: int64
|
||||
type: integer
|
||||
dependencyUnsatisfied:
|
||||
description: DependencyUnsatisfied notify does the workload has dependency unsatisfied
|
||||
type: boolean
|
||||
scopes:
|
||||
description: Scopes associated with this workload.
|
||||
items:
|
||||
@@ -430,6 +432,13 @@ spec:
|
||||
items:
|
||||
description: A WorkloadTrait represents a trait associated with a workload and its status
|
||||
properties:
|
||||
appliedGeneration:
|
||||
description: AppliedGeneration indicates the generation observed by the appConfig controller. The same field is also recorded in the annotations of traits. A trait is possible to be deleted from cluster after created. This field is useful to track the observed generation of traits after they are deleted.
|
||||
format: int64
|
||||
type: integer
|
||||
dependencyUnsatisfied:
|
||||
description: DependencyUnsatisfied notify does the trait has dependency unsatisfied
|
||||
type: boolean
|
||||
message:
|
||||
description: Message will allow controller to leave some additional information for this trait
|
||||
type: string
|
||||
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
listKind: ScopeDefinitionList
|
||||
plural: scopedefinitions
|
||||
singular: scopedefinition
|
||||
scope: Cluster
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.definitionRef.name
|
||||
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
listKind: TraitDefinitionList
|
||||
plural: traitdefinitions
|
||||
singular: traitdefinition
|
||||
scope: Cluster
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.definitionRef.name
|
||||
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
listKind: WorkloadDefinitionList
|
||||
plural: workloaddefinitions
|
||||
singular: workloaddefinition
|
||||
scope: Cluster
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.definitionRef.name
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: containerizedworkloads.core.oam.dev
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
spec:
|
||||
definitionRef:
|
||||
name: containerizedworkloads.core.oam.dev
|
||||
|
||||
@@ -2,7 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: ScopeDefinition
|
||||
metadata:
|
||||
name: healthscopes.core.oam.dev
|
||||
namespace: default
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
spec:
|
||||
workloadRefsPath: spec.workloadRefs
|
||||
allowComponentOverlap: true
|
||||
|
||||
@@ -6,6 +6,7 @@ metadata:
|
||||
definition.oam.dev/description: "Configures K8s ingress and service to enable web traffic for your service.
|
||||
Please use route trait in cap center for advanced usage."
|
||||
name: ingress
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
spec:
|
||||
status:
|
||||
customStatus: |-
|
||||
|
||||
@@ -5,6 +5,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Configures replicas for your service."
|
||||
name: scaler
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
|
||||
@@ -3,6 +3,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: task
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes jobs that run code or a script to completion."
|
||||
spec:
|
||||
|
||||
@@ -3,6 +3,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: webservice
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
|
||||
If workload type is skipped for any service defined in Appfile, it will be defaulted to `webservice` type."
|
||||
|
||||
@@ -3,6 +3,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic."
|
||||
spec:
|
||||
|
||||
@@ -84,3 +84,5 @@ certificate:
|
||||
secretName: webhook-server-cert
|
||||
mountPath: /etc/k8s-webhook-certs
|
||||
caBundle: replace-me
|
||||
|
||||
systemDefinitionNamespace: vela-system
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
oamcontroller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
|
||||
oamv1alpha2 "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/system"
|
||||
oamwebhook "github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev"
|
||||
velawebhook "github.com/oam-dev/kubevela/pkg/webhook/standard.oam.dev"
|
||||
@@ -105,6 +106,7 @@ func main() {
|
||||
flag.StringVar(&storageDriver, "storage-driver", driver.LocalDriverName, "Application file save to the storage driver")
|
||||
flag.DurationVar(&syncPeriod, "informer-re-sync-interval", 5*time.Minute,
|
||||
"controller shared informer lister full re-sync period")
|
||||
flag.StringVar(&oam.SystemDefinitonNamespace, "system-definition-namespace", "vela-system", "define the namespace of the system-level definition")
|
||||
flag.Parse()
|
||||
|
||||
// setup logging
|
||||
@@ -126,8 +128,12 @@ func main() {
|
||||
|
||||
setupLog.Info(fmt.Sprintf("KubeVela Version: %s, GIT Revision: %s.", version.VelaVersion, version.GitRevision))
|
||||
setupLog.Info(fmt.Sprintf("Disable Capabilities: %s.", disableCaps))
|
||||
setupLog.Info(fmt.Sprintf("core init with definition namespace %s", oam.SystemDefinitonNamespace))
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
restConfig := ctrl.GetConfigOrDie()
|
||||
restConfig.UserAgent = kubevelaName + "/" + version.GitRevision
|
||||
|
||||
mgr, err := ctrl.NewManager(restConfig, ctrl.Options{
|
||||
Scheme: scheme,
|
||||
MetricsBindAddress: metricsAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
|
||||
@@ -130,8 +130,13 @@ The same mechanism also works for Trait as well as Workload.
|
||||
|
||||
### Apply Once Only Force
|
||||
|
||||
Based on the same mechanism as `apply-once-only`, `apply-once-only-force` allows to skip re-creating a workload or trait that has already been DELETED from the cluster if its spec is not changed.
|
||||
It's regarded as a stronger case of `apply-once-only`.
|
||||
Based on the same mechanism as `apply-once-only`, `apply-once-only-force` has a more strict method for apply only once.
|
||||
|
||||
It allows to skip re-creating a workload or trait that has already been DELETED from the cluster if its spec is not changed.
|
||||
|
||||
Besides the condition in `apply-once-only`, `apply-once-only-force` has one more condition:
|
||||
|
||||
- if the component revision not changed, the workload will not be applied.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@@ -29,6 +29,7 @@ require (
|
||||
github.com/google/go-github/v32 v32.1.0
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174
|
||||
github.com/klauspost/compress v1.10.5 // indirect
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mholt/archiver/v3 v3.3.0
|
||||
@@ -38,7 +39,6 @@ require (
|
||||
github.com/onsi/ginkgo v1.13.0
|
||||
github.com/onsi/gomega v1.10.3
|
||||
github.com/openkruise/kruise-api v0.7.0
|
||||
github.com/openservicemesh/osm v0.3.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
|
||||
github.com/spf13/cobra v1.1.1
|
||||
|
||||
74
go.sum
74
go.sum
@@ -81,7 +81,6 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
|
||||
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI=
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
|
||||
github.com/AlekSi/gocov-xml v0.0.0-20190121064608-3a14fb1c4737/go.mod h1:w1KSuh2JgIL3nyRiZijboSUwbbxOrTzWwyWVFUHtXBQ=
|
||||
github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU=
|
||||
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
|
||||
github.com/Azure/azure-pipeline-go v0.1.9/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
|
||||
@@ -95,7 +94,6 @@ github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
|
||||
github.com/Azure/azure-sdk-for-go v28.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v34.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v36.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
@@ -127,9 +125,7 @@ github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMl
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.3 h1:O1AGG9Xig71FxdX9HO5pGNyZ7TbSyHaVg+5eJO/jSGw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
|
||||
@@ -161,7 +157,6 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
|
||||
github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/GoogleCloudPlatform/cloud-builders/gcs-fetcher v0.0.0-20191203181535-308b93ad1f39/go.mod h1:yfGmCjKuUzk9WzubMlW2zwjhCraIc/J+M40cufdemRM=
|
||||
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
|
||||
@@ -284,7 +279,6 @@ github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU
|
||||
github.com/aws/aws-sdk-go v1.31.12 h1:SxRRGyhlCagI0DYkhOg+FgdXGXzRTE3vEX/gsgFaiKQ=
|
||||
github.com/aws/aws-sdk-go v1.31.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/axw/gocov v1.0.0/go.mod h1:LvQpEYiwwIb2nYkXY2fDWhg9/AsYqkhmrCshjlUJECE=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20190917191645-69366ca98f89/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
|
||||
@@ -361,8 +355,6 @@ github.com/cloudevents/sdk-go v0.0.0-20190509003705-56931988abe3/go.mod h1:j1nZW
|
||||
github.com/cloudevents/sdk-go v1.0.0/go.mod h1:3TkmM0cFqkhCHOq5JzzRU/RxRkwzoS8TZ+G448qVTog=
|
||||
github.com/cloudevents/sdk-go/v2 v2.0.0/go.mod h1:3CTrpB4+u7Iaj6fd7E2Xvm5IxMdRoaAhqaRVnOr2rCU=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE=
|
||||
@@ -419,7 +411,6 @@ github.com/crossplane/crossplane-runtime v0.10.0 h1:H8YvMcrm1uzZYpwU/BpxjRQfceVu
|
||||
github.com/crossplane/crossplane-runtime v0.10.0/go.mod h1:cJl5ZZONisre4v6wTmbrC8Jh3AI+erq/lNaxZzv9tnU=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/daixiang0/gci v0.0.0-20200727065011-66f1df783cb2/go.mod h1:+AV8KmHTGxxwp/pY84TLQfFKp2vuKXXJVzF3kD/hfR4=
|
||||
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -430,7 +421,6 @@ github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9r
|
||||
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/deislabs/oras v0.8.1 h1:If674KraJVpujYR00rzdi0QAmW4BxzMJPVAZJKuhQ0c=
|
||||
github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As=
|
||||
github.com/denis-tingajkin/go-header v0.3.1/go.mod h1:sq/2IxMhaZX+RRcgHfCRx/m0M5na0fBt4/CRe7Lrji0=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190111225525-2fea367d496d/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
@@ -504,7 +494,6 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.6/go.mod h1:GFqM7v0B62MraO4PWRedIbhThr/Rf7ev6aHOOPXeaDA=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@@ -522,7 +511,6 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fatih/structtag v1.1.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
@@ -569,7 +557,6 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T
|
||||
github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I=
|
||||
github.com/go-critic/go-critic v0.4.1/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g=
|
||||
github.com/go-critic/go-critic v0.4.3/go.mod h1:j4O3D4RoIwRqlZw5jJpx0BNfXWWbpcJoKu5cYSe4YmQ=
|
||||
github.com/go-critic/go-critic v0.5.0/go.mod h1:4jeRh3ZAVnRYhuWdOEvwzVqLUpxMSoAT0xZ74JsTPlo=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||
@@ -583,7 +570,6 @@ github.com/go-ini/ini v1.55.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
@@ -703,7 +689,6 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
||||
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
|
||||
@@ -831,7 +816,6 @@ github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d/go.mod h1:ozx7R9S
|
||||
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
|
||||
github.com/golangci/golangci-lint v1.23.7/go.mod h1:g/38bxfhp4rI7zeWSxcdIeHTQGS58TCak8FYcyCmavQ=
|
||||
github.com/golangci/golangci-lint v1.27.0/go.mod h1:+eZALfxIuthdrHPtfM7w/R3POJLjHDfJJw8XZl9xOng=
|
||||
github.com/golangci/golangci-lint v1.30.0/go.mod h1:5t0i3wHlqQc9deBBvZsP+a/4xz7cfjV+zhp5U0Mzp14=
|
||||
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
|
||||
@@ -932,14 +916,12 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
|
||||
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||
github.com/googleapis/gnostic v0.4.0 h1:BXDUo8p/DaxC+4FJY/SSx3gvnx9C1VdHNgaUkiEL5mk=
|
||||
github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||
github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
|
||||
@@ -1012,8 +994,6 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-getter v1.4.0 h1:ENHNi8494porjD0ZhIrjlAHnveSFhY7hvOJrV/fsKkw=
|
||||
github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
@@ -1027,9 +1007,7 @@ github.com/hashicorp/go-multierror v0.0.0-20171204182908-b7773ae21874/go.mod h1:
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
@@ -1045,7 +1023,6 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
@@ -1069,12 +1046,6 @@ github.com/hashicorp/memberlist v0.2.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.8.5/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k=
|
||||
github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU=
|
||||
github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU=
|
||||
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
|
||||
github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8=
|
||||
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
@@ -1118,7 +1089,6 @@ github.com/jenkins-x/go-scm v1.5.117/go.mod h1:PCT338UhP/pQ0IeEeMEf/hoLTYKcH7qjG
|
||||
github.com/jessevdk/go-flags v0.0.0-20180331124232-1c38ed7ad0cc/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
|
||||
github.com/jinzhu/gorm v0.0.0-20170316141641-572d0a0ab1eb/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
|
||||
github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
@@ -1183,7 +1153,6 @@ github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
|
||||
github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
|
||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc=
|
||||
github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
@@ -1222,7 +1191,6 @@ github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LE
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyoh86/exportloopref v0.1.7/go.mod h1:h1rDl2Kdj97+Kwh4gdz3ujE7XHmH51Q0lUiZ1z4NLj8=
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4=
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||
@@ -1267,7 +1235,6 @@ github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzfe
|
||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/matm/gocov-html v0.0.0-20200509184451-71874e2e203b/go.mod h1:zha4ZSIA/qviBBKx3j6tJG/Lx6aIdjOXPWuKAcJchQM=
|
||||
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -1340,16 +1307,12 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
|
||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
||||
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 h1:hOY53G+kBFhbYFpRVxHl5eS7laP6B1+Cq+Z9Dry1iMU=
|
||||
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.1 h1:L60q1+q7cXE4JeEJJKMnh2brFIe3rZxCihYAB61ypAY=
|
||||
@@ -1408,7 +1371,6 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96d
|
||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nishanths/exhaustive v0.0.0-20200708172631-8866003e3856/go.mod h1:wBEpHwM2OdmeNpdCvRPUlkEbBuaFmcK4Wv8Q7FuGW3c=
|
||||
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
||||
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
@@ -1474,8 +1436,6 @@ github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.m
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/openkruise/kruise-api v0.7.0 h1:BBQotEfeZ2l1+R0uvlsVK2FN8C4RTlG+JT86ba2hOR4=
|
||||
github.com/openkruise/kruise-api v0.7.0/go.mod h1:nCf5vVOjQJX5OaV7Qi0Z51/Rn9cd7s5kVrg8YLgFp1I=
|
||||
github.com/openservicemesh/osm v0.3.0 h1:U88Nv1xm+7M+xYNkwjYVU6WSMp3MHIObTcO+gH20nOw=
|
||||
github.com/openservicemesh/osm v0.3.0/go.mod h1:gyK0vN5ENnP26Y8huqgeTR52fOotZhnEorie215FnpU=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
@@ -1516,7 +1476,6 @@ github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw=
|
||||
github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/errors v0.0.0-20180311214515-816c9085562c/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -1606,7 +1565,6 @@ github.com/prometheus/statsd_exporter v0.15.0/go.mod h1:Dv8HnkoLQkeEjkIE4/2ndAA7
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20190706150252-9beb055b7962/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
@@ -1624,9 +1582,6 @@ github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
|
||||
github.com/rogpeppe/go-internal v1.6.0 h1:IZRgg4sfrDH7nsAD1Y/Nwj+GzIfEwpJSLjCaNC3SbsI=
|
||||
github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3 h1:xkBtI5JktwbW/vf4vopBbhYsRFTGfQWHYXzC0/qYwxI=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc=
|
||||
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
|
||||
@@ -1636,11 +1591,8 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryancurrah/gomodguard v1.0.4/go.mod h1:9T/Cfuxs5StfsocWr4WzDL36HqnX0fVb9d5fSEaLhoE=
|
||||
github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUccKSJBU0UMXJFVM=
|
||||
github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4=
|
||||
@@ -1657,13 +1609,10 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
||||
github.com/securego/gosec v0.0.0-20200103095621-79fbf3af8d83/go.mod h1:vvbZ2Ae7AzSq3/kywjUDxSNq2SJ27RxCz2un0H3ePqE=
|
||||
github.com/securego/gosec v0.0.0-20200401082031-e946c8c39989/go.mod h1:i9l/TNj+yDFh9SZXUTvspXTjbFXgZGP/UvhU1S65A4A=
|
||||
github.com/securego/gosec/v2 v2.3.0/go.mod h1:UzeVyUXbxukhLeHKV3VVqo7HdoQR9MrRfFmZYotn8ME=
|
||||
github.com/securego/gosec/v2 v2.4.0/go.mod h1:0/Q4cjmlFDfDUj1+Fib61sc+U5IQb2w+Iv9/C3wPVko=
|
||||
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/servicemeshinterface/smi-sdk-go v0.4.1/go.mod h1:9rsLPBNcqfDNmEgyYwpopn93aE9yz46d2EHFBNOYj/w=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
|
||||
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20180925043049-51d7b505e2e9/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
|
||||
@@ -1699,7 +1648,6 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
|
||||
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
|
||||
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
||||
github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
|
||||
@@ -1737,7 +1685,6 @@ github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfD
|
||||
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/ssgreg/nlreturn/v2 v2.0.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
@@ -1754,8 +1701,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
@@ -1780,7 +1725,6 @@ github.com/tektoncd/plumbing v0.0.0-20200430135134-e53521e1d887/go.mod h1:cZPJIe
|
||||
github.com/tektoncd/plumbing/pipelinerun-logs v0.0.0-20191206114338-712d544c2c21/go.mod h1:S62EUWtqmejjJgUMOGB1CCCHRp6C706laH06BoALkzU=
|
||||
github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
|
||||
github.com/tetafro/godot v0.4.2/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
|
||||
github.com/tetafro/godot v0.4.8/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
|
||||
github.com/thanos-io/thanos v0.11.0/go.mod h1:N/Yes7J68KqvmY+xM6J5CJqEvWIvKSR5sqGtmuD6wDc=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
@@ -1836,9 +1780,7 @@ github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
||||
github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFXQnxHGXy6E=
|
||||
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||
github.com/valyala/quicktemplate v1.5.1/go.mod h1:v7yYWpBEiutDyNfVaph6oC/yKwejzVyTX/2cwwHxyok=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo=
|
||||
github.com/vdemeester/k8s-pkg-credentialprovider v1.13.12-1/go.mod h1:Fko0rTxEtDW2kju5Ky7yFJNS3IcNvW8IPsp4/e9oev0=
|
||||
@@ -1882,7 +1824,6 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.elastic.co/apm v1.5.0/go.mod h1:OdB9sPtM6Vt7oz3VXt7+KR96i9li74qrxBGHTQygFvk=
|
||||
@@ -1959,7 +1900,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -2204,7 +2144,6 @@ golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDq
|
||||
golang.org/x/tools v0.0.0-20190807223507-b346f7fd45de/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190813034749-528a2984e271/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -2238,7 +2177,6 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200115165105-de0b1760071a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -2253,7 +2191,6 @@ golang.org/x/tools v0.0.0-20200303214625-2b0b585e22fe/go.mod h1:o4KQGtdN14AW+yjs
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200321224714-0d839f3cf2ed/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
@@ -2266,20 +2203,15 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200527183253-8e7acdbce89d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200603131246-cc40288be839/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200612220849-54c614fe050c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200701000337-a32c0cb1d5b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200701041122-1837592efa10/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200709181711-e327e1019dfe/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305 h1:yaM5S0KcY0lIoZo7Fl+oi91b/DdlU2zuWpfHrpWbCS0=
|
||||
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200725200936-102e7d357031/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
@@ -2356,7 +2288,6 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181016170114-94acd270e44e/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -2456,7 +2387,6 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -2503,8 +2433,6 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/robfig/cron.v2 v2.0.0-20150107220207-be2e0b0deed5/go.mod h1:hiOFpYm0ZJbusNj2ywpbrXowU3G8U6GIQzqn2mw1UIE=
|
||||
gopkg.in/square/go-jose.v2 v2.0.0-20180411045311-89060dee6a84/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
@@ -2538,7 +2466,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
helm.sh/helm/v3 v3.1.1/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g=
|
||||
helm.sh/helm/v3 v3.2.0/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0=
|
||||
helm.sh/helm/v3 v3.2.4 h1:lz/0ZRkSgyIF+pCo6pjFzap1udCARB1IN6CRfqkpcOg=
|
||||
helm.sh/helm/v3 v3.2.4/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -2753,7 +2680,6 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
mvdan.cc/gofumpt v0.0.0-20200709182408-4fd085cb6d5f/go.mod h1:9VQ397fNXEnF84t90W4r4TRCQK+pg9f8ugVfyj+S26w=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||
|
||||
@@ -8,14 +8,12 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/oam-dev/kubevela/hack/utils"
|
||||
|
||||
"github.com/openservicemesh/osm/pkg/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Path relative to the Makefile where this is invoked.
|
||||
chartPath := filepath.Join("charts", "vela-core")
|
||||
source, err := cli.GetChartSource(chartPath)
|
||||
source, err := utils.GetChartSource(chartPath)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error getting chart source:", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -2,7 +2,16 @@ package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
helm "helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
)
|
||||
|
||||
// FprintZipData converts zip binary contents to a string literal.
|
||||
@@ -28,3 +37,36 @@ func FprintZipData(dest *bytes.Buffer, zipData []byte) {
|
||||
fmt.Fprintf(dest, "\\x%02x", b)
|
||||
}
|
||||
}
|
||||
|
||||
// GetChartSource is a helper to convert a filepath to a chart to a
|
||||
// base64-encoded, gzipped tarball.
|
||||
func GetChartSource(path string) (string, error) {
|
||||
pack := helm.NewPackage()
|
||||
packagedPath, err := pack.Run(path, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(packagedPath)
|
||||
packaged, err := ioutil.ReadFile(packagedPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b64Encoded := bytes.NewBuffer(nil)
|
||||
enc := base64.NewEncoder(base64.StdEncoding, b64Encoded)
|
||||
_, err = io.Copy(enc, bytes.NewReader(packaged))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b64Encoded.String(), nil
|
||||
}
|
||||
|
||||
// LoadChart is a helper to turn a base64-encoded, gzipped tarball into a chart.
|
||||
func LoadChart(source string) (*chart.Chart, error) {
|
||||
dec := base64.NewDecoder(base64.StdEncoding, strings.NewReader(source))
|
||||
tgz := bytes.NewBuffer(nil)
|
||||
_, err := io.Copy(tgz, dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loader.LoadArchive(tgz)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ metadata:
|
||||
definition.oam.dev/description: "Configures K8s ingress and service to enable web traffic for your service.
|
||||
Please use route trait in cap center for advanced usage."
|
||||
name: ingress
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
spec:
|
||||
status:
|
||||
customStatus: |-
|
||||
|
||||
@@ -4,6 +4,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Configures replicas for your service."
|
||||
name: scaler
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: task
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes jobs that run code or a script to completion."
|
||||
spec:
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: webservice
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
|
||||
If workload type is skipped for any service defined in Appfile, it will be defaulted to `webservice` type."
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic."
|
||||
spec:
|
||||
|
||||
@@ -380,16 +380,18 @@ spec:
|
||||
items:
|
||||
description: A WorkloadStatus represents the status of a workload.
|
||||
properties:
|
||||
appliedComponentRevision:
|
||||
description: AppliedComponentRevision indicates the applied component revision name of this workload
|
||||
type: string
|
||||
componentName:
|
||||
description: ComponentName that produced this workload.
|
||||
type: string
|
||||
componentRevisionName:
|
||||
description: ComponentRevisionName of current component
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: ObservedGeneration indicates the generation observed by the appconfig controller. The same field is also recorded in the annotations of workloads. A workload is possible to be deleted from cluster after created. This field is useful to track the observed generation of workloads after they are deleted.
|
||||
format: int64
|
||||
type: integer
|
||||
dependencyUnsatisfied:
|
||||
description: DependencyUnsatisfied notify does the workload has dependency unsatisfied
|
||||
type: boolean
|
||||
scopes:
|
||||
description: Scopes associated with this workload.
|
||||
items:
|
||||
@@ -430,6 +432,13 @@ spec:
|
||||
items:
|
||||
description: A WorkloadTrait represents a trait associated with a workload and its status
|
||||
properties:
|
||||
appliedGeneration:
|
||||
description: AppliedGeneration indicates the generation observed by the appConfig controller. The same field is also recorded in the annotations of traits. A trait is possible to be deleted from cluster after created. This field is useful to track the observed generation of traits after they are deleted.
|
||||
format: int64
|
||||
type: integer
|
||||
dependencyUnsatisfied:
|
||||
description: DependencyUnsatisfied notify does the trait has dependency unsatisfied
|
||||
type: boolean
|
||||
message:
|
||||
description: Message will allow controller to leave some additional information for this trait
|
||||
type: string
|
||||
|
||||
@@ -21,7 +21,7 @@ spec:
|
||||
listKind: ScopeDefinitionList
|
||||
plural: scopedefinitions
|
||||
singular: scopedefinition
|
||||
scope: Cluster
|
||||
scope: Namespaced
|
||||
subresources: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
|
||||
@@ -21,7 +21,7 @@ spec:
|
||||
listKind: TraitDefinitionList
|
||||
plural: traitdefinitions
|
||||
singular: traitdefinition
|
||||
scope: Cluster
|
||||
scope: Namespaced
|
||||
subresources: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
|
||||
@@ -21,7 +21,7 @@ spec:
|
||||
listKind: WorkloadDefinitionList
|
||||
plural: workloaddefinitions
|
||||
singular: workloaddefinition
|
||||
scope: Cluster
|
||||
scope: Namespaced
|
||||
subresources: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
|
||||
@@ -34,7 +34,8 @@ func ApplyTerraform(app *v1alpha2.Application, k8sClient client.Client, ioStream
|
||||
var nativeVelaComponents []v1alpha2.ApplicationComponent
|
||||
// parse template
|
||||
appParser := NewApplicationParser(k8sClient, dm)
|
||||
appFile, err := appParser.GenerateAppFile(app.Name, app)
|
||||
// TODO(wangyike) this context only for compiling success, lately mabey surport setting sysNs and appNs in api-server or cli
|
||||
appFile, err := appParser.GenerateAppFile(context.TODO(), app.Name, app)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse appfile: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package appfile
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
"github.com/pkg/errors"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -130,12 +132,12 @@ func NewApplicationParser(cli client.Client, dm discoverymapper.DiscoveryMapper)
|
||||
}
|
||||
|
||||
// GenerateAppFile converts an application to an Appfile
|
||||
func (p *Parser) GenerateAppFile(name string, app *v1alpha2.Application) (*Appfile, error) {
|
||||
func (p *Parser) GenerateAppFile(ctx context.Context, name string, app *v1alpha2.Application) (*Appfile, error) {
|
||||
appfile := new(Appfile)
|
||||
appfile.Name = name
|
||||
var wds []*Workload
|
||||
for _, comp := range app.Spec.Components {
|
||||
wd, err := p.parseWorkload(comp)
|
||||
wd, err := p.parseWorkload(ctx, comp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -146,12 +148,12 @@ func (p *Parser) GenerateAppFile(name string, app *v1alpha2.Application) (*Appfi
|
||||
return appfile, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseWorkload(comp v1alpha2.ApplicationComponent) (*Workload, error) {
|
||||
func (p *Parser) parseWorkload(ctx context.Context, comp v1alpha2.ApplicationComponent) (*Workload, error) {
|
||||
workload := new(Workload)
|
||||
workload.Traits = []*Trait{}
|
||||
workload.Name = comp.Name
|
||||
workload.Type = comp.WorkloadType
|
||||
templ, err := util.LoadTemplate(p.client, workload.Type, types.TypeWorkload)
|
||||
templ, err := util.LoadTemplate(ctx, p.client, workload.Type, types.TypeWorkload)
|
||||
if err != nil && !kerrors.IsNotFound(err) {
|
||||
return nil, errors.WithMessagef(err, "fetch type of %s", comp.Name)
|
||||
}
|
||||
@@ -169,7 +171,7 @@ func (p *Parser) parseWorkload(comp v1alpha2.ApplicationComponent) (*Workload, e
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("fail to parse properties of %s for %s", traitValue.Name, comp.Name)
|
||||
}
|
||||
trait, err := p.parseTrait(traitValue.Name, properties)
|
||||
trait, err := p.parseTrait(ctx, traitValue.Name, properties)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "component(%s) parse trait(%s)", comp.Name, traitValue.Name)
|
||||
}
|
||||
@@ -177,7 +179,7 @@ func (p *Parser) parseWorkload(comp v1alpha2.ApplicationComponent) (*Workload, e
|
||||
workload.Traits = append(workload.Traits, trait)
|
||||
}
|
||||
for scopeType, instanceName := range comp.Scopes {
|
||||
gvk, err := util.GetScopeGVK(p.client, p.dm, scopeType)
|
||||
gvk, err := util.GetScopeGVK(ctx, p.client, p.dm, scopeType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -189,8 +191,8 @@ func (p *Parser) parseWorkload(comp v1alpha2.ApplicationComponent) (*Workload, e
|
||||
return workload, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseTrait(name string, properties map[string]interface{}) (*Trait, error) {
|
||||
templ, err := util.LoadTemplate(p.client, name, types.TypeTrait)
|
||||
func (p *Parser) parseTrait(ctx context.Context, name string, properties map[string]interface{}) (*Trait, error) {
|
||||
templ, err := util.LoadTemplate(ctx, p.client, name, types.TypeTrait)
|
||||
if kerrors.IsNotFound(err) {
|
||||
return nil, errors.Errorf("trait definition of %s not found", name)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
@@ -240,7 +239,7 @@ var _ = Describe("Test application parser", func() {
|
||||
},
|
||||
}
|
||||
|
||||
appfile, err := NewApplicationParser(&tclient, nil).GenerateAppFile("test", &o)
|
||||
appfile, err := NewApplicationParser(&tclient, nil).GenerateAppFile(context.TODO(), "test", &o)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
|
||||
Expect(equal(expectedExceptApp, appfile)).Should(BeTrue())
|
||||
@@ -426,37 +425,33 @@ var _ = Describe("Test appFile parser", func() {
|
||||
Name: "myweb",
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{"application.oam.dev": "test"},
|
||||
}, Spec: v1alpha2.ComponentSpec{
|
||||
Workload: runtime.RawExtension{
|
||||
Object: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"workload.oam.dev/type": "worker",
|
||||
"app.oam.dev/component": "myweb",
|
||||
"app.oam.dev/name": "test",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app.oam.dev/component": "myweb"}},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"labels": map[string]interface{}{"app.oam.dev/component": "myweb"}},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"command": []interface{}{"sleep", "1000"},
|
||||
"image": "busybox",
|
||||
"name": "myweb",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{"name": "c1", "value": "v1"},
|
||||
map[string]interface{}{"name": "c2", "value": "v2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
expectWorkload := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"workload.oam.dev/type": "worker",
|
||||
"app.oam.dev/component": "myweb",
|
||||
"app.oam.dev/name": "test",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app.oam.dev/component": "myweb"}},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"labels": map[string]interface{}{"app.oam.dev/component": "myweb"}},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"command": []interface{}{"sleep", "1000"},
|
||||
"image": "busybox",
|
||||
"name": "myweb",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{"name": "c1", "value": "v1"},
|
||||
map[string]interface{}{"name": "c2", "value": "v2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -465,12 +460,30 @@ var _ = Describe("Test appFile parser", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
// assertion util cannot compare slices embedded in map correctly while slice order is not required
|
||||
// e.g., .containers[0].env in this case
|
||||
// as a workaround, prepare two expected targets covering all possible slice order
|
||||
// if any one is satisfied, the equal assertion pass
|
||||
expectWorkloadOptional := expectWorkload.DeepCopy()
|
||||
unstructured.SetNestedSlice(expectWorkloadOptional.Object, []interface{}{
|
||||
map[string]interface{}{
|
||||
"command": []interface{}{"sleep", "1000"},
|
||||
"image": "busybox",
|
||||
"name": "myweb",
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{"name": "c2", "value": "v2"},
|
||||
map[string]interface{}{"name": "c1", "value": "v1"},
|
||||
},
|
||||
},
|
||||
}, "spec", "template", "spec", "containers")
|
||||
|
||||
By(" built components' length must be 1")
|
||||
Expect(len(components)).To(BeEquivalentTo(1))
|
||||
Expect(components[0].ObjectMeta).To(BeEquivalentTo(expectComponent.ObjectMeta))
|
||||
Expect(components[0].TypeMeta).To(BeEquivalentTo(expectComponent.TypeMeta))
|
||||
logf.Log.Info(cmp.Diff(components[0].Spec.Workload.Object, expectComponent.Spec.Workload.Object))
|
||||
Expect(assert.ObjectsAreEqual(components[0].Spec.Workload.Object, expectComponent.Spec.Workload.Object)).To(BeTrue())
|
||||
Expect(components[0].Spec.Workload.Object).Should(SatisfyAny(
|
||||
BeEquivalentTo(expectWorkload),
|
||||
BeEquivalentTo(expectWorkloadOptional)))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/appfile"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
type dryRunOptions struct {
|
||||
@@ -52,7 +54,8 @@ func NewDryRunCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command
|
||||
|
||||
parser := appfile.NewApplicationParser(newClient, dm)
|
||||
|
||||
appFile, err := parser.GenerateAppFile(app.Name, app)
|
||||
ctx := oamutil.SetNnamespaceInCtx(context.Background(), app.Namespace)
|
||||
appFile, err := parser.GenerateAppFile(ctx, app.Name, app)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "generate appFile")
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/openservicemesh/osm/pkg/cli"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/hack/utils"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
||||
"github.com/oam-dev/kubevela/pkg/plugins"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/helm"
|
||||
@@ -247,7 +247,7 @@ func InstallOamRuntime(chartPath, chartSource string, vals map[string]interface{
|
||||
ioStreams.Infof("Use customized chart at: %s", chartPath)
|
||||
chartRequested, err = loader.Load(chartPath)
|
||||
} else {
|
||||
chartRequested, err = cli.LoadChart(chartSource)
|
||||
chartRequested, err = utils.LoadChart(chartSource)
|
||||
if chartRequested != nil {
|
||||
m, l := chartRequested.Metadata, len(chartRequested.Raw)
|
||||
ioStreams.Infof("install chart %s, version %s, desc : %s, contains %d file\n", m.Name, m.Version, m.Description, l)
|
||||
|
||||
@@ -26,12 +26,12 @@ const (
|
||||
|
||||
// ApplyOnceOnlyOn indicates workloads and traits should not be affected
|
||||
// if no spec change is made in the ApplicationConfiguration.
|
||||
ApplyOnceOnlyOn = "on"
|
||||
ApplyOnceOnlyOn ApplyOnceOnlyMode = "on"
|
||||
|
||||
// ApplyOnceOnlyForce is a more strong case for ApplyOnceOnly, the workload
|
||||
// and traits won't be affected if no spec change is made in the ApplicationConfiguration,
|
||||
// even if the workload or trait has been deleted from cluster.
|
||||
ApplyOnceOnlyForce = "force"
|
||||
ApplyOnceOnlyForce ApplyOnceOnlyMode = "force"
|
||||
)
|
||||
|
||||
// Args args used by controller
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
core "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
// RolloutReconcileWaitTime is the time to wait before reconcile again an application still in rollout phase
|
||||
@@ -93,7 +94,9 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
// parse template
|
||||
appParser := appfile.NewApplicationParser(r.Client, r.dm)
|
||||
|
||||
appfile, err := appParser.GenerateAppFile(app.Name, app)
|
||||
ctx = oamutil.SetNnamespaceInCtx(ctx, app.Namespace)
|
||||
|
||||
appfile, err := appParser.GenerateAppFile(ctx, app.Name, app)
|
||||
if err != nil {
|
||||
handler.l.Error(err, "[Handle Parse]")
|
||||
app.Status.SetConditions(errorCondition("Parsed", err))
|
||||
|
||||
@@ -651,7 +651,7 @@ var _ = Describe("Test Application Controller", func() {
|
||||
ntd, otd := &v1alpha2.TraitDefinition{}, &v1alpha2.TraitDefinition{}
|
||||
tDDefJson, _ := yaml.YAMLToJSON([]byte(tdDefYamlWithHttp))
|
||||
Expect(json.Unmarshal(tDDefJson, ntd)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "scaler"}, otd)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: ntd.Name, Namespace: ntd.Namespace}, otd)).Should(BeNil())
|
||||
ntd.ResourceVersion = otd.ResourceVersion
|
||||
Expect(k8sClient.Update(ctx, ntd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
@@ -695,13 +695,13 @@ var _ = Describe("Test Application Controller", func() {
|
||||
nwd, owd := &v1alpha2.WorkloadDefinition{}, &v1alpha2.WorkloadDefinition{}
|
||||
wDDefJson, _ := yaml.YAMLToJSON([]byte(wDDefWithHealthYaml))
|
||||
Expect(json.Unmarshal(wDDefJson, nwd)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "worker"}, owd)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: nwd.Name, Namespace: nwd.Namespace}, owd)).Should(BeNil())
|
||||
nwd.ResourceVersion = owd.ResourceVersion
|
||||
Expect(k8sClient.Update(ctx, nwd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
ntd, otd := &v1alpha2.TraitDefinition{}, &v1alpha2.TraitDefinition{}
|
||||
tDDefJson, _ := yaml.YAMLToJSON([]byte(tDDefWithHealthYaml))
|
||||
Expect(json.Unmarshal(tDDefJson, ntd)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "scaler"}, otd)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: ntd.Name, Namespace: ntd.Namespace}, otd)).Should(BeNil())
|
||||
ntd.ResourceVersion = otd.ResourceVersion
|
||||
Expect(k8sClient.Update(ctx, ntd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
compName := "myweb-health"
|
||||
@@ -995,7 +995,7 @@ const (
|
||||
kind: ScopeDefinition
|
||||
metadata:
|
||||
name: healthscopes.core.oam.dev
|
||||
namespace: default
|
||||
namespace: vela-system
|
||||
spec:
|
||||
workloadRefsPath: spec.workloadRefs
|
||||
allowComponentOverlap: true
|
||||
@@ -1007,6 +1007,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: vela-system
|
||||
annotations:
|
||||
definition.oam.dev/description: "Long-running scalable backend worker without network endpoint"
|
||||
spec:
|
||||
@@ -1066,6 +1067,7 @@ spec:
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: webserver
|
||||
namespace: vela-system
|
||||
annotations:
|
||||
definition.oam.dev/description: "webserver was composed by deployment and service"
|
||||
spec:
|
||||
@@ -1157,6 +1159,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: vela-system
|
||||
annotations:
|
||||
definition.oam.dev/description: "Long-running scalable backend worker without network endpoint"
|
||||
spec:
|
||||
@@ -1217,6 +1220,7 @@ spec:
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: nworker
|
||||
namespace: vela-system
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic."
|
||||
spec:
|
||||
@@ -1286,6 +1290,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: scaler
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
@@ -1315,6 +1320,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: scaler
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
@@ -1359,6 +1365,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: scaler
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
@@ -1387,6 +1394,7 @@ spec:
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: ingress
|
||||
namespace: vela-system
|
||||
spec:
|
||||
status:
|
||||
customStatus: |-
|
||||
|
||||
@@ -17,12 +17,16 @@ limitations under the License.
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -61,9 +65,16 @@ var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
|
||||
|
||||
By("bootstrapping test environment")
|
||||
var yamlPath string
|
||||
if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
|
||||
yamlPath = "../../../../../test/compatibility-test/testdata"
|
||||
} else {
|
||||
yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
|
||||
}
|
||||
logf.Log.Info("start application suit test", "yaml_path", yamlPath)
|
||||
testEnv = &envtest.Environment{
|
||||
UseExistingCluster: pointer.BoolPtr(false),
|
||||
CRDDirectoryPaths: []string{filepath.Join("../../../../..", "charts", "vela-core", "crds")},
|
||||
CRDDirectoryPaths: []string{yamlPath},
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -89,6 +100,8 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Scheme: testScheme,
|
||||
dm: dm,
|
||||
}
|
||||
definitonNs := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}}
|
||||
Expect(k8sClient.Create(context.Background(), definitonNs.DeepCopy())).Should(BeNil())
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/event"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/meta"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/resource"
|
||||
@@ -102,7 +103,7 @@ func Setup(mgr ctrl.Manager, args core.Args, l logging.Logger) error {
|
||||
CustomRevisionHookURL: args.CustomRevisionHookURL,
|
||||
}).
|
||||
Complete(NewReconciler(mgr, dm,
|
||||
WithLogger(l.WithValues("controller", name)),
|
||||
l.WithValues("controller", name),
|
||||
WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))),
|
||||
WithApplyOnceOnlyMode(args.ApplyMode)))
|
||||
}
|
||||
@@ -148,13 +149,6 @@ func WithGarbageCollector(gc GarbageCollector) ReconcilerOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger specifies how the Reconciler should log messages.
|
||||
func WithLogger(l logging.Logger) ReconcilerOption {
|
||||
return func(r *OAMApplicationReconciler) {
|
||||
r.log = l
|
||||
}
|
||||
}
|
||||
|
||||
// WithRecorder specifies how the Reconciler should record events.
|
||||
func WithRecorder(er event.Recorder) ReconcilerOption {
|
||||
return func(r *OAMApplicationReconciler) {
|
||||
@@ -186,7 +180,7 @@ func WithApplyOnceOnlyMode(mode core.ApplyOnceOnlyMode) ReconcilerOption {
|
||||
|
||||
// NewReconciler returns an OAMApplicationReconciler that reconciles ApplicationConfigurations
|
||||
// by rendering and instantiating their Components and Traits.
|
||||
func NewReconciler(m ctrl.Manager, dm discoverymapper.DiscoveryMapper, o ...ReconcilerOption) *OAMApplicationReconciler {
|
||||
func NewReconciler(m ctrl.Manager, dm discoverymapper.DiscoveryMapper, log logging.Logger, o ...ReconcilerOption) *OAMApplicationReconciler {
|
||||
r := &OAMApplicationReconciler{
|
||||
client: m.GetClient(),
|
||||
scheme: m.GetScheme(),
|
||||
@@ -198,12 +192,12 @@ func NewReconciler(m ctrl.Manager, dm discoverymapper.DiscoveryMapper, o ...Reco
|
||||
trait: ResourceRenderFn(renderTrait),
|
||||
},
|
||||
workloads: &workloads{
|
||||
applicator: apply.NewAPIApplicator(m.GetClient()),
|
||||
applicator: apply.NewAPIApplicator(m.GetClient(), log),
|
||||
rawClient: m.GetClient(),
|
||||
dm: dm,
|
||||
},
|
||||
gc: GarbageCollectorFn(eligible),
|
||||
log: logging.NewNopLogger(),
|
||||
log: log,
|
||||
record: event.NewNopRecorder(),
|
||||
preHooks: make(map[string]ControllerHooks),
|
||||
postHooks: make(map[string]ControllerHooks),
|
||||
@@ -236,6 +230,7 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
|
||||
}
|
||||
acPatch := ac.DeepCopy()
|
||||
|
||||
ctx = util.SetNnamespaceInCtx(ctx, ac.Namespace)
|
||||
if ac.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
if registerFinalizers(ac) {
|
||||
log.Debug("Register new finalizers", "finalizers", ac.ObjectMeta.Finalizers)
|
||||
@@ -252,6 +247,14 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
|
||||
return reconcile.Result{}, errors.Wrap(r.client.Update(ctx, ac), errUpdateAppConfigStatus)
|
||||
}
|
||||
|
||||
// make sure this is the last functional defer function to be called
|
||||
defer func() {
|
||||
// Make sure if error occurs, reconcile will not happen too frequency
|
||||
if returnErr != nil {
|
||||
result.RequeueAfter = 0
|
||||
}
|
||||
}()
|
||||
|
||||
// execute the posthooks at the end no matter what
|
||||
defer func() {
|
||||
updateObservedGeneration(ac)
|
||||
@@ -268,11 +271,6 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
|
||||
r.record.Event(ac, event.Normal(reasonExecutePosthook, "Successfully executed a posthook", "posthook name", name))
|
||||
}
|
||||
returnErr = errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
|
||||
|
||||
// Make sure if error occurs, reconcile will not happen too frequency
|
||||
if returnErr != nil && result.RequeueAfter < shortWait {
|
||||
result.RequeueAfter = shortWait
|
||||
}
|
||||
}()
|
||||
|
||||
// execute the prehooks
|
||||
@@ -299,7 +297,7 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
|
||||
log.Debug("Successfully rendered components", "workloads", len(workloads))
|
||||
r.record.Event(ac, event.Normal(reasonRenderComponents, "Successfully rendered components", "workloads", strconv.Itoa(len(workloads))))
|
||||
|
||||
applyOpts := []apply.ApplyOption{apply.MustBeControllableBy(ac.GetUID()), applyOnceOnly(ac, r.applyOnceOnlyMode)}
|
||||
applyOpts := []apply.ApplyOption{apply.MustBeControllableBy(ac.GetUID()), applyOnceOnly(ac, r.applyOnceOnlyMode, log)}
|
||||
if err := r.workloads.Apply(ctx, ac.Status.Workloads, workloads, applyOpts...); err != nil {
|
||||
log.Debug("Cannot apply components", "error", err, "requeue-after", time.Now().Add(shortWait))
|
||||
r.record.Event(ac, event.Warning(reasonCannotApplyComponents, err))
|
||||
@@ -320,8 +318,15 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
|
||||
log := log.WithValues("kind", e.GetKind(), "name", e.GetName())
|
||||
record := r.record.WithAnnotations("kind", e.GetKind(), "name", e.GetName())
|
||||
|
||||
err := r.confirmDeleteOnApplyOnceMode(ctx, ac.GetNamespace(), &e)
|
||||
if err != nil {
|
||||
log.Debug("confirm component can't be garbage collected", "error", err)
|
||||
record.Event(ac, event.Warning(reasonCannotGGComponents, err))
|
||||
ac.SetConditions(v1alpha1.ReconcileError(errors.Wrap(err, errGCComponent)))
|
||||
return errResult, errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
|
||||
}
|
||||
if err := r.client.Delete(ctx, &e); resource.IgnoreNotFound(err) != nil {
|
||||
log.Debug("Cannot garbage collect component", "error", err, "requeue-after", time.Now().Add(shortWait))
|
||||
log.Debug("Cannot garbage collect component", "error", err)
|
||||
record.Event(ac, event.Warning(reasonCannotGGComponents, err))
|
||||
ac.SetConditions(v1alpha1.ReconcileError(errors.Wrap(err, errGCComponent)))
|
||||
return errResult, errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
|
||||
@@ -344,6 +349,41 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
|
||||
return reconcile.Result{RequeueAfter: waitTime}, nil
|
||||
}
|
||||
|
||||
// confirmDeleteOnApplyOnceMode will confirm whether the workload can be delete or not in apply once only enabled mode
|
||||
// currently only workload replicas with 0 can be delete
|
||||
func (r *OAMApplicationReconciler) confirmDeleteOnApplyOnceMode(ctx context.Context, namespace string, u *unstructured.Unstructured) error {
|
||||
if r.applyOnceOnlyMode == core.ApplyOnceOnlyOff {
|
||||
return nil
|
||||
}
|
||||
getU := u.DeepCopy()
|
||||
err := r.client.Get(ctx, client.ObjectKey{Name: u.GetName(), Namespace: namespace}, getU)
|
||||
if err != nil {
|
||||
// no need to check if workload not found
|
||||
return resource.IgnoreNotFound(err)
|
||||
}
|
||||
// only check for workload
|
||||
if labels := getU.GetLabels(); labels == nil || labels[oam.LabelOAMResourceType] != oam.ResourceTypeWorkload {
|
||||
return nil
|
||||
}
|
||||
paved := fieldpath.Pave(getU.Object)
|
||||
|
||||
// TODO: add more kinds of workload replica check here if needed
|
||||
// "spec.replicas" maybe not accurate for all kinds of workload, but it work for most of them(including Deployment/StatefulSet/CloneSet).
|
||||
// For workload which don't align with the `spec.replicas` schema, the check won't work
|
||||
replicas, err := paved.GetInteger("spec.replicas")
|
||||
if err != nil {
|
||||
// it's possible for workload without the `spec.replicas`, it's omitempty
|
||||
if strings.Contains(err.Error(), "no such field") {
|
||||
return nil
|
||||
}
|
||||
return errors.WithMessage(err, "fail to get 'spec.replicas' from workload")
|
||||
}
|
||||
if replicas > 0 {
|
||||
return errors.Errorf("can't delete workload with replicas %d in apply once only mode", replicas)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateStatus updates v1alpha2.ApplicationConfiguration's Status with retry.RetryOnConflict
|
||||
func (r *OAMApplicationReconciler) UpdateStatus(ctx context.Context, ac *v1alpha2.ApplicationConfiguration, opts ...client.UpdateOption) error {
|
||||
status := ac.DeepCopy().Status
|
||||
@@ -361,7 +401,6 @@ func (r *OAMApplicationReconciler) updateStatus(ctx context.Context, ac, acPatch
|
||||
historyWorkloads := make([]v1alpha2.HistoryWorkload, 0)
|
||||
for i, w := range workloads {
|
||||
ac.Status.Workloads[i] = workloads[i].Status()
|
||||
ac.Status.Workloads[i].ObservedGeneration = ac.GetGeneration()
|
||||
if !w.RevisionEnabled {
|
||||
continue
|
||||
}
|
||||
@@ -398,6 +437,17 @@ func updateObservedGeneration(ac *v1alpha2.ApplicationConfiguration) {
|
||||
if ac.Status.ObservedGeneration != ac.Generation {
|
||||
ac.Status.ObservedGeneration = ac.Generation
|
||||
}
|
||||
for i, w := range ac.Status.Workloads {
|
||||
// only the workload meet all dependency can mean the generation applied successfully
|
||||
if w.AppliedComponentRevision != w.ComponentRevisionName && !w.DependencyUnsatisfied {
|
||||
ac.Status.Workloads[i].AppliedComponentRevision = w.ComponentRevisionName
|
||||
}
|
||||
for j, t := range w.Traits {
|
||||
if t.AppliedGeneration != ac.Generation && !t.DependencyUnsatisfied {
|
||||
ac.Status.Workloads[i].Traits[j].AppliedGeneration = ac.Generation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func patchExtraStatusField(acStatus *v1alpha2.ApplicationConfigurationStatus, acPatchStatus v1alpha2.ApplicationConfigurationStatus) {
|
||||
@@ -485,6 +535,7 @@ func (w Workload) Status() v1alpha2.WorkloadStatus {
|
||||
acw := v1alpha2.WorkloadStatus{
|
||||
ComponentName: w.ComponentName,
|
||||
ComponentRevisionName: w.ComponentRevisionName,
|
||||
DependencyUnsatisfied: w.HasDep,
|
||||
Reference: v1alpha1.TypedReference{
|
||||
APIVersion: w.Workload.GetAPIVersion(),
|
||||
Kind: w.Workload.GetKind(),
|
||||
@@ -502,6 +553,7 @@ func (w Workload) Status() v1alpha2.WorkloadStatus {
|
||||
Kind: w.Traits[i].Object.GetKind(),
|
||||
Name: w.Traits[i].Object.GetName(),
|
||||
}
|
||||
acw.Traits[i].DependencyUnsatisfied = tr.HasDep
|
||||
}
|
||||
for i, s := range w.Scopes {
|
||||
acw.Scopes[i].Reference = v1alpha1.TypedReference{
|
||||
@@ -529,9 +581,20 @@ func (fn GarbageCollectorFn) Eligible(namespace string, ws []v1alpha2.WorkloadSt
|
||||
}
|
||||
|
||||
// IsRevisionWorkload check is a workload is an old revision Workload which shouldn't be garbage collected.
|
||||
// TODO(wonderflow): Do we have a better way to recognise it's a revisionWorkload which can't be garbage collected by AppConfig?
|
||||
func IsRevisionWorkload(status v1alpha2.WorkloadStatus) bool {
|
||||
return strings.HasPrefix(status.Reference.Name, status.ComponentName+"-")
|
||||
func IsRevisionWorkload(status v1alpha2.WorkloadStatus, w []Workload) bool {
|
||||
if strings.HasPrefix(status.Reference.Name, status.ComponentName+"-") {
|
||||
// for compatibility, keep the old way
|
||||
return true
|
||||
}
|
||||
|
||||
// check all workload, with same componentName
|
||||
for _, wr := range w {
|
||||
if wr.ComponentName == status.ComponentName {
|
||||
return wr.RevisionEnabled
|
||||
}
|
||||
}
|
||||
// component not found, should be deleted
|
||||
return false
|
||||
}
|
||||
|
||||
func eligible(namespace string, ws []v1alpha2.WorkloadStatus, w []Workload) []unstructured.Unstructured {
|
||||
@@ -555,7 +618,7 @@ func eligible(namespace string, ws []v1alpha2.WorkloadStatus, w []Workload) []un
|
||||
eligible := make([]unstructured.Unstructured, 0)
|
||||
for _, s := range ws {
|
||||
|
||||
if !applied[s.Reference] && !IsRevisionWorkload(s) {
|
||||
if !applied[s.Reference] && !IsRevisionWorkload(s, w) {
|
||||
w := &unstructured.Unstructured{}
|
||||
w.SetAPIVersion(s.Reference.APIVersion)
|
||||
w.SetKind(s.Reference.Kind)
|
||||
@@ -591,12 +654,11 @@ func (e *GenerationUnchanged) Error() string {
|
||||
|
||||
// applyOnceOnly is an ApplyOption that controls the applying mechanism for workload and trait.
|
||||
// More detail refers to the ApplyOnceOnlyMode type annotation
|
||||
func applyOnceOnly(ac *v1alpha2.ApplicationConfiguration, mode core.ApplyOnceOnlyMode) apply.ApplyOption {
|
||||
func applyOnceOnly(ac *v1alpha2.ApplicationConfiguration, mode core.ApplyOnceOnlyMode, log logging.Logger) apply.ApplyOption {
|
||||
return func(_ context.Context, existing, desired runtime.Object) error {
|
||||
if mode == core.ApplyOnceOnlyOff {
|
||||
return nil
|
||||
}
|
||||
|
||||
d, _ := desired.(metav1.Object)
|
||||
if d == nil {
|
||||
return errors.Errorf("cannot access metadata of object being applied: %q",
|
||||
@@ -608,6 +670,7 @@ func applyOnceOnly(ac *v1alpha2.ApplicationConfiguration, mode core.ApplyOnceOnl
|
||||
dLabels[oam.LabelOAMResourceType] != oam.ResourceTypeTrait {
|
||||
// this ApplyOption only works for workload and trait
|
||||
// skip if the resource is not workload nor trait, e.g., scope
|
||||
log.Info("ignore apply only once check, because resourceType is not workload or trait", oam.LabelOAMResourceType, dLabels[oam.LabelOAMResourceType])
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -615,41 +678,54 @@ func applyOnceOnly(ac *v1alpha2.ApplicationConfiguration, mode core.ApplyOnceOnl
|
||||
if existing == nil {
|
||||
if mode != core.ApplyOnceOnlyForce {
|
||||
// non-force mode will always create the resource if not exist.
|
||||
log.Info("apply only once with mode:" + string(mode) + ", but old resource not exist, will create a new one")
|
||||
return nil
|
||||
}
|
||||
|
||||
createdBefore := false
|
||||
var appliedRevision, appliedGeneration string
|
||||
for _, w := range ac.Status.Workloads {
|
||||
// traverse recorded workloads to find the one matching applied resource
|
||||
if w.Reference.GetObjectKind().GroupVersionKind() == desired.GetObjectKind().GroupVersionKind() &&
|
||||
w.Reference.Name == d.GetName() {
|
||||
// the workload matches applied resource
|
||||
createdBefore = true
|
||||
// for workload, when revision enabled, only when revision changed that can trigger to create a new one
|
||||
if dLabels[oam.LabelOAMResourceType] == oam.ResourceTypeWorkload && w.AppliedComponentRevision == dLabels[oam.LabelAppComponentRevision] {
|
||||
// the revision is not changed, so return an error to abort creating it
|
||||
return &GenerationUnchanged{}
|
||||
}
|
||||
appliedRevision = w.AppliedComponentRevision
|
||||
break
|
||||
}
|
||||
if !createdBefore {
|
||||
// the workload is not matched, then traverse its traits to find matching one
|
||||
for _, t := range w.Traits {
|
||||
if t.Reference.GetObjectKind().GroupVersionKind() == desired.GetObjectKind().GroupVersionKind() &&
|
||||
t.Reference.Name == d.GetName() {
|
||||
// the trait matches applied resource
|
||||
createdBefore = true
|
||||
// the workload is not matched, then traverse its traits to find matching one
|
||||
for _, t := range w.Traits {
|
||||
if t.Reference.GetObjectKind().GroupVersionKind() == desired.GetObjectKind().GroupVersionKind() &&
|
||||
t.Reference.Name == d.GetName() {
|
||||
// the trait matches applied resource
|
||||
createdBefore = true
|
||||
// the resource was created before and appConfig status recorded the resource version applied
|
||||
// if recorded AppliedGeneration and ComponentRevisionName both equal to the applied resource's,
|
||||
// that means its spec is not changed
|
||||
if dLabels[oam.LabelOAMResourceType] == oam.ResourceTypeTrait &&
|
||||
w.ComponentRevisionName == dLabels[oam.LabelAppComponentRevision] &&
|
||||
strconv.FormatInt(t.AppliedGeneration, 10) == dAnnots[oam.AnnotationAppGeneration] {
|
||||
// the revision is not changed, so return an error to abort creating it
|
||||
return &GenerationUnchanged{}
|
||||
}
|
||||
appliedGeneration = strconv.FormatInt(t.AppliedGeneration, 10)
|
||||
break
|
||||
}
|
||||
}
|
||||
// don't use if-else here because it will miss the case that the resource is a trait
|
||||
if createdBefore {
|
||||
// the resource was created before and appconfig status recorded the resource version applied
|
||||
// if recored ObservedGeneration and ComponentRevisionName both equal to the applied resource's,
|
||||
// that means its spec is not changed
|
||||
if (strconv.Itoa(int(w.ObservedGeneration)) != dAnnots[oam.AnnotationAppGeneration]) ||
|
||||
(w.ComponentRevisionName != dLabels[oam.LabelAppComponentRevision]) {
|
||||
// its spec is changed, so re-create the resource
|
||||
return nil
|
||||
}
|
||||
// its spec is not changed, so return an error to abort creating it
|
||||
return &GenerationUnchanged{}
|
||||
}
|
||||
}
|
||||
var message = "apply only once with mode: force, but resource not created before, will create new"
|
||||
if createdBefore {
|
||||
message = "apply only once with mode: force, but resource updated, will create new"
|
||||
}
|
||||
log.Info(message, "appConfig", ac.Name, "gvk", desired.GetObjectKind().GroupVersionKind(), "name", d.GetName(),
|
||||
"resourceType", dLabels[oam.LabelOAMResourceType], "appliedCompRevision", appliedRevision, "labeledCompRevision", dLabels[oam.LabelAppComponentRevision],
|
||||
"appliedGeneration", appliedGeneration, "labeledGeneration", dAnnots[oam.AnnotationAppGeneration])
|
||||
|
||||
// no recorded workloads nor traits matches the applied resource
|
||||
// that means the resource is not created before, so create it
|
||||
return nil
|
||||
@@ -662,10 +738,13 @@ func applyOnceOnly(ac *v1alpha2.ApplicationConfiguration, mode core.ApplyOnceOnl
|
||||
existing.GetObjectKind().GroupVersionKind())
|
||||
}
|
||||
eLabels := e.GetLabels()
|
||||
// if existing reource's (observed)AppConfigGeneration and ComponentRevisionName both equal to the applied one's,
|
||||
// if existing resource's (observed)AppConfigGeneration and ComponentRevisionName both equal to the applied one's,
|
||||
// that means its spec is not changed
|
||||
if (e.GetAnnotations()[oam.AnnotationAppGeneration] != dAnnots[oam.AnnotationAppGeneration]) ||
|
||||
(eLabels[oam.LabelAppComponentRevision] != dLabels[oam.LabelAppComponentRevision]) {
|
||||
log.Info("apply only once with mode: "+string(mode)+", but new generation or revision created, will create new",
|
||||
oam.AnnotationAppGeneration, e.GetAnnotations()[oam.AnnotationAppGeneration]+"/"+dAnnots[oam.AnnotationAppGeneration],
|
||||
oam.LabelAppComponentRevision, eLabels[oam.LabelAppComponentRevision]+"/"+dLabels[oam.LabelAppComponentRevision])
|
||||
// its spec is changed, so apply new configuration to it
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -661,7 +661,7 @@ func TestReconciler(t *testing.T) {
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
r := NewReconciler(tc.args.m, nil, tc.args.o...)
|
||||
r := NewReconciler(tc.args.m, nil, logging.NewNopLogger(), tc.args.o...)
|
||||
got, err := r.Reconcile(reconcile.Request{})
|
||||
|
||||
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
|
||||
@@ -847,9 +847,18 @@ func TestEligible(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsRevisionWorkload(t *testing.T) {
|
||||
if true != IsRevisionWorkload(v1alpha2.WorkloadStatus{ComponentName: "compName", Reference: runtimev1alpha1.TypedReference{Name: "compName-rev1"}}) {
|
||||
if true != IsRevisionWorkload(v1alpha2.WorkloadStatus{ComponentName: "compName", Reference: runtimev1alpha1.TypedReference{Name: "compName-rev1"}}, nil) {
|
||||
t.Error("workloadName has componentName as prefix is revisionWorkload")
|
||||
}
|
||||
if true != IsRevisionWorkload(v1alpha2.WorkloadStatus{ComponentName: "compName", Reference: runtimev1alpha1.TypedReference{Name: "speciedName"}}, []Workload{{ComponentName: "compName", RevisionEnabled: true}}) {
|
||||
t.Error("workloadName has componentName same and revisionEnabled is revisionWorkload")
|
||||
}
|
||||
if false != IsRevisionWorkload(v1alpha2.WorkloadStatus{ComponentName: "compName", Reference: runtimev1alpha1.TypedReference{Name: "speciedName"}}, []Workload{{ComponentName: "compName", RevisionEnabled: false}}) {
|
||||
t.Error("workloadName has componentName same and revisionEnabled is false")
|
||||
}
|
||||
if false != IsRevisionWorkload(v1alpha2.WorkloadStatus{ComponentName: "compName", Reference: runtimev1alpha1.TypedReference{Name: "speciedName"}}, []Workload{{ComponentName: "compName-notmatch", RevisionEnabled: true}}) {
|
||||
t.Error("workload with no prefix and no componentName match is not revisionEnabled ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependency(t *testing.T) {
|
||||
@@ -1628,7 +1637,7 @@ func TestUpdateStatus(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
r := NewReconciler(m, nil)
|
||||
r := NewReconciler(m, nil, logging.NewNopLogger())
|
||||
|
||||
ac := &v1alpha2.ApplicationConfiguration{}
|
||||
err := r.client.Get(context.Background(), types.NamespacedName{Name: "example-appconfig"}, ac)
|
||||
|
||||
@@ -136,13 +136,12 @@ spec:
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: appName}, ac)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check workload created successfully")
|
||||
Eventually(func() error {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
return k8sClient.Get(ctx, workloadKey, &workload)
|
||||
}, 5*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("Check reconcile again and no error will happen")
|
||||
reconcileRetry(reconciler, req)
|
||||
@@ -223,9 +222,14 @@ spec:
|
||||
By("Check new trait CR is applied")
|
||||
scale := v1alpha2.ManualScalerTrait{}
|
||||
scaleKey := client.ObjectKey{Name: scaleName, Namespace: namespace}
|
||||
err = k8sClient.Get(ctx, scaleKey, &scale)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(scale.Spec.ReplicaCount).Should(Equal(int32(3)))
|
||||
Eventually(func() int32 {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
if err := k8sClient.Get(ctx, scaleKey, &scale); err != nil {
|
||||
return 0
|
||||
}
|
||||
return scale.Spec.ReplicaCount
|
||||
}, 5*time.Second, time.Second).Should(Equal(int32(3)))
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
|
||||
@@ -4,6 +4,11 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
@@ -141,8 +146,7 @@ var _ = Describe("Test apply (workloads/traits) once only", func() {
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile")
|
||||
Expect(func() error { _, err := reconciler.Reconcile(req); return err }()).Should(BeNil())
|
||||
time.Sleep(3 * time.Second)
|
||||
reconcileRetry(reconciler, req)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
@@ -166,6 +170,8 @@ var _ = Describe("Test apply (workloads/traits) once only", func() {
|
||||
By("Get workload instance & Check workload spec")
|
||||
cwObj := v1alpha2.ContainerizedWorkload{}
|
||||
Eventually(func() error {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
return k8sClient.Get(ctx, cwObjKey, &cwObj)
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
Expect(cwObj.Spec.Containers[0].Image).Should(Equal(image1))
|
||||
@@ -267,6 +273,8 @@ var _ = Describe("Test apply (workloads/traits) once only", func() {
|
||||
By("Get workload instance & Check workload spec")
|
||||
cwObj := v1alpha2.ContainerizedWorkload{}
|
||||
Eventually(func() error {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
return k8sClient.Get(ctx, cwObjKey, &cwObj)
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
@@ -285,6 +293,375 @@ var _ = Describe("Test apply (workloads/traits) once only", func() {
|
||||
})
|
||||
|
||||
When("ApplyOnceOnlyForce is enabled", func() {
|
||||
It("tests the situation where workload is not applied at the first because of unsatisfied dependency",
|
||||
func() {
|
||||
componentHandler := &ComponentHandler{Client: k8sClient, RevisionLimit: 100, Logger: logging.NewLogrLogger(ctrl.Log.WithName("component-handler"))}
|
||||
|
||||
By("Enable ApplyOnceOnlyForce")
|
||||
reconciler.applyOnceOnlyMode = core.ApplyOnceOnlyForce
|
||||
|
||||
tempFoo := &unstructured.Unstructured{}
|
||||
tempFoo.SetAPIVersion("example.com/v1")
|
||||
tempFoo.SetKind("Foo")
|
||||
tempFoo.SetNamespace(namespace)
|
||||
|
||||
inName := "data-input"
|
||||
inputWorkload := &unstructured.Unstructured{}
|
||||
inputWorkload.SetAPIVersion("example.com/v1")
|
||||
inputWorkload.SetKind("Foo")
|
||||
inputWorkload.SetNamespace(namespace)
|
||||
inputWorkload.SetName(inName)
|
||||
|
||||
compInName := "comp-in"
|
||||
compIn := v1alpha2.Component{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: compInName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ComponentSpec{
|
||||
Workload: runtime.RawExtension{
|
||||
Object: inputWorkload,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
outName := "data-output"
|
||||
outputTrait := tempFoo.DeepCopy()
|
||||
outputTrait.SetName(outName)
|
||||
|
||||
acWithDepName := "ac-dep"
|
||||
acWithDep := v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: acWithDepName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{
|
||||
Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
ComponentName: compInName,
|
||||
DataInputs: []v1alpha2.DataInput{
|
||||
{
|
||||
ValueFrom: v1alpha2.DataInputValueFrom{
|
||||
DataOutputName: "trait-output",
|
||||
},
|
||||
ToFieldPaths: []string{"spec.key"},
|
||||
},
|
||||
},
|
||||
Traits: []v1alpha2.ComponentTrait{{
|
||||
Trait: runtime.RawExtension{Object: outputTrait},
|
||||
DataOutputs: []v1alpha2.DataOutput{{
|
||||
Name: "trait-output",
|
||||
FieldPath: "status.key",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("Create Component")
|
||||
Expect(k8sClient.Create(ctx, &compIn)).Should(Succeed())
|
||||
cmp := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compInName}, cmp)).Should(Succeed())
|
||||
|
||||
cmpV1 := cmp.DeepCopy()
|
||||
By("component handler will automatically create controller revision")
|
||||
Expect(func() bool {
|
||||
_, ok := componentHandler.createControllerRevision(cmpV1, cmpV1)
|
||||
return ok
|
||||
}()).Should(BeTrue())
|
||||
|
||||
By("Creat appConfig & check successfully")
|
||||
Expect(k8sClient.Create(ctx, &acWithDep)).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: acWithDepName}, &acWithDep)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile & check successfully")
|
||||
|
||||
reqDep := reconcile.Request{
|
||||
NamespacedName: client.ObjectKey{Namespace: namespace, Name: acWithDepName},
|
||||
}
|
||||
Eventually(func() bool {
|
||||
reconcileRetry(reconciler, reqDep)
|
||||
acWithDep = v1alpha2.ApplicationConfiguration{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: acWithDepName}, &acWithDep); err != nil {
|
||||
return false
|
||||
}
|
||||
return len(acWithDep.Status.Workloads) == 1
|
||||
}, time.Second, 300*time.Millisecond).Should(BeTrue())
|
||||
|
||||
// because dependency is not satisfied so the workload should not be created
|
||||
By("Check the workload is NOT created")
|
||||
workloadIn := tempFoo.DeepCopy()
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: inName}, workloadIn)).Should(&util.NotFoundMatcher{})
|
||||
|
||||
// modify the trait to make it satisfy comp's dependency
|
||||
outputTrait = tempFoo.DeepCopy()
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: outName}, outputTrait)).Should(Succeed())
|
||||
err := unstructured.SetNestedField(outputTrait.Object, "test", "status", "key")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(k8sClient.Status().Update(ctx, outputTrait)).Should(Succeed())
|
||||
Eventually(func() string {
|
||||
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: outName}, outputTrait)
|
||||
data, _, _ := unstructured.NestedString(outputTrait.Object, "status", "key")
|
||||
return data
|
||||
}, 3*time.Second, time.Second).Should(Equal("test"))
|
||||
|
||||
By("Reconcile & check ac is satisfied")
|
||||
Eventually(func() []v1alpha2.UnstaifiedDependency {
|
||||
reconcileRetry(reconciler, reqDep)
|
||||
acWithDep = v1alpha2.ApplicationConfiguration{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: acWithDepName}, &acWithDep); err != nil {
|
||||
return []v1alpha2.UnstaifiedDependency{{Reason: err.Error()}}
|
||||
}
|
||||
return acWithDep.Status.Dependency.Unsatisfied
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile & check workload is created")
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, reqDep)
|
||||
// the workload is created now because its dependency is satisfied
|
||||
workloadIn := tempFoo.DeepCopy()
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: inName}, workloadIn)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Delete the workload")
|
||||
recreatedWL := tempFoo.DeepCopy()
|
||||
recreatedWL.SetName(inName)
|
||||
Expect(k8sClient.Delete(ctx, recreatedWL)).Should(Succeed())
|
||||
outputTrait = tempFoo.DeepCopy()
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: inName}, outputTrait)).Should(util.NotFoundMatcher{})
|
||||
|
||||
By("Reconcile")
|
||||
Expect(func() error { _, err := reconciler.Reconcile(req); return err }()).Should(BeNil())
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
By("Check workload is not re-created by reconciliation")
|
||||
inputWorkload = tempFoo.DeepCopy()
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: inName}, inputWorkload)).Should(util.NotFoundMatcher{})
|
||||
})
|
||||
|
||||
It("tests the situation where workload is not applied at the first because of unsatisfied dependency and revision specified",
|
||||
func() {
|
||||
componentHandler := &ComponentHandler{Client: k8sClient, RevisionLimit: 100, Logger: logging.NewLogrLogger(ctrl.Log.WithName("component-handler"))}
|
||||
|
||||
By("Enable ApplyOnceOnlyForce")
|
||||
reconciler.applyOnceOnlyMode = core.ApplyOnceOnlyForce
|
||||
|
||||
tempFoo := &unstructured.Unstructured{}
|
||||
tempFoo.SetAPIVersion("example.com/v1")
|
||||
tempFoo.SetKind("Foo")
|
||||
tempFoo.SetNamespace(namespace)
|
||||
|
||||
inputWorkload := &unstructured.Unstructured{}
|
||||
inputWorkload.SetAPIVersion("example.com/v1")
|
||||
inputWorkload.SetKind("Foo")
|
||||
inputWorkload.SetNamespace(namespace)
|
||||
|
||||
compInName := "comp-in-revision"
|
||||
compIn := v1alpha2.Component{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: compInName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ComponentSpec{
|
||||
Workload: runtime.RawExtension{
|
||||
Object: inputWorkload,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
outName := "data-output"
|
||||
outputTrait := tempFoo.DeepCopy()
|
||||
outputTrait.SetName(outName)
|
||||
|
||||
acWithDepName := "ac-dep"
|
||||
acWithDep := v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: acWithDepName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{
|
||||
Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
RevisionName: compInName + "-v1",
|
||||
DataInputs: []v1alpha2.DataInput{
|
||||
{
|
||||
ValueFrom: v1alpha2.DataInputValueFrom{
|
||||
DataOutputName: "trait-output",
|
||||
},
|
||||
ToFieldPaths: []string{"spec.key"},
|
||||
},
|
||||
},
|
||||
Traits: []v1alpha2.ComponentTrait{{
|
||||
Trait: runtime.RawExtension{Object: outputTrait},
|
||||
DataOutputs: []v1alpha2.DataOutput{{
|
||||
Name: "trait-output",
|
||||
FieldPath: "status.key",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("Create Component")
|
||||
Expect(k8sClient.Create(ctx, &compIn)).Should(Succeed())
|
||||
cmp := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compInName}, cmp)).Should(Succeed())
|
||||
|
||||
cmpV1 := cmp.DeepCopy()
|
||||
By("component handler will automatically create controller revision")
|
||||
Expect(func() bool {
|
||||
_, ok := componentHandler.createControllerRevision(cmpV1, cmpV1)
|
||||
return ok
|
||||
}()).Should(BeTrue())
|
||||
|
||||
By("Creat appConfig & check successfully")
|
||||
Expect(k8sClient.Create(ctx, &acWithDep)).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: acWithDepName}, &acWithDep)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile & check successfully")
|
||||
|
||||
reqDep := reconcile.Request{
|
||||
NamespacedName: client.ObjectKey{Namespace: namespace, Name: acWithDepName},
|
||||
}
|
||||
Eventually(func() bool {
|
||||
reconcileRetry(reconciler, reqDep)
|
||||
acWithDep = v1alpha2.ApplicationConfiguration{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: acWithDepName}, &acWithDep); err != nil {
|
||||
return false
|
||||
}
|
||||
return len(acWithDep.Status.Workloads) == 1
|
||||
}, time.Second, 300*time.Millisecond).Should(BeTrue())
|
||||
|
||||
// because dependency is not satisfied so the workload should not be created
|
||||
By("Check the workload is NOT created")
|
||||
workloadIn := tempFoo.DeepCopy()
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compInName + "-v1"}, workloadIn)).Should(&util.NotFoundMatcher{})
|
||||
|
||||
// modify the trait to make it satisfy comp's dependency
|
||||
outputTrait = tempFoo.DeepCopy()
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: outName}, outputTrait)).Should(Succeed())
|
||||
err := unstructured.SetNestedField(outputTrait.Object, "test", "status", "key")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(k8sClient.Status().Update(ctx, outputTrait)).Should(Succeed())
|
||||
Eventually(func() string {
|
||||
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: outName}, outputTrait)
|
||||
data, _, _ := unstructured.NestedString(outputTrait.Object, "status", "key")
|
||||
return data
|
||||
}, 3*time.Second, time.Second).Should(Equal("test"))
|
||||
|
||||
By("Reconcile & check ac is satisfied")
|
||||
Eventually(func() []v1alpha2.UnstaifiedDependency {
|
||||
reconcileRetry(reconciler, reqDep)
|
||||
acWithDep = v1alpha2.ApplicationConfiguration{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: acWithDepName}, &acWithDep); err != nil {
|
||||
return []v1alpha2.UnstaifiedDependency{{Reason: err.Error()}}
|
||||
}
|
||||
return acWithDep.Status.Dependency.Unsatisfied
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile & check workload is created")
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, reqDep)
|
||||
// the workload should be created now because its dependency is satisfied
|
||||
workloadIn := tempFoo.DeepCopy()
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compInName}, workloadIn)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Delete the workload")
|
||||
recreatedWL := tempFoo.DeepCopy()
|
||||
recreatedWL.SetName(compInName)
|
||||
Expect(k8sClient.Delete(ctx, recreatedWL)).Should(Succeed())
|
||||
inputWorkload2 := tempFoo.DeepCopy()
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compInName}, inputWorkload2)).Should(util.NotFoundMatcher{})
|
||||
|
||||
By("Reconcile")
|
||||
Expect(func() error { _, err := reconciler.Reconcile(req); return err }()).Should(BeNil())
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
By("Check workload is not re-created by reconciliation")
|
||||
inputWorkload = tempFoo.DeepCopy()
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compInName}, inputWorkload)).Should(util.NotFoundMatcher{})
|
||||
})
|
||||
|
||||
It("should normally create workload/trait resources at fist time", func() {
|
||||
By("Enable ApplyOnceOnlyForce")
|
||||
reconciler.applyOnceOnlyMode = core.ApplyOnceOnlyForce
|
||||
component2 := v1alpha2.Component{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
Kind: "Component",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mycomp2",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ComponentSpec{
|
||||
Workload: runtime.RawExtension{
|
||||
Object: &cw,
|
||||
},
|
||||
},
|
||||
}
|
||||
newFakeTrait := fakeTrait.DeepCopy()
|
||||
newFakeTrait.SetName("mytrait2")
|
||||
appConfig2 := v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "myac2",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{
|
||||
Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
ComponentName: "mycomp2",
|
||||
Traits: []v1alpha2.ComponentTrait{
|
||||
{Trait: runtime.RawExtension{Object: newFakeTrait}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("Create Component")
|
||||
Expect(k8sClient.Create(ctx, &component2)).Should(Succeed())
|
||||
time.Sleep(time.Second)
|
||||
|
||||
By("Creat appConfig & check successfully")
|
||||
Expect(k8sClient.Create(ctx, &appConfig2)).Should(Succeed())
|
||||
time.Sleep(time.Second)
|
||||
|
||||
By("Reconcile")
|
||||
Expect(func() error {
|
||||
_, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: "myac2", Namespace: namespace}})
|
||||
return err
|
||||
}()).Should(BeNil())
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
By("Get workload instance & Check workload spec")
|
||||
cwObj := v1alpha2.ContainerizedWorkload{}
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: "mycomp2"}, &cwObj)
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
Expect(cwObj.Spec.Containers[0].Image).Should(Equal(image1))
|
||||
|
||||
By("Get trait instance & Check trait spec")
|
||||
fooObj := &unstructured.Unstructured{}
|
||||
fooObj.SetAPIVersion("example.com/v1")
|
||||
fooObj.SetKind("Foo")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: "mytrait2"}, fooObj)
|
||||
}, 3*time.Second, time.Second).Should(BeNil())
|
||||
fooObjV, _, _ := unstructured.NestedString(fooObj.Object, "spec", "key")
|
||||
Expect(fooObjV).Should(Equal(traitSpecValue1))
|
||||
|
||||
})
|
||||
|
||||
It("should not revert changes of workload/trait made by others", func() {
|
||||
By("Enable ApplyOnceOnlyForce")
|
||||
reconciler.applyOnceOnlyMode = core.ApplyOnceOnlyForce
|
||||
@@ -292,6 +669,8 @@ var _ = Describe("Test apply (workloads/traits) once only", func() {
|
||||
By("Get workload instance & Check workload spec")
|
||||
cwObj := v1alpha2.ContainerizedWorkload{}
|
||||
Eventually(func() error {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
return k8sClient.Get(ctx, cwObjKey, &cwObj)
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
Expect(cwObj.Spec.Containers[0].Image).Should(Equal(image1))
|
||||
@@ -393,8 +772,10 @@ var _ = Describe("Test apply (workloads/traits) once only", func() {
|
||||
By("Get workload instance")
|
||||
cwObj := v1alpha2.ContainerizedWorkload{}
|
||||
Eventually(func() error {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
return k8sClient.Get(ctx, cwObjKey, &cwObj)
|
||||
}, 3*time.Second, time.Second).Should(BeNil())
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("Get trait instance & Check trait spec")
|
||||
fooObj := unstructured.Unstructured{}
|
||||
@@ -426,7 +807,7 @@ var _ = Describe("Test apply (workloads/traits) once only", func() {
|
||||
recreatedFooObj.SetKind("Foo")
|
||||
Expect(k8sClient.Get(ctx, traitObjKey, &recreatedFooObj)).Should(util.NotFoundMatcher{})
|
||||
|
||||
By("Update Appconfig to trigger generation augment")
|
||||
By("Update AppConfig to trigger generation updated")
|
||||
unstructured.SetNestedField(fakeTrait.Object, "newvalue", "spec", "key")
|
||||
appConfig = v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -445,14 +826,24 @@ var _ = Describe("Test apply (workloads/traits) once only", func() {
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Patch(ctx, &appConfig, client.Merge)).Should(Succeed())
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
time.Sleep(3 * time.Second)
|
||||
By("Check AppConfig is updated successfully")
|
||||
updateAC := v1alpha2.ApplicationConfiguration{}
|
||||
Eventually(func() int64 {
|
||||
if err := k8sClient.Get(ctx, appConfigKey, &updateAC); err != nil {
|
||||
return 0
|
||||
}
|
||||
return updateAC.GetGeneration()
|
||||
}, 3*time.Second, time.Second).Should(Equal(int64(2)))
|
||||
|
||||
By("Check workload is re-created by reconciliation")
|
||||
recreatedCwObj = v1alpha2.ContainerizedWorkload{}
|
||||
Expect(k8sClient.Get(ctx, cwObjKey, &recreatedCwObj)).Should(Succeed())
|
||||
By("Check workload was not created by reconciliation")
|
||||
Eventually(func() error {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
recreatedCwObj = v1alpha2.ContainerizedWorkload{}
|
||||
return k8sClient.Get(ctx, cwObjKey, &recreatedCwObj)
|
||||
}, 5*time.Second, time.Second).Should(SatisfyAll(util.NotFoundMatcher{}))
|
||||
|
||||
By("Check trait is re-created by reconciliation")
|
||||
recreatedFooObj = unstructured.Unstructured{}
|
||||
|
||||
@@ -126,9 +126,8 @@ func TestApplyWorkloads(t *testing.T) {
|
||||
}
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
ws []v1alpha2.WorkloadStatus
|
||||
w []Workload
|
||||
ws []v1alpha2.WorkloadStatus
|
||||
w []Workload
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
@@ -330,7 +329,7 @@ func TestApplyWorkloads(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
mapper := mock.NewMockDiscoveryMapper()
|
||||
w := workloads{applicator: tc.applicator, rawClient: tc.rawClient, dm: mapper}
|
||||
err := w.Apply(tc.args.ctx, tc.args.ws, tc.args.w)
|
||||
err := w.Apply(context.TODO(), tc.args.ws, tc.args.w)
|
||||
|
||||
if diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != "" {
|
||||
t.Errorf("\n%s\nw.Apply(...): -want error, +got error:\n%s", tc.reason, diff)
|
||||
|
||||
@@ -149,7 +149,8 @@ var _ = Describe("Test apply changes to trait", func() {
|
||||
APIVersion: "TraitDefinition",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bars.example.com",
|
||||
Name: "bars.example.com",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
@@ -236,31 +237,26 @@ spec:
|
||||
return appConfig.GetGeneration()
|
||||
}, time.Second, 300*time.Millisecond).Should(Equal(int64(2)))
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
Eventually(func() string {
|
||||
By("Reconcile & check updated trait")
|
||||
var traitObj unstructured.Unstructured
|
||||
Eventually(func() int64 {
|
||||
reconcileRetry(reconciler, req)
|
||||
if err := k8sClient.Get(ctx, appConfigKey, &appConfig); err != nil {
|
||||
return ""
|
||||
return 0
|
||||
}
|
||||
if appConfig.Status.Workloads == nil {
|
||||
reconcileRetry(reconciler, req)
|
||||
return ""
|
||||
return 0
|
||||
}
|
||||
return appConfig.Status.Workloads[0].Traits[0].Reference.Name
|
||||
}, 3*time.Second, time.Second).ShouldNot(BeEmpty())
|
||||
|
||||
By("Get changed trait object")
|
||||
traitName := appConfig.Status.Workloads[0].Traits[0].Reference.Name
|
||||
var traitObj unstructured.Unstructured
|
||||
traitObj.SetAPIVersion("example.com/v1")
|
||||
traitObj.SetKind("Bar")
|
||||
Eventually(func() int64 {
|
||||
traitName := appConfig.Status.Workloads[0].Traits[0].Reference.Name
|
||||
traitObj.SetAPIVersion("example.com/v1")
|
||||
traitObj.SetKind("Bar")
|
||||
if err := k8sClient.Get(ctx,
|
||||
client.ObjectKey{Namespace: namespace, Name: traitName}, &traitObj); err != nil {
|
||||
return 0
|
||||
}
|
||||
return traitObj.GetGeneration()
|
||||
}, 3*time.Second, time.Second).Should(Equal(int64(2)))
|
||||
}, 5*time.Second, time.Second).Should(Equal(int64(2)))
|
||||
|
||||
By("Check labels are removed")
|
||||
_, found, _ := unstructured.NestedString(traitObj.UnstructuredContent(), "metadata", "labels", "test.label")
|
||||
@@ -355,13 +351,19 @@ spec:
|
||||
}
|
||||
return appConfig.GetGeneration()
|
||||
}, time.Second, 300*time.Millisecond).Should(Equal(int64(2)))
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
changedTrait = unstructured.Unstructured{}
|
||||
changedTrait.SetAPIVersion("example.com/v1")
|
||||
changedTrait.SetKind("Bar")
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: traitName}, &changedTrait)).Should(Succeed())
|
||||
Eventually(func() int64 {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
changedTrait = unstructured.Unstructured{}
|
||||
changedTrait.SetAPIVersion("example.com/v1")
|
||||
changedTrait.SetKind("Bar")
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: traitName}, &changedTrait); err != nil {
|
||||
return 0
|
||||
}
|
||||
return changedTrait.GetGeneration()
|
||||
}, 5*time.Second, time.Second).Should(Equal(int64(3)))
|
||||
|
||||
By("Check AppConfig's change works")
|
||||
// changed a field
|
||||
v, _, _ = unstructured.NestedString(changedTrait.UnstructuredContent(), "spec", "valueChanged")
|
||||
|
||||
@@ -81,7 +81,7 @@ func (c *ComponentHandler) Generic(_ event.GenericEvent, _ workqueue.RateLimitin
|
||||
func isMatch(appConfigs *v1alpha2.ApplicationConfigurationList, compName string) (bool, types.NamespacedName) {
|
||||
for _, app := range appConfigs.Items {
|
||||
for _, comp := range app.Spec.Components {
|
||||
if comp.ComponentName == compName {
|
||||
if comp.ComponentName == compName || (strings.HasPrefix(comp.RevisionName, compName+"-")) {
|
||||
return true, types.NamespacedName{Namespace: app.Namespace, Name: app.Name}
|
||||
}
|
||||
}
|
||||
@@ -140,6 +140,10 @@ func newTrue() *bool {
|
||||
func (c *ComponentHandler) createControllerRevision(mt metav1.Object, obj runtime.Object) ([]reconcile.Request, bool) {
|
||||
curComp := obj.(*v1alpha2.Component)
|
||||
comp := curComp.DeepCopy()
|
||||
// No generation changed, will not create revision
|
||||
if comp.Generation == comp.Status.ObservedGeneration {
|
||||
return nil, false
|
||||
}
|
||||
diff, curRevision := c.IsRevisionDiff(mt, comp)
|
||||
if !diff {
|
||||
// No difference, no need to create new revision.
|
||||
|
||||
@@ -18,50 +18,68 @@ package applicationconfiguration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
)
|
||||
|
||||
func TestCustomRevisionHook(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var req RevisionHookRequest
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &req)
|
||||
if err != nil {
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
if len(req.RelatedApps) != 1 {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("we should have only one relatedApps"))
|
||||
return
|
||||
}
|
||||
if req.Comp.Annotations == nil {
|
||||
req.Comp.Annotations = make(map[string]string)
|
||||
var RevisionHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var req RevisionHookRequest
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &req)
|
||||
if err != nil {
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
fmt.Println("got request from", req.Comp.Name)
|
||||
|
||||
if len(req.RelatedApps) != 1 {
|
||||
var abc []string
|
||||
for _, v := range req.RelatedApps {
|
||||
abc = append(abc, v.Name)
|
||||
}
|
||||
// we can add a check here for real world handler
|
||||
fmt.Printf("we should have only one relatedApps, but now %d: %s\n", len(req.RelatedApps), strings.Join(abc, ", "))
|
||||
}
|
||||
if req.Comp.Annotations == nil {
|
||||
req.Comp.Annotations = make(map[string]string)
|
||||
}
|
||||
if len(req.RelatedApps) > 0 {
|
||||
req.Comp.Annotations["app-name"] = req.RelatedApps[0].Name
|
||||
req.Comp.Annotations["app-namespace"] = req.RelatedApps[0].Namespace
|
||||
}
|
||||
a := &unstructured.Unstructured{}
|
||||
err = json.Unmarshal(req.Comp.Spec.Workload.Raw, a)
|
||||
fmt.Println("XX:", err)
|
||||
a.SetAnnotations(map[string]string{"time": time.Now().Format(time.RFC3339Nano)})
|
||||
data, _ = json.Marshal(a)
|
||||
req.Comp.Spec.Workload.Raw = data
|
||||
newdata, err := json.Marshal(req.Comp)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(200)
|
||||
w.Write(newdata)
|
||||
})
|
||||
|
||||
newdata, err := json.Marshal(req.Comp)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(200)
|
||||
w.Write(newdata)
|
||||
}))
|
||||
func TestCustomRevisionHook(t *testing.T) {
|
||||
srv := httptest.NewServer(RevisionHandler)
|
||||
defer srv.Close()
|
||||
compHandler := ComponentHandler{
|
||||
CustomRevisionHookURL: srv.URL,
|
||||
@@ -71,7 +89,4 @@ func TestCustomRevisionHook(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "app1", comp.Annotations["app-name"])
|
||||
assert.Equal(t, "default1", comp.Annotations["app-namespace"])
|
||||
|
||||
err = compHandler.customComponentRevisionHook([]reconcile.Request{{NamespacedName: types.NamespacedName{Name: "app1", Namespace: "default1"}}, {NamespacedName: types.NamespacedName{Name: "app2", Namespace: "default2"}}}, comp)
|
||||
assert.Equal(t, err.Error(), "httpcode(400) err: we should have only one relatedApps")
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ func TestComponentHandler(t *testing.T) {
|
||||
// check component's status saved in corresponding controllerRevision
|
||||
assert.Equal(t, gotComp.Status.LatestRevision.Name, revisions.Items[0].Name)
|
||||
assert.Equal(t, gotComp.Status.LatestRevision.Revision, revisions.Items[0].Revision)
|
||||
// check component's status ObservedGeneration
|
||||
// check component's status AppliedGeneration
|
||||
assert.Equal(t, gotComp.Status.ObservedGeneration, comp.Generation)
|
||||
q.Done(item)
|
||||
// ============ Test Create Event End ===================
|
||||
|
||||
@@ -80,8 +80,23 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
AfterEach(func() {
|
||||
logf.Log.Info("Clean up resources")
|
||||
// delete the namespace with all its resources
|
||||
Expect(k8sClient.Delete(ctx, in)).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{}))
|
||||
Expect(k8sClient.Delete(ctx, out)).Should(BeNil())
|
||||
ac := &v1alpha2.ApplicationConfiguration{}
|
||||
Expect(k8sClient.DeleteAllOf(ctx, ac, client.InNamespace(namespace))).Should(Succeed())
|
||||
cm := &corev1.ConfigMap{}
|
||||
Expect(k8sClient.DeleteAllOf(ctx, cm, client.InNamespace(namespace))).Should(Succeed())
|
||||
foo := &unstructured.Unstructured{}
|
||||
foo.SetAPIVersion("example.com/v1")
|
||||
foo.SetKind("Foo")
|
||||
Expect(k8sClient.DeleteAllOf(ctx, foo, client.InNamespace(namespace))).Should(Succeed())
|
||||
Eventually(func() bool {
|
||||
l := &unstructured.UnstructuredList{}
|
||||
l.SetAPIVersion("example.com/v1")
|
||||
l.SetKind("Foo")
|
||||
if err := k8sClient.List(ctx, l, client.InNamespace(namespace)); err != nil {
|
||||
return false
|
||||
}
|
||||
return len(l.Items) == 0
|
||||
}, 3*time.Second, time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
// common function for verification
|
||||
@@ -114,11 +129,11 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, outFooKey, outFoo)
|
||||
if err != nil {
|
||||
// Try 3 (= 1s/300ms) times
|
||||
// Try 3 (= 3s/1s) times
|
||||
reconciler.Reconcile(req)
|
||||
}
|
||||
return err
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}, 3*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
@@ -159,7 +174,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
k8sClient.Get(ctx, outFooKey, outFoo)
|
||||
data, _, _ := unstructured.NestedString(outFoo.Object, "status", "key")
|
||||
return data
|
||||
}, time.Second, 300*time.Millisecond).Should(Equal("test"))
|
||||
}, 3*time.Second, time.Second).Should(Equal("test"))
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
@@ -175,7 +190,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
reconciler.Reconcile(req)
|
||||
k8sClient.Get(ctx, appconfigKey, appconfig)
|
||||
return appconfig.Status.Dependency.Unsatisfied
|
||||
}, 2*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}, 3*time.Second, time.Second).Should(BeNil())
|
||||
}
|
||||
|
||||
It("trait depends on another trait", func() {
|
||||
@@ -372,7 +387,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
Expect(k8sClient.Create(ctx, &appConfig)).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, appconfigKey, &appConfig)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}, 3*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
@@ -399,11 +414,11 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, outFooKey, outFoo)
|
||||
if err != nil {
|
||||
// Try 3 (= 1s/300ms) times
|
||||
// Try 3 (= 3s/1s) times
|
||||
reconciler.Reconcile(req)
|
||||
}
|
||||
return err
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}, 3*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
err := unstructured.SetNestedField(outFoo.Object, "test", "status", "key")
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -423,11 +438,11 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
err = k8sClient.Get(ctx, appconfigKey, tempApp)
|
||||
tempApp.DeepCopyInto(newAppConfig)
|
||||
if err != nil || tempApp.Status.Dependency.Unsatisfied != nil {
|
||||
// Try 3 (= 1s/300ms) times
|
||||
// Try 3 (= 3s/1s) times
|
||||
reconciler.Reconcile(req)
|
||||
}
|
||||
return tempApp.Status.Dependency.Unsatisfied
|
||||
}(), time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}(), 3*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("Checking that resource which accepts data is created now")
|
||||
logf.Log.Info("Checking on resource that inputs data", "Key", inFooKey)
|
||||
@@ -436,11 +451,11 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, outFooKey, outFoo)
|
||||
if err != nil {
|
||||
// Try 3 (= 1s/300ms) times
|
||||
// Try 3 (= 3s/1s) times
|
||||
reconciler.Reconcile(req)
|
||||
}
|
||||
return err
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}, 3*time.Second, time.Second).Should(BeNil())
|
||||
err = unstructured.SetNestedField(outFoo.Object, "test", "status", "key")
|
||||
Expect(err).Should(BeNil())
|
||||
err = unstructured.SetNestedField(outFoo.Object, "hash-v1", "status", "app-hash")
|
||||
@@ -453,7 +468,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
}
|
||||
s, _, _ := unstructured.NestedString(outFoo.Object, "status", "key")
|
||||
return s == "test"
|
||||
}, time.Second, 300*time.Millisecond).Should(BeTrue())
|
||||
}, 3*time.Second, time.Second).Should(BeTrue())
|
||||
|
||||
newAppConfig.Labels["app-hash"] = "hash-v2"
|
||||
By("Update newAppConfig & check successfully")
|
||||
@@ -464,10 +479,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
return false
|
||||
}
|
||||
return newAppConfig.Labels["app-hash"] == "hash-v2"
|
||||
}, time.Second, 300*time.Millisecond).Should(BeTrue())
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
}, 3*time.Second, time.Second).Should(BeTrue())
|
||||
|
||||
By("Verify the appconfig's dependency should be unsatisfied, because requirementCondition valueFrom not match")
|
||||
depStatus := v1alpha2.DependencyStatus{
|
||||
@@ -492,9 +504,11 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
}}}},
|
||||
}
|
||||
Eventually(func() v1alpha2.DependencyStatus {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
k8sClient.Get(ctx, appconfigKey, newAppConfig)
|
||||
return newAppConfig.Status.Dependency
|
||||
}, time.Second, 300*time.Millisecond).Should(Equal(depStatus))
|
||||
}, 3*time.Second, time.Second).Should(Equal(depStatus))
|
||||
|
||||
By("Update trait resource to meet the requirement")
|
||||
Expect(k8sClient.Get(ctx, outFooKey, outFoo)).Should(BeNil()) // Get the latest before update
|
||||
@@ -508,7 +522,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
}
|
||||
s, _, _ := unstructured.NestedString(outFoo.Object, "status", "key")
|
||||
return s == "test-new"
|
||||
}, time.Second, 300*time.Millisecond).Should(BeTrue())
|
||||
}, 3*time.Second, time.Second).Should(BeTrue())
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
@@ -519,11 +533,11 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
tempAppConfig := &v1alpha2.ApplicationConfiguration{}
|
||||
err := k8sClient.Get(ctx, appconfigKey, tempAppConfig)
|
||||
if err != nil || tempAppConfig.Status.Dependency.Unsatisfied != nil {
|
||||
// Try 3 (= 1s/300ms) times
|
||||
// Try 3 (= 3s/1s) times
|
||||
reconciler.Reconcile(req)
|
||||
}
|
||||
return tempAppConfig.Status.Dependency.Unsatisfied
|
||||
}(), time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}(), 3*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("Checking that resource which accepts data is updated")
|
||||
Expect(func() string {
|
||||
@@ -625,6 +639,9 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
Name: appConfigName,
|
||||
Namespace: namespace,
|
||||
}
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, appconfigKey, &v1alpha2.ApplicationConfiguration{})
|
||||
}, 3*time.Second, time.Second).Should(Succeed())
|
||||
By("Reconcile")
|
||||
req := reconcile.Request{NamespacedName: appconfigKey}
|
||||
reconcileRetry(reconciler, req)
|
||||
@@ -641,7 +658,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
reconciler.Reconcile(req)
|
||||
}
|
||||
return err
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}, 3*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("Get reconciled AppConfig the first time")
|
||||
appconfig := &v1alpha2.ApplicationConfiguration{}
|
||||
@@ -669,7 +686,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
k8sClient.Get(ctx, outFooKey, outFoo)
|
||||
data, _, _ := unstructured.NestedSlice(outFoo.Object, "status", "complex2")
|
||||
return data
|
||||
}, time.Second, 300*time.Millisecond).Should(BeEquivalentTo(complex2))
|
||||
}, 3*time.Second, time.Second).Should(BeEquivalentTo(complex2))
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
@@ -680,7 +697,7 @@ var _ = Describe("Resource Dependency in an ApplicationConfiguration", func() {
|
||||
reconciler.Reconcile(req)
|
||||
k8sClient.Get(ctx, appconfigKey, appconfig)
|
||||
return appconfig.Status.Dependency.Unsatisfied
|
||||
}, 2*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
}, 2*3*time.Second, time.Second).Should(BeNil())
|
||||
// Verification after satisfying dependency
|
||||
By("Checking that resource which accepts data is created now")
|
||||
inFooKey := client.ObjectKey{
|
||||
|
||||
@@ -39,7 +39,6 @@ import (
|
||||
|
||||
core "github.com/oam-dev/kubevela/apis/core.oam.dev"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/mock"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
@@ -99,8 +98,7 @@ func TestRenderComponents(t *testing.T) {
|
||||
trait ResourceRenderer
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
ac *v1alpha2.ApplicationConfiguration
|
||||
ac *v1alpha2.ApplicationConfiguration
|
||||
}
|
||||
type want struct {
|
||||
w []Workload
|
||||
@@ -525,7 +523,7 @@ func TestRenderComponents(t *testing.T) {
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
r := &components{tc.fields.client, mock.NewMockDiscoveryMapper(), tc.fields.params, tc.fields.workload, tc.fields.trait}
|
||||
got, _, err := r.Render(tc.args.ctx, tc.args.ac)
|
||||
got, _, err := r.Render(context.TODO(), tc.args.ac)
|
||||
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
|
||||
t.Errorf("\n%s\nr.Render(...): -want error, +got error:\n%s\n", tc.reason, diff)
|
||||
}
|
||||
@@ -813,8 +811,7 @@ func TestRenderTraitWithoutMetadataName(t *testing.T) {
|
||||
trait ResourceRenderer
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
ac *v1alpha2.ApplicationConfiguration
|
||||
ac *v1alpha2.ApplicationConfiguration
|
||||
}
|
||||
type want struct {
|
||||
w []Workload
|
||||
@@ -871,7 +868,7 @@ func TestRenderTraitWithoutMetadataName(t *testing.T) {
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
r := &components{tc.fields.client, mock.NewMockDiscoveryMapper(), tc.fields.params, tc.fields.workload, tc.fields.trait}
|
||||
got, _, _ := r.Render(tc.args.ctx, tc.args.ac)
|
||||
got, _, _ := r.Render(context.TODO(), tc.args.ac)
|
||||
if len(got) == 0 || len(got[0].Traits) == 0 || got[0].Traits[0].Object.GetName() != util.GenTraitName(componentName, ac.Spec.Components[0].Traits[0].DeepCopy(), "") {
|
||||
t.Errorf("\n%s\nr.Render(...): -want error, +got error:\n%s\n", tc.reason, "Trait name is NOT "+
|
||||
"automatically set.")
|
||||
|
||||
@@ -19,18 +19,21 @@ package applicationconfiguration
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/pointer"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
@@ -200,11 +203,10 @@ var _ = Describe("Test ApplicationConfiguration Component Revision Enabled trait
|
||||
return k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check workload created successfully")
|
||||
Eventually(func() error {
|
||||
By("Reconcile")
|
||||
reconcileRetry(reconciler, req)
|
||||
var workloadKey = client.ObjectKey{Namespace: namespace, Name: compName + "-v1"}
|
||||
return k8sClient.Get(ctx, workloadKey, &wr)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
@@ -295,11 +297,11 @@ var _ = Describe("Test ApplicationConfiguration Component Revision Enabled trait
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Reconcile for new revision")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check new revision workload created successfully")
|
||||
Eventually(func() error {
|
||||
By("Reconcile for new revision")
|
||||
reconcileRetry(reconciler, req)
|
||||
var workloadKey = client.ObjectKey{Namespace: namespace, Name: compName + "-v2"}
|
||||
return k8sClient.Get(ctx, workloadKey, &wr)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
@@ -341,3 +343,824 @@ var _ = Describe("Test ApplicationConfiguration Component Revision Enabled trait
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
var _ = Describe("Test Component Revision Enabled with custom component revision hook", func() {
|
||||
const (
|
||||
namespace = "revision-enable-test2"
|
||||
compName = "revision-test-comp2"
|
||||
)
|
||||
var (
|
||||
ctx = context.Background()
|
||||
component v1alpha2.Component
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {})
|
||||
|
||||
AfterEach(func() {
|
||||
// delete the namespace with all its resources
|
||||
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).
|
||||
Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{}))
|
||||
})
|
||||
|
||||
It("custom component change revision lead to revision difference, it should not loop infinitely create", func() {
|
||||
srv := httptest.NewServer(RevisionHandler)
|
||||
defer srv.Close()
|
||||
customComponentHandler := &ComponentHandler{Client: k8sClient, RevisionLimit: 100, Logger: logging.NewLogrLogger(ctrl.Log.WithName("component-handler")), CustomRevisionHookURL: srv.URL}
|
||||
getDeploy := func(image string) *v1.Deployment {
|
||||
return &v1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
|
||||
"app": compName,
|
||||
}},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
|
||||
"app": compName,
|
||||
}},
|
||||
Spec: corev1.PodSpec{Containers: []corev1.Container{{
|
||||
Name: "wordpress",
|
||||
Image: image,
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "wordpress",
|
||||
ContainerPort: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
},
|
||||
}
|
||||
}
|
||||
component = v1alpha2.Component{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
Kind: "Component",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: compName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ComponentSpec{
|
||||
Workload: runtime.RawExtension{
|
||||
Object: getDeploy("wordpress:4.6.1-apache"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("Create namespace")
|
||||
Eventually(
|
||||
func() error {
|
||||
return k8sClient.Create(ctx, &ns)
|
||||
},
|
||||
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Create Component")
|
||||
Expect(k8sClient.Create(ctx, &component)).Should(Succeed())
|
||||
|
||||
By("component handler will automatically create controller revision")
|
||||
Expect(func() bool {
|
||||
_, ok := customComponentHandler.createControllerRevision(component.DeepCopy(), component.DeepCopy())
|
||||
return ok
|
||||
}()).Should(BeTrue())
|
||||
|
||||
By("it should not create again for the same generation component")
|
||||
cmpV1 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compName}, cmpV1)).Should(Succeed())
|
||||
Expect(func() bool {
|
||||
_, ok := customComponentHandler.createControllerRevision(cmpV1, cmpV1)
|
||||
return ok
|
||||
}()).Should(BeFalse())
|
||||
|
||||
var crList v1.ControllerRevisionList
|
||||
By("Check controller revision created successfully")
|
||||
Eventually(func() error {
|
||||
labels := &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
ControllerRevisionComponentLabel: compName,
|
||||
},
|
||||
}
|
||||
selector, err := metav1.LabelSelectorAsSelector(labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = k8sClient.List(ctx, &crList, &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(crList.Items) != 1 {
|
||||
return fmt.Errorf("want only 1 revision created but got %d", len(crList.Items))
|
||||
}
|
||||
return nil
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("===================================== Start to Update =========================================")
|
||||
cmpV2 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compName}, cmpV2)).Should(Succeed())
|
||||
cmpV2.Spec.Workload = runtime.RawExtension{
|
||||
Object: getDeploy("wordpress:v2"),
|
||||
}
|
||||
By("Update Component")
|
||||
Expect(k8sClient.Update(ctx, cmpV2)).Should(Succeed())
|
||||
By("component handler will automatically create a ne controller revision")
|
||||
Expect(func() bool { _, ok := componentHandler.createControllerRevision(cmpV2, cmpV2); return ok }()).Should(BeTrue())
|
||||
|
||||
cmpV3 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compName}, cmpV3)).Should(Succeed())
|
||||
Expect(func() bool { _, ok := componentHandler.createControllerRevision(cmpV3, cmpV3); return ok }()).Should(BeFalse())
|
||||
|
||||
By("Check controller revision created successfully")
|
||||
Eventually(func() error {
|
||||
labels := &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
ControllerRevisionComponentLabel: compName,
|
||||
},
|
||||
}
|
||||
selector, err := metav1.LabelSelectorAsSelector(labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = k8sClient.List(ctx, &crList, &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(crList.Items) != 2 {
|
||||
return fmt.Errorf("there should be exactly 2 revision created but got %d", len(crList.Items))
|
||||
}
|
||||
return nil
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Component Revision Enabled with apply once only force", func() {
|
||||
const (
|
||||
namespace = "revision-and-apply-once-force"
|
||||
appName = "revision-apply-once"
|
||||
compName = "revision-apply-once-comp"
|
||||
)
|
||||
var (
|
||||
ctx = context.Background()
|
||||
wr v1.Deployment
|
||||
component v1alpha2.Component
|
||||
appConfig v1alpha2.ApplicationConfiguration
|
||||
appConfigKey = client.ObjectKey{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
}
|
||||
req = reconcile.Request{NamespacedName: appConfigKey}
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {})
|
||||
|
||||
AfterEach(func() {
|
||||
// delete the namespace with all its resources
|
||||
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).
|
||||
Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{}))
|
||||
})
|
||||
|
||||
It("revision enabled should create workload with revisionName and work upgrade with new revision successfully", func() {
|
||||
|
||||
getDeploy := func(image string) *v1.Deployment {
|
||||
return &v1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
|
||||
"app": compName,
|
||||
}},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
|
||||
"app": compName,
|
||||
}},
|
||||
Spec: corev1.PodSpec{Containers: []corev1.Container{{
|
||||
Name: "wordpress",
|
||||
Image: image,
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "wordpress",
|
||||
ContainerPort: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
},
|
||||
}
|
||||
}
|
||||
component = v1alpha2.Component{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
Kind: "Component",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: compName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ComponentSpec{
|
||||
Workload: runtime.RawExtension{
|
||||
Object: getDeploy("wordpress:4.6.1-apache"),
|
||||
},
|
||||
},
|
||||
}
|
||||
appConfig = v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
}
|
||||
|
||||
By("Create namespace")
|
||||
Eventually(
|
||||
func() error {
|
||||
return k8sClient.Create(ctx, &ns)
|
||||
},
|
||||
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Create Component")
|
||||
Expect(k8sClient.Create(ctx, &component)).Should(Succeed())
|
||||
cmpV1 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compName}, cmpV1)).Should(Succeed())
|
||||
|
||||
By("component handler will automatically create controller revision")
|
||||
Expect(func() bool {
|
||||
_, ok := componentHandler.createControllerRevision(cmpV1, cmpV1)
|
||||
return ok
|
||||
}()).Should(BeTrue())
|
||||
var crList v1.ControllerRevisionList
|
||||
By("Check controller revision created successfully")
|
||||
Eventually(func() error {
|
||||
labels := &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
ControllerRevisionComponentLabel: compName,
|
||||
},
|
||||
}
|
||||
selector, err := metav1.LabelSelectorAsSelector(labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = k8sClient.List(ctx, &crList, &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(crList.Items) != 1 {
|
||||
return fmt.Errorf("want only 1 revision created but got %d", len(crList.Items))
|
||||
}
|
||||
return nil
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Create an ApplicationConfiguration")
|
||||
appConfig = v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
RevisionName: compName + "-v1",
|
||||
Traits: []v1alpha2.ComponentTrait{
|
||||
{
|
||||
Trait: runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"trait.oam.dev/type": "rollout-revision",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"key": "test1",
|
||||
},
|
||||
}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
By("Creat appConfig & check successfully")
|
||||
Expect(k8sClient.Create(ctx, &appConfig)).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile")
|
||||
reconciler.applyOnceOnlyMode = "force"
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check workload created successfully")
|
||||
var workloadKey1 = client.ObjectKey{Namespace: namespace, Name: compName + "-v1"}
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, req)
|
||||
return k8sClient.Get(ctx, workloadKey1, &wr)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Check workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
|
||||
By("Delete the workload")
|
||||
Expect(k8sClient.Delete(ctx, &wr)).Should(BeNil())
|
||||
|
||||
By("Check reconcile again and no error will happen")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check workload will not created after reconcile because apply once force enabled")
|
||||
Expect(k8sClient.Get(ctx, workloadKey1, &wr)).Should(SatisfyAll(util.NotFoundMatcher{}))
|
||||
Expect(k8sClient.Get(ctx, appConfigKey, &appConfig)).Should(BeNil())
|
||||
By("update the trait of ac")
|
||||
appConfig.Spec.Components[0].Traits[0].Trait = runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"trait.oam.dev/type": "rollout-revision",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"key": "test2",
|
||||
},
|
||||
}}}
|
||||
Expect(k8sClient.Update(ctx, &appConfig)).Should(Succeed())
|
||||
|
||||
By("Reconcile and Check appconfig condition should not have error")
|
||||
reconcileRetry(reconciler, req)
|
||||
Eventually(func() string {
|
||||
By("Reconcile again and should not have error")
|
||||
reconcileRetry(reconciler, req)
|
||||
err := k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if len(appConfig.Status.Conditions) != 1 {
|
||||
return "condition len should be 1 but now is " + strconv.Itoa(len(appConfig.Status.Conditions))
|
||||
}
|
||||
return string(appConfig.Status.Conditions[0].Reason)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeEquivalentTo("ReconcileSuccess"))
|
||||
time.Sleep(time.Second)
|
||||
By("Check workload will not created even AC changed because apply once force working")
|
||||
Expect(k8sClient.Get(ctx, workloadKey1, &wr)).Should(SatisfyAll(util.NotFoundMatcher{}))
|
||||
By("Check the trait was updated as expected")
|
||||
var tr unstructured.Unstructured
|
||||
Eventually(func() error {
|
||||
tr.SetAPIVersion("example.com/v1")
|
||||
tr.SetKind("Foo")
|
||||
var traitKey = client.ObjectKey{Namespace: namespace, Name: appConfig.Status.Workloads[0].Traits[0].Reference.Name}
|
||||
return k8sClient.Get(ctx, traitKey, &tr)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
Expect(tr.Object["spec"]).Should(BeEquivalentTo(map[string]interface{}{"key": "test2"}))
|
||||
|
||||
By("===================================== Start to Upgrade revision of component =========================================")
|
||||
cmpV2 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compName}, cmpV2)).Should(Succeed())
|
||||
cmpV2.Spec.Workload = runtime.RawExtension{
|
||||
Object: getDeploy("wordpress:v2"),
|
||||
}
|
||||
By("Update Component")
|
||||
Expect(k8sClient.Update(ctx, cmpV2)).Should(Succeed())
|
||||
By("component handler will automatically create a ne controller revision")
|
||||
Expect(func() bool { _, ok := componentHandler.createControllerRevision(cmpV2, cmpV2); return ok }()).Should(BeTrue())
|
||||
By("Check controller revision created successfully")
|
||||
Eventually(func() error {
|
||||
labels := &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
ControllerRevisionComponentLabel: compName,
|
||||
},
|
||||
}
|
||||
selector, err := metav1.LabelSelectorAsSelector(labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = k8sClient.List(ctx, &crList, &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(crList.Items) != 2 {
|
||||
return fmt.Errorf("there should be exactly 2 revision created but got %d", len(crList.Items))
|
||||
}
|
||||
return nil
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Update appConfig & check successfully")
|
||||
appConfig.Spec.Components[0].RevisionName = compName + "-v2"
|
||||
appConfig.Spec.Components[0].Traits[0].Trait = runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"trait.oam.dev/type": "rollout-revision",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"key": "test3",
|
||||
},
|
||||
}}}
|
||||
Expect(k8sClient.Update(ctx, &appConfig)).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Reconcile for new revision")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check new revision workload created successfully")
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, req)
|
||||
var workloadKey = client.ObjectKey{Namespace: namespace, Name: compName + "-v2"}
|
||||
return k8sClient.Get(ctx, workloadKey, &wr)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Check the new workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
Expect(wr.Spec.Template.Spec.Containers[0].Image).Should(BeEquivalentTo("wordpress:v2"))
|
||||
|
||||
By("Check the new workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
|
||||
By("Check reconcile again and no error will happen")
|
||||
reconcileRetry(reconciler, req)
|
||||
By("Check appconfig condition should not have error")
|
||||
Eventually(func() string {
|
||||
By("Once more Reconcile and should not have error")
|
||||
reconcileRetry(reconciler, req)
|
||||
err := k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if len(appConfig.Status.Conditions) != 1 {
|
||||
return "condition len should be 1 but now is " + strconv.Itoa(len(appConfig.Status.Conditions))
|
||||
}
|
||||
return string(appConfig.Status.Conditions[0].Reason)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeEquivalentTo("ReconcileSuccess"))
|
||||
By("Check trait was updated as expected")
|
||||
Eventually(func() error {
|
||||
tr.SetAPIVersion("example.com/v1")
|
||||
tr.SetKind("Foo")
|
||||
var traitKey = client.ObjectKey{Namespace: namespace, Name: appConfig.Status.Workloads[0].Traits[0].Reference.Name}
|
||||
return k8sClient.Get(ctx, traitKey, &tr)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
Expect(tr.Object["spec"]).Should(BeEquivalentTo(map[string]interface{}{"key": "test3"}))
|
||||
reconciler.applyOnceOnlyMode = "off"
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
var _ = Describe("Component Revision Enabled with workloadName set and apply once only force", func() {
|
||||
const (
|
||||
namespace = "revision-and-workload-name-specified"
|
||||
appName = "revision-apply-once2"
|
||||
compName = "revision-apply-once-comp2"
|
||||
specifiedNameBase = "specified-name-base"
|
||||
specifiedNameV1 = "specified-name-v1"
|
||||
specifiedNameV2 = "specified-name-v2"
|
||||
)
|
||||
var (
|
||||
ctx = context.Background()
|
||||
wr v1.Deployment
|
||||
component v1alpha2.Component
|
||||
appConfig v1alpha2.ApplicationConfiguration
|
||||
appConfigKey = client.ObjectKey{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
}
|
||||
req = reconcile.Request{NamespacedName: appConfigKey}
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {})
|
||||
|
||||
AfterEach(func() {
|
||||
// delete the namespace with all its resources
|
||||
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).
|
||||
Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{}))
|
||||
})
|
||||
|
||||
It("revision enabled should create workload with specified name protect delete with replicas larger than 0", func() {
|
||||
|
||||
getDeploy := func(image, name string) *v1.Deployment {
|
||||
return &v1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
Spec: v1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
|
||||
"app": compName,
|
||||
}},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
|
||||
"app": compName,
|
||||
}},
|
||||
Spec: corev1.PodSpec{Containers: []corev1.Container{{
|
||||
Name: "wordpress",
|
||||
Image: image,
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "wordpress",
|
||||
ContainerPort: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
},
|
||||
}
|
||||
}
|
||||
component = v1alpha2.Component{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
Kind: "Component",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: compName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ComponentSpec{
|
||||
Workload: runtime.RawExtension{
|
||||
Object: getDeploy("wordpress:4.6.1-apache", specifiedNameBase),
|
||||
},
|
||||
},
|
||||
}
|
||||
appConfig = v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
}
|
||||
By("Create namespace")
|
||||
Eventually(
|
||||
func() error {
|
||||
return k8sClient.Create(ctx, &ns)
|
||||
},
|
||||
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Create Component")
|
||||
Expect(k8sClient.Create(ctx, &component)).Should(Succeed())
|
||||
cmpV1 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compName}, cmpV1)).Should(Succeed())
|
||||
|
||||
By("component handler will automatically create controller revision")
|
||||
Expect(func() bool {
|
||||
_, ok := componentHandler.createControllerRevision(cmpV1, cmpV1)
|
||||
return ok
|
||||
}()).Should(BeTrue())
|
||||
var crList v1.ControllerRevisionList
|
||||
By("Check controller revision created successfully")
|
||||
Eventually(func() error {
|
||||
labels := &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
ControllerRevisionComponentLabel: compName,
|
||||
},
|
||||
}
|
||||
selector, err := metav1.LabelSelectorAsSelector(labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = k8sClient.List(ctx, &crList, &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(crList.Items) != 1 {
|
||||
return fmt.Errorf("want only 1 revision created but got %d", len(crList.Items))
|
||||
}
|
||||
return nil
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Create an ApplicationConfiguration")
|
||||
appConfig = v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
RevisionName: compName + "-v1",
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
By("Creat appConfig & check successfully")
|
||||
Expect(k8sClient.Create(ctx, &appConfig)).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Reconcile")
|
||||
reconciler.applyOnceOnlyMode = "force"
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check workload created successfully")
|
||||
var workloadKey1 = client.ObjectKey{Namespace: namespace, Name: specifiedNameBase}
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, req)
|
||||
return k8sClient.Get(ctx, workloadKey1, &wr)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Check workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
|
||||
By("Check reconcile again and no error will happen")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
Expect(k8sClient.Get(ctx, appConfigKey, &appConfig)).Should(BeNil())
|
||||
|
||||
By("===================================== Start to Upgrade revision of component =========================================")
|
||||
cmpV2 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compName}, cmpV2)).Should(Succeed())
|
||||
cmpV2.Spec.Workload = runtime.RawExtension{
|
||||
Object: getDeploy("wordpress:v2", specifiedNameV1),
|
||||
}
|
||||
By("Update Component")
|
||||
Expect(k8sClient.Update(ctx, cmpV2)).Should(Succeed())
|
||||
By("component handler will automatically create a ne controller revision")
|
||||
Expect(func() bool { _, ok := componentHandler.createControllerRevision(cmpV2, cmpV2); return ok }()).Should(BeTrue())
|
||||
By("Check controller revision created successfully")
|
||||
Eventually(func() error {
|
||||
labels := &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
ControllerRevisionComponentLabel: compName,
|
||||
},
|
||||
}
|
||||
selector, err := metav1.LabelSelectorAsSelector(labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = k8sClient.List(ctx, &crList, &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(crList.Items) != 2 {
|
||||
return fmt.Errorf("there should be exactly 2 revision created but got %d", len(crList.Items))
|
||||
}
|
||||
return nil
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Update appConfig & check successfully")
|
||||
appConfig.Spec.Components[0].RevisionName = compName + "-v2"
|
||||
|
||||
Expect(k8sClient.Update(ctx, &appConfig)).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Reconcile for new revision")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check new revision workload created successfully")
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, req)
|
||||
var workloadKey = client.ObjectKey{Namespace: namespace, Name: specifiedNameV1}
|
||||
return k8sClient.Get(ctx, workloadKey, &wr)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Check the new workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
Expect(wr.Spec.Template.Spec.Containers[0].Image).Should(BeEquivalentTo("wordpress:v2"))
|
||||
|
||||
By("Check the new workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
|
||||
By("Check reconcile again")
|
||||
reconcileRetry(reconciler, req)
|
||||
|
||||
By("Check appconfig condition should have error")
|
||||
Eventually(func() string {
|
||||
reconcileRetry(reconciler, req)
|
||||
err := k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if len(appConfig.Status.Conditions) != 1 {
|
||||
return "condition len should be 1 but now is " + strconv.Itoa(len(appConfig.Status.Conditions))
|
||||
}
|
||||
By(fmt.Sprintf("Reconcile with condition %v", appConfig.Status.Conditions[0]))
|
||||
return string(appConfig.Status.Conditions[0].Reason)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeEquivalentTo("ReconcileError"))
|
||||
|
||||
By("Check the old workload still there")
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, req)
|
||||
var workloadKey = client.ObjectKey{Namespace: namespace, Name: specifiedNameBase}
|
||||
return k8sClient.Get(ctx, workloadKey, &wr)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Check the old workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
Expect(wr.Spec.Template.Spec.Containers[0].Image).Should(BeEquivalentTo("wordpress:4.6.1-apache"))
|
||||
|
||||
wr.Spec.Replicas = pointer.Int32Ptr(0)
|
||||
Expect(k8sClient.Update(ctx, &wr)).Should(Succeed())
|
||||
|
||||
By("Reconcile Again and appconfig condition should not have error")
|
||||
Eventually(func() string {
|
||||
By("Once more Reconcile and should not have error")
|
||||
reconcileRetry(reconciler, req)
|
||||
err := k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if len(appConfig.Status.Conditions) != 1 {
|
||||
return "condition len should be 1 but now is " + strconv.Itoa(len(appConfig.Status.Conditions))
|
||||
}
|
||||
return string(appConfig.Status.Conditions[0].Reason)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeEquivalentTo("ReconcileSuccess"))
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: specifiedNameBase}, &wr)).Should(SatisfyAny(util.NotFoundMatcher{}))
|
||||
|
||||
By("===================================== Start to Upgrade revision of component again =========================================")
|
||||
cmpV3 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: compName}, cmpV3)).Should(Succeed())
|
||||
cmpV3.Spec.Workload = runtime.RawExtension{
|
||||
Object: getDeploy("wordpress:v3", specifiedNameV2),
|
||||
}
|
||||
By("Update Component")
|
||||
Expect(k8sClient.Update(ctx, cmpV3)).Should(Succeed())
|
||||
By("component handler will automatically create a ne controller revision")
|
||||
Expect(func() bool { _, ok := componentHandler.createControllerRevision(cmpV3, cmpV3); return ok }()).Should(BeTrue())
|
||||
By("Update the AC and add the revisionEnabled Trait")
|
||||
appConfig.Spec.Components[0].RevisionName = compName + "-v3"
|
||||
appConfig.Spec.Components[0].Traits = []v1alpha2.ComponentTrait{
|
||||
{Trait: runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"apiVersion": "example.com/v1",
|
||||
"kind": "Foo",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"trait.oam.dev/type": "rollout-revision",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"key": "test3",
|
||||
},
|
||||
}}}},
|
||||
}
|
||||
Expect(k8sClient.Update(ctx, &appConfig)).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
Expect(len(appConfig.Spec.Components[0].Traits)).Should(BeEquivalentTo(1))
|
||||
|
||||
By("Check reconcile again and no error will happen, revisionEnabled will skip delete")
|
||||
reconcileRetry(reconciler, req)
|
||||
By("Check appconfig condition should not have error")
|
||||
Eventually(func() string {
|
||||
By("Once more Reconcile and should not have error")
|
||||
reconcileRetry(reconciler, req)
|
||||
err := k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if len(appConfig.Status.Conditions) != 1 {
|
||||
return "condition len should be 1 but now is " + strconv.Itoa(len(appConfig.Status.Conditions))
|
||||
}
|
||||
return string(appConfig.Status.Conditions[0].Reason)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeEquivalentTo("ReconcileSuccess"))
|
||||
|
||||
By("Check new revision workload created successfully")
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, req)
|
||||
var workloadKey = client.ObjectKey{Namespace: namespace, Name: specifiedNameV2}
|
||||
return k8sClient.Get(ctx, workloadKey, &wr)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
By("Check the new workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
Expect(wr.Spec.Template.Spec.Containers[0].Image).Should(BeEquivalentTo("wordpress:v3"))
|
||||
By("Check the new workload should only have 1 generation")
|
||||
Expect(wr.GetGeneration()).Should(BeEquivalentTo(1))
|
||||
By("Check the old workload still there")
|
||||
Eventually(func() error {
|
||||
reconcileRetry(reconciler, req)
|
||||
var workloadKey = client.ObjectKey{Namespace: namespace, Name: specifiedNameV1}
|
||||
return k8sClient.Get(ctx, workloadKey, &wr)
|
||||
}, time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
reconciler.applyOnceOnlyMode = "off"
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ package applicationconfiguration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -44,7 +46,7 @@ var k8sClient client.Client
|
||||
var scheme = runtime.NewScheme()
|
||||
var crd crdv1.CustomResourceDefinition
|
||||
|
||||
func TestReconcilder(t *testing.T) {
|
||||
func TestReconcilerSuit(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
RunSpecsWithDefaultAndCustomReporters(t,
|
||||
@@ -55,9 +57,16 @@ func TestReconcilder(t *testing.T) {
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
ctx := context.Background()
|
||||
By("Bootstrapping test environment")
|
||||
var yamlPath string
|
||||
if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
|
||||
yamlPath = "../../../../../test/compatibility-test/testdata"
|
||||
} else {
|
||||
yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
|
||||
}
|
||||
logf.Log.Info("start applicationconfiguration suit test", "yaml_path", yamlPath)
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{
|
||||
filepath.Join("../../../../..", "charts/vela-core/crds"), // this has all the required CRDs,
|
||||
yamlPath, // this has all the required CRDs,
|
||||
},
|
||||
}
|
||||
var err error
|
||||
@@ -150,13 +159,14 @@ var _ = BeforeSuite(func(done Done) {
|
||||
}, time.Second*30, time.Millisecond*500).Should(BeNil())
|
||||
Expect(mapping.Resource.Resource).Should(Equal("foo"))
|
||||
|
||||
reconciler = NewReconciler(mgr, dm, WithLogger(logging.NewLogrLogger(ctrl.Log.WithName("suit-test-appconfig"))))
|
||||
reconciler = NewReconciler(mgr, dm, logging.NewLogrLogger(ctrl.Log.WithName("suit-test-appconfig")))
|
||||
componentHandler = &ComponentHandler{Client: k8sClient, RevisionLimit: 100, Logger: logging.NewLogrLogger(ctrl.Log.WithName("component-handler"))}
|
||||
|
||||
By("Creating workload definition and trait definition")
|
||||
wd := v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo.example.com",
|
||||
Name: "foo.example.com",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.WorkloadDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
@@ -166,7 +176,8 @@ var _ = BeforeSuite(func(done Done) {
|
||||
}
|
||||
td := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo.example.com",
|
||||
Name: "foo.example.com",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
@@ -177,7 +188,8 @@ var _ = BeforeSuite(func(done Done) {
|
||||
|
||||
rollout := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "rollout-revision",
|
||||
Name: "rollout-revision",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
@@ -186,6 +198,8 @@ var _ = BeforeSuite(func(done Done) {
|
||||
RevisionEnabled: true,
|
||||
},
|
||||
}
|
||||
definitonNs := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}}
|
||||
Expect(k8sClient.Create(context.Background(), definitonNs.DeepCopy())).Should(BeNil())
|
||||
|
||||
// For some reason, WorkloadDefinition is created as a Cluster scope object
|
||||
Expect(k8sClient.Create(ctx, &wd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
@@ -31,7 +31,8 @@ func (r *Reconciler) extractWorkloadTypeAndGVK(ctx context.Context, componentLis
|
||||
}
|
||||
// get the workload definition
|
||||
// the validator webhook has checked that source and the target are the same type
|
||||
wd, err := oamutil.GetWorkloadDefinition(ctx, r, componentType)
|
||||
wd := new(corev1alpha2.WorkloadDefinition)
|
||||
err := oamutil.GetDefinition(ctx, r, wd, componentType)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, fmt.Sprintf("failed to get workload definition %s", componentType))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
const appDeployFinalizer = "finalizers.applicationdeployment.oam.dev"
|
||||
@@ -71,6 +72,8 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (res reconcile.Result, retErr e
|
||||
// TODO: check if the target/source has changed
|
||||
r.handleFinalizer(&appDeploy)
|
||||
|
||||
ctx = oamutil.SetNnamespaceInCtx(ctx, appDeploy.Namespace)
|
||||
|
||||
// Get the target application
|
||||
var targetApp corev1alpha2.Application
|
||||
var sourceApp *corev1alpha2.Application
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package applicationdeployment
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@@ -37,9 +38,16 @@ var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
|
||||
|
||||
By("bootstrapping test environment")
|
||||
var yamlPath string
|
||||
if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
|
||||
yamlPath = "../../../../../test/compatibility-test/testdata"
|
||||
} else {
|
||||
yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
|
||||
}
|
||||
logf.Log.Info("start application deployment suit test", "yaml_path", yamlPath)
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{
|
||||
filepath.Join("../../../..", "charts/vela-core/crds"), // this has all the required CRDs,
|
||||
yamlPath, // this has all the required CRDs,
|
||||
filepath.Join("..", "config", "crd", "bases")},
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,9 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
if err := r.Get(ctx, req.NamespacedName, &manualScalar); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
ctx = util.SetNnamespaceInCtx(ctx, manualScalar.Namespace)
|
||||
|
||||
r.log.Info("Get the manualscalar trait", "ReplicaCount", manualScalar.Spec.ReplicaCount,
|
||||
"Annotations", manualScalar.GetAnnotations())
|
||||
// find the resource object to record the event to, default is the parent appConfig.
|
||||
|
||||
@@ -73,6 +73,8 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
}
|
||||
log.Info("Retrieved trait Autoscaler", "APIVersion", scaler.APIVersion, "Kind", scaler.Kind)
|
||||
|
||||
ctx = util.SetNnamespaceInCtx(ctx, scaler.Namespace)
|
||||
|
||||
// find the resource object to record the event to, default is the parent appConfig.
|
||||
eventObj, err := util.LocateParentAppConfig(ctx, r.Client, &scaler)
|
||||
if err != nil {
|
||||
|
||||
@@ -103,6 +103,8 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
"workload reference", metricsTrait.Spec.WorkloadReference,
|
||||
"labels", metricsTrait.GetLabels())
|
||||
|
||||
ctx = oamutil.SetNnamespaceInCtx(ctx, metricsTrait.Namespace)
|
||||
|
||||
// find the resource object to record the event to, default is the parent appConfig.
|
||||
eventObj, err := oamutil.LocateParentAppConfig(ctx, r.Client, &metricsTrait)
|
||||
if eventObj == nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@@ -68,10 +69,18 @@ var _ = BeforeSuite(func(done Done) {
|
||||
},
|
||||
}
|
||||
By("Bootstrapping test environment")
|
||||
var yamlPath string
|
||||
if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
|
||||
yamlPath = "../../../../../test/compatibility-test/testdata"
|
||||
} else {
|
||||
yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
|
||||
}
|
||||
logf.Log.Info("start metrics test", "yaml_path", yamlPath)
|
||||
useExistCluster := false
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{
|
||||
filepath.Join("../../../../..", "charts/vela-core/crds"), // this has all the required CRDs,
|
||||
yamlPath, // this has all the required oam CRDs,
|
||||
filepath.Join("..", "testdata/crds"),
|
||||
},
|
||||
UseExistingCluster: &useExistCluster,
|
||||
}
|
||||
|
||||
@@ -82,6 +82,8 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
"workload reference", routeTrait.Spec.WorkloadReference,
|
||||
"labels", routeTrait.GetLabels())
|
||||
|
||||
ctx = oamutil.SetNnamespaceInCtx(ctx, routeTrait.Namespace)
|
||||
|
||||
// find the resource object to record the event to, default is the parent appConfig.
|
||||
eventObj, err := oamutil.LocateParentAppConfig(ctx, r.Client, &routeTrait)
|
||||
if eventObj == nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ package routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@@ -75,10 +76,18 @@ var _ = BeforeSuite(func(done Done) {
|
||||
},
|
||||
}
|
||||
By("Bootstrapping test environment")
|
||||
var yamlPath string
|
||||
if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
|
||||
yamlPath = "../../../../../test/compatibility-test/testdata"
|
||||
} else {
|
||||
yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
|
||||
}
|
||||
logf.Log.Info("start route suit_test", "yaml_path", yamlPath)
|
||||
useExistCluster := false
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{
|
||||
filepath.Join("../../../../..", "charts/vela-core/crds"), // this has all the required CRDs,
|
||||
yamlPath, // this has all the required CRDs,
|
||||
filepath.Join("..", "testdata/crds"),
|
||||
},
|
||||
UseExistingCluster: &useExistCluster,
|
||||
}
|
||||
@@ -96,6 +105,9 @@ var _ = BeforeSuite(func(done Done) {
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
By("create definition namespace vela-system")
|
||||
ns := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}}
|
||||
Expect(k8sClient.Create(context.Background(), &ns)).Should(BeNil())
|
||||
|
||||
By("Starting the route trait controller in the background")
|
||||
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
|
||||
@@ -123,14 +135,14 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(k8sClient.Create(context.Background(), &routeNS)).ToNot(HaveOccurred())
|
||||
routeDef := &v1alpha2.TraitDefinition{}
|
||||
routeDef.Name = "route"
|
||||
routeDef.Namespace = RouteNSName
|
||||
routeDef.Namespace = "vela-system"
|
||||
routeDef.Spec.Reference.Name = "routes.standard.oam.dev"
|
||||
routeDef.Spec.WorkloadRefPath = "spec.workloadRef"
|
||||
Expect(k8sClient.Create(context.Background(), routeDef)).ToNot(HaveOccurred())
|
||||
|
||||
webservice := &v1alpha2.WorkloadDefinition{}
|
||||
webservice.Name = "webservice"
|
||||
webservice.Namespace = RouteNSName
|
||||
webservice.Namespace = "vela-system"
|
||||
webservice.Spec.Reference.Name = "deployments.apps"
|
||||
webservice.Spec.ChildResourceKinds = []v1alpha2.ChildResourceKind{{
|
||||
APIVersion: "apps/v1",
|
||||
@@ -143,14 +155,14 @@ var _ = BeforeSuite(func(done Done) {
|
||||
|
||||
deployment := &v1alpha2.WorkloadDefinition{}
|
||||
deployment.Name = "deployment"
|
||||
deployment.Namespace = RouteNSName
|
||||
deployment.Namespace = "vela-system"
|
||||
deployment.Labels = map[string]string{"workload.oam.dev/podspecable": "true"}
|
||||
deployment.Spec.Reference.Name = "deployments.apps"
|
||||
Expect(k8sClient.Create(context.Background(), deployment)).ToNot(HaveOccurred())
|
||||
|
||||
deploy := &v1alpha2.WorkloadDefinition{}
|
||||
deploy.Name = "deploy"
|
||||
deploy.Namespace = RouteNSName
|
||||
deploy.Namespace = "vela-system"
|
||||
deploy.Spec.PodSpecPath = "spec.template.spec"
|
||||
deploy.Spec.Reference.Name = "deployments.apps"
|
||||
Expect(k8sClient.Create(context.Background(), deploy)).ToNot(HaveOccurred())
|
||||
|
||||
@@ -72,6 +72,13 @@ const (
|
||||
errFmtInvalidRevisionType = "invalid type of revision %s, type should not be %v"
|
||||
)
|
||||
|
||||
type namespaceContextKey int
|
||||
|
||||
const (
|
||||
// AppDefinitionNamespace is context key to define app namespace
|
||||
AppDefinitionNamespace namespaceContextKey = iota
|
||||
)
|
||||
|
||||
// A ConditionedObject is an Object type with condition field
|
||||
type ConditionedObject interface {
|
||||
oam.Object
|
||||
@@ -163,10 +170,8 @@ func FetchScopeDefinition(ctx context.Context, r client.Reader, dm discoverymapp
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nn := GenNamespacedDefinitionName(spName)
|
||||
// Fetch the corresponding scopeDefinition CR
|
||||
scopeDefinition := &v1alpha2.ScopeDefinition{}
|
||||
if err := r.Get(ctx, nn, scopeDefinition); err != nil {
|
||||
scopeDefinition := new(v1alpha2.ScopeDefinition)
|
||||
if err = GetDefinition(ctx, r, scopeDefinition, spName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scopeDefinition, nil
|
||||
@@ -180,9 +185,8 @@ func FetchTraitDefinition(ctx context.Context, r client.Reader, dm discoverymapp
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Fetch the corresponding traitDefinition CR
|
||||
traitDefinition, err := GetTraitDefinition(ctx, r, trName)
|
||||
if err != nil {
|
||||
traitDefinition := new(v1alpha2.TraitDefinition)
|
||||
if err = GetDefinition(ctx, r, traitDefinition, trName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return traitDefinition, nil
|
||||
@@ -196,39 +200,70 @@ func FetchWorkloadDefinition(ctx context.Context, r client.Reader, dm discoverym
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Fetch the corresponding workloadDefinition CR
|
||||
workloadDefinition, err := GetWorkloadDefinition(ctx, r, wldName)
|
||||
if err != nil {
|
||||
workloadDefinition := new(v1alpha2.WorkloadDefinition)
|
||||
if err = GetDefinition(ctx, r, workloadDefinition, wldName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return workloadDefinition, nil
|
||||
}
|
||||
|
||||
// GenNamespacedDefinitionName generate definition name with customized namespace
|
||||
func GenNamespacedDefinitionName(dn string) types.NamespacedName {
|
||||
// GetDefinitionNamespaceWithCtx will get namespace from context, it will try get `AppDefinitionNamespace` key, if not found,
|
||||
// will use default system level namespace defined in `systemvar.SystemDefinitonNamespace`
|
||||
func GetDefinitionNamespaceWithCtx(ctx context.Context) string {
|
||||
var appNs string
|
||||
if app := ctx.Value(AppDefinitionNamespace); app == nil {
|
||||
appNs = oam.SystemDefinitonNamespace
|
||||
} else {
|
||||
appNs = app.(string)
|
||||
}
|
||||
return appNs
|
||||
}
|
||||
|
||||
// SetNnamespaceInCtx set app namespace in context,
|
||||
// Sometimes webhook handler may receive request that appNs is empty string, and will cause error when search definition
|
||||
// So if namespace is empty, it will use `default` namespace by default.
|
||||
func SetNnamespaceInCtx(ctx context.Context, appNs string) context.Context {
|
||||
if appNs == "" {
|
||||
// compatible with some webhook handlers that maybe recei ve empty string as app namespace which means `default` namespace
|
||||
appNs = "default"
|
||||
}
|
||||
ctx = context.WithValue(ctx, AppDefinitionNamespace, appNs)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// GetDefinition get definition from two level namespace
|
||||
func GetDefinition(ctx context.Context, cli client.Reader, definition runtime.Object, definitionName string) error {
|
||||
if dns := os.Getenv(DefinitionNamespaceEnv); dns != "" {
|
||||
return types.NamespacedName{Name: dn, Namespace: dns}
|
||||
if err := cli.Get(ctx, types.NamespacedName{Name: definitionName, Namespace: dns}, definition); err == nil {
|
||||
return nil
|
||||
} else if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return types.NamespacedName{Name: dn}
|
||||
appNs := GetDefinitionNamespaceWithCtx(ctx)
|
||||
if err := cli.Get(ctx, types.NamespacedName{Name: definitionName, Namespace: appNs}, definition); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
if err = cli.Get(ctx, types.NamespacedName{Name: definitionName, Namespace: oam.SystemDefinitonNamespace}, definition); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
// compatibility code for old clusters those definition crd is cluster scope
|
||||
var newErr error
|
||||
if newErr = cli.Get(ctx, types.NamespacedName{Name: definitionName}, definition); checkRequestNamespaceError(newErr) {
|
||||
return err
|
||||
}
|
||||
return newErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetWorkloadDefinition Get WorkloadDefinition
|
||||
func GetWorkloadDefinition(ctx context.Context, cli client.Reader,
|
||||
workitemName string) (*v1alpha2.WorkloadDefinition, error) {
|
||||
wd := new(v1alpha2.WorkloadDefinition)
|
||||
if err := cli.Get(ctx, GenNamespacedDefinitionName(workitemName), wd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wd, nil
|
||||
}
|
||||
|
||||
// GetTraitDefinition Get TraitDefinition
|
||||
func GetTraitDefinition(ctx context.Context, cli client.Reader, traitName string) (*v1alpha2.TraitDefinition, error) {
|
||||
td := new(v1alpha2.TraitDefinition)
|
||||
if err := cli.Get(ctx, GenNamespacedDefinitionName(traitName), td); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return td, nil
|
||||
// when get a namespaced scope object without namespace, would get an error request namespace
|
||||
func checkRequestNamespaceError(err error) bool {
|
||||
return err != nil && err.Error() == "an empty namespace may not be set when a resource name is provided"
|
||||
}
|
||||
|
||||
// FetchWorkloadChildResources fetch corresponding child resources given a workload
|
||||
@@ -301,12 +336,13 @@ func PassLabel(parentObj oam.Object, childObj labelAnnotationObject) {
|
||||
childObj.SetLabels(MergeMapOverrideWithDst(parentObj.GetLabels(), childObj.GetLabels()))
|
||||
}
|
||||
|
||||
// PassLabelAndAnnotation passes through labels and annotation objectMeta from the parent to the child object
|
||||
// PassLabelAndAnnotation passes through labels and annotation objectMeta from the parent to the child object,
|
||||
// when annotation or labels has conflicts, the parentObj will override the childObj.
|
||||
func PassLabelAndAnnotation(parentObj oam.Object, childObj labelAnnotationObject) {
|
||||
// pass app-config labels
|
||||
childObj.SetLabels(MergeMapOverrideWithDst(parentObj.GetLabels(), childObj.GetLabels()))
|
||||
childObj.SetLabels(MergeMapOverrideWithDst(childObj.GetLabels(), parentObj.GetLabels()))
|
||||
// pass app-config annotation
|
||||
childObj.SetAnnotations(MergeMapOverrideWithDst(parentObj.GetAnnotations(), childObj.GetAnnotations()))
|
||||
childObj.SetAnnotations(MergeMapOverrideWithDst(childObj.GetAnnotations(), parentObj.GetAnnotations()))
|
||||
}
|
||||
|
||||
// GetDefinitionName return the Definition name of any resources
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -1192,7 +1193,7 @@ func TestPassThroughObjMeta(t *testing.T) {
|
||||
|
||||
gotAnnotation = u.GetAnnotations()
|
||||
wantAnnotation = map[string]string{
|
||||
"key1": "exist value1",
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
}
|
||||
@@ -1200,7 +1201,7 @@ func TestPassThroughObjMeta(t *testing.T) {
|
||||
|
||||
gotLabels := u.GetLabels()
|
||||
wantLabels := map[string]string{
|
||||
"core.oam.dev/ns": "kube-system",
|
||||
"core.oam.dev/ns": "oam-system",
|
||||
"core.oam.dev/kube-native": "deployment",
|
||||
"core.oam.dev/controller": "oam-kubernetes-runtime",
|
||||
}
|
||||
@@ -1330,15 +1331,6 @@ func TestGetDummy(t *testing.T) {
|
||||
}, util.GetDummyWorkloadDefinition(u))
|
||||
}
|
||||
|
||||
func TestNamespacedDefinition(t *testing.T) {
|
||||
ns := "namespaced"
|
||||
n := "definition"
|
||||
_ = os.Setenv(util.DefinitionNamespaceEnv, ns)
|
||||
nn := util.GenNamespacedDefinitionName(n)
|
||||
assert.Equal(t, nn.Namespace, ns)
|
||||
assert.Equal(t, nn.Name, n)
|
||||
}
|
||||
|
||||
func TestRawExtension2Map(t *testing.T) {
|
||||
r1 := runtime.RawExtension{
|
||||
Raw: []byte(`{"a":{"c":"d"},"b":1}`),
|
||||
@@ -1367,3 +1359,508 @@ func TestRawExtension2Map(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, exp1, got2)
|
||||
}
|
||||
|
||||
func TestGenDefinitionNsFromCtx(t *testing.T) {
|
||||
type testcase struct {
|
||||
ctx context.Context
|
||||
wantNs string
|
||||
}
|
||||
testcases := []testcase{
|
||||
{ctx: context.TODO(), wantNs: "vela-system"},
|
||||
{ctx: util.SetNnamespaceInCtx(context.Background(), "vela-app"), wantNs: "vela-app"},
|
||||
{ctx: util.SetNnamespaceInCtx(context.Background(), ""), wantNs: "default"},
|
||||
}
|
||||
for _, ts := range testcases {
|
||||
resNs := util.GetDefinitionNamespaceWithCtx(ts.ctx)
|
||||
assert.Equal(t, ts.wantNs, resNs)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetDefinitionError is try to mock test when get an not existed definition in namespaced scope cluster
|
||||
// will get an error that tpye is not found
|
||||
func TestGetDefinitionError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = util.SetNnamespaceInCtx(ctx, "vela-app")
|
||||
|
||||
errNotFound := apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, "mock")
|
||||
errNeedNamespace := fmt.Errorf("an empty namespace may not be set when a resource name is provided")
|
||||
|
||||
getFunc := func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
ns := key.Namespace
|
||||
if ns != "" {
|
||||
return errNotFound
|
||||
} else {
|
||||
return errNeedNamespace
|
||||
}
|
||||
}
|
||||
|
||||
client := test.MockClient{MockGet: getFunc}
|
||||
td := new(v1alpha2.TraitDefinition)
|
||||
got := util.GetDefinition(ctx, &client, td, "mock")
|
||||
assert.Equal(t, errNotFound, got)
|
||||
}
|
||||
|
||||
// TestGetDefinitionWithClusterScope is try to test compatibility of GetDefinition,
|
||||
// GetDefinition try to search definition in system-level namespace firstly,
|
||||
// if not found will search in app namespace, still cannot find it, try to search definition without namespace
|
||||
func TestGetDefinitionWithClusterScope(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = util.SetNnamespaceInCtx(ctx, "vela-app")
|
||||
// system-level definition
|
||||
sys := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sysDefinition",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: "definitionrefrence.core.oam.dev",
|
||||
},
|
||||
},
|
||||
}
|
||||
// app workload Definition
|
||||
app := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "appDefinition",
|
||||
Namespace: "vela-app",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: "definitionrefrence",
|
||||
},
|
||||
},
|
||||
}
|
||||
// old cluster workload trait scope definition crd is cluster scope, the namesapce field is empty
|
||||
noNs := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "noNsDefinition",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: "definitionrefrence",
|
||||
},
|
||||
},
|
||||
}
|
||||
tdList := []v1alpha2.TraitDefinition{app, sys, noNs}
|
||||
mockIndexer := map[string]v1alpha2.TraitDefinition{}
|
||||
for i := 0; i < len(tdList); i++ {
|
||||
var key string
|
||||
if tdList[i].Namespace != "" {
|
||||
key = tdList[i].Namespace + "/" + tdList[i].Name
|
||||
} else {
|
||||
key = tdList[i].Name
|
||||
}
|
||||
mockIndexer[key] = tdList[i]
|
||||
}
|
||||
|
||||
getFunc := func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
var namespacedName string
|
||||
if key.Namespace != "" {
|
||||
namespacedName = key.Namespace + "/" + key.Name
|
||||
} else {
|
||||
namespacedName = key.Name
|
||||
}
|
||||
td, ok := mockIndexer[namespacedName]
|
||||
if ok {
|
||||
obj, _ := obj.(*v1alpha2.TraitDefinition)
|
||||
*obj = td
|
||||
return nil
|
||||
} else {
|
||||
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, namespacedName)
|
||||
}
|
||||
}
|
||||
|
||||
type want struct {
|
||||
td *v1alpha2.TraitDefinition
|
||||
err error
|
||||
}
|
||||
testcases := map[string]struct {
|
||||
tdName string
|
||||
want want
|
||||
}{
|
||||
"app namespace is first level": {
|
||||
tdName: "appDefinition",
|
||||
want: want{
|
||||
err: nil,
|
||||
td: &app,
|
||||
},
|
||||
},
|
||||
"got sys namespace in system levle": {
|
||||
tdName: "sysDefinition",
|
||||
want: want{
|
||||
err: nil,
|
||||
td: &sys,
|
||||
},
|
||||
},
|
||||
"old cluster traitdefinition crd is cluster scope": {
|
||||
tdName: "noNsDefinition",
|
||||
want: want{
|
||||
err: nil,
|
||||
td: &noNs,
|
||||
},
|
||||
},
|
||||
"return err search not exsited definition": {
|
||||
tdName: "notExistedDefinition",
|
||||
want: want{
|
||||
err: apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, "notExistedDefinition"),
|
||||
td: new(v1alpha2.TraitDefinition),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tclient := test.MockClient{MockGet: getFunc}
|
||||
|
||||
for name, tc := range testcases {
|
||||
got := new(v1alpha2.TraitDefinition)
|
||||
err := util.GetDefinition(ctx, &tclient, got, tc.tdName)
|
||||
t.Log(fmt.Sprint("Running test: ", name))
|
||||
|
||||
assert.Equal(t, tc.want.err, err)
|
||||
assert.Equal(t, tc.want.td, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetWorkloadDefinition(t *testing.T) {
|
||||
// Test common variables
|
||||
ctx := context.Background()
|
||||
ctx = util.SetNnamespaceInCtx(ctx, "vela-app")
|
||||
|
||||
// sys workload Definition
|
||||
sysWorkloadDefinition := v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mockdefinition",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.WorkloadDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: "definitionrefrence.core.oam.dev",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// app workload Definition
|
||||
appWorkloadDefinition := v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mockdefinition.core.oam.dev",
|
||||
Namespace: "vela-app",
|
||||
},
|
||||
Spec: v1alpha2.WorkloadDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: "definitionrefrence.core.oam.dev",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type fields struct {
|
||||
getFunc test.MockGetFn
|
||||
}
|
||||
type want struct {
|
||||
wld v1alpha2.WorkloadDefinition
|
||||
err error
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
fields fields
|
||||
want want
|
||||
}{
|
||||
|
||||
"app defintion will overlay system definition": {
|
||||
fields: fields{
|
||||
getFunc: func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
o := obj.(*v1alpha2.WorkloadDefinition)
|
||||
if key.Namespace == "vela-system" {
|
||||
*o = sysWorkloadDefinition
|
||||
} else {
|
||||
*o = appWorkloadDefinition
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
wld: appWorkloadDefinition,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
"return system definiton when cannot find in app ns": {
|
||||
fields: fields{
|
||||
getFunc: func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
if key.Namespace == "vela-system" {
|
||||
o := obj.(*v1alpha2.WorkloadDefinition)
|
||||
*o = sysWorkloadDefinition
|
||||
return nil
|
||||
}
|
||||
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "workloadDefinition"}, key.Name)
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
wld: sysWorkloadDefinition,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, tc := range cases {
|
||||
tclient := test.MockClient{
|
||||
MockGet: tc.fields.getFunc,
|
||||
}
|
||||
got := new(v1alpha2.WorkloadDefinition)
|
||||
err := util.GetDefinition(ctx, &tclient, got, "mockdefinition")
|
||||
t.Log(fmt.Sprint("Running test: ", name))
|
||||
|
||||
assert.Equal(t, tc.want.err, err)
|
||||
assert.Equal(t, tc.want.wld, *got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTraitDefinition(t *testing.T) {
|
||||
// Test common variables
|
||||
ctx := context.Background()
|
||||
ctx = util.SetNnamespaceInCtx(ctx, "vela-app")
|
||||
|
||||
// sys workload Definition
|
||||
sysTraitDefinition := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mockdefinition",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: "definitionrefrence.core.oam.dev",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// app workload Definition
|
||||
appTraitDefinition := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mockdefinition.core.oam.dev",
|
||||
Namespace: "vela-app",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: "definitionrefrence.core.oam.dev",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type fields struct {
|
||||
getFunc test.MockGetFn
|
||||
}
|
||||
type want struct {
|
||||
wld v1alpha2.TraitDefinition
|
||||
err error
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
fields fields
|
||||
want want
|
||||
}{
|
||||
"app defintion will overlay system definition": {
|
||||
fields: fields{
|
||||
getFunc: func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
o := obj.(*v1alpha2.TraitDefinition)
|
||||
if key.Namespace == "vela-system" {
|
||||
*o = sysTraitDefinition
|
||||
} else {
|
||||
*o = appTraitDefinition
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
wld: appTraitDefinition,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
"return system definiton when cannot find in app ns": {
|
||||
fields: fields{
|
||||
getFunc: func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
if key.Namespace == "vela-system" {
|
||||
o := obj.(*v1alpha2.TraitDefinition)
|
||||
*o = sysTraitDefinition
|
||||
return nil
|
||||
}
|
||||
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "workloadDefinition"}, key.Name)
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
wld: sysTraitDefinition,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, tc := range cases {
|
||||
tclient := test.MockClient{
|
||||
MockGet: tc.fields.getFunc,
|
||||
}
|
||||
got := new(v1alpha2.TraitDefinition)
|
||||
err := util.GetDefinition(ctx, &tclient, got, "mockdefinition")
|
||||
t.Log(fmt.Sprint("Running test: ", name))
|
||||
|
||||
assert.Equal(t, tc.want.err, err)
|
||||
assert.Equal(t, tc.want.wld, *got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefinition(t *testing.T) {
|
||||
// Test common variables
|
||||
env := "env-namespace"
|
||||
|
||||
// sys workload Definition
|
||||
sysTraitDefinition := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mockdefinition",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
}
|
||||
|
||||
// app workload Definition
|
||||
appTraitDefinition := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mockdefinition",
|
||||
Namespace: "vela-app",
|
||||
},
|
||||
}
|
||||
|
||||
// env workload Definition
|
||||
envTraitDefinition := v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mockdefinition",
|
||||
Namespace: env,
|
||||
},
|
||||
}
|
||||
|
||||
cli := test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
o := obj.(*v1alpha2.TraitDefinition)
|
||||
switch key.Namespace {
|
||||
case "vela-system":
|
||||
*o = sysTraitDefinition
|
||||
case "vela-app":
|
||||
*o = appTraitDefinition
|
||||
case env:
|
||||
*o = envTraitDefinition
|
||||
default:
|
||||
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, key.Name)
|
||||
}
|
||||
return nil
|
||||
}}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = util.SetNnamespaceInCtx(ctx, "vela-app")
|
||||
appTd := new(v1alpha2.TraitDefinition)
|
||||
err := util.GetDefinition(ctx, &cli, appTd, "mockTrait")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, &appTraitDefinition, appTd)
|
||||
|
||||
err = os.Setenv(util.DefinitionNamespaceEnv, env)
|
||||
assert.Equal(t, nil, err)
|
||||
envTd := new(v1alpha2.TraitDefinition)
|
||||
err = util.GetDefinition(ctx, &cli, envTd, "mockTrait")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, &envTraitDefinition, envTd)
|
||||
}
|
||||
|
||||
func TestGetScopeDefiniton(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
namespace := "vela-app"
|
||||
ctx = util.SetNnamespaceInCtx(ctx, namespace)
|
||||
scopeDefinitionKind := "ScopeDefinition"
|
||||
mockVerision := "core.oam.dev/v1alpha2"
|
||||
scopeDefinitionName := "mockscopes.core.oam.dev"
|
||||
scopeDefinitionRefName := "mockscopes.core.oam.dev"
|
||||
scopeDefinitionWorkloadRefsPath := "spec.workloadRefs"
|
||||
|
||||
sysScopeDefinition := v1alpha2.ScopeDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: scopeDefinitionKind,
|
||||
APIVersion: mockVerision,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: scopeDefinitionName,
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.ScopeDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: scopeDefinitionRefName,
|
||||
},
|
||||
WorkloadRefsPath: scopeDefinitionWorkloadRefsPath,
|
||||
AllowComponentOverlap: false,
|
||||
},
|
||||
}
|
||||
|
||||
appScopeDefinition := v1alpha2.ScopeDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: scopeDefinitionKind,
|
||||
APIVersion: mockVerision,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: scopeDefinitionName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ScopeDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
Name: scopeDefinitionRefName,
|
||||
},
|
||||
WorkloadRefsPath: scopeDefinitionWorkloadRefsPath,
|
||||
AllowComponentOverlap: false,
|
||||
},
|
||||
}
|
||||
type fields struct {
|
||||
getFunc test.MockGetFn
|
||||
}
|
||||
type want struct {
|
||||
spd *v1alpha2.ScopeDefinition
|
||||
err error
|
||||
}
|
||||
cases := map[string]struct {
|
||||
fields fields
|
||||
want want
|
||||
}{
|
||||
"app defintion will overlay system definition": {
|
||||
fields: fields{
|
||||
getFunc: func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
o := obj.(*v1alpha2.ScopeDefinition)
|
||||
if key.Namespace == "vela-system" {
|
||||
*o = sysScopeDefinition
|
||||
} else {
|
||||
*o = appScopeDefinition
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
spd: &appScopeDefinition,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
"return system definiton when cannot find in app ns": {
|
||||
fields: fields{
|
||||
getFunc: func(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
if key.Namespace == "vela-system" {
|
||||
o := obj.(*v1alpha2.ScopeDefinition)
|
||||
*o = sysScopeDefinition
|
||||
return nil
|
||||
}
|
||||
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "scopeDefinition"}, key.Name)
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
spd: &sysScopeDefinition,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, tc := range cases {
|
||||
tclient := test.MockClient{
|
||||
MockGet: tc.fields.getFunc,
|
||||
}
|
||||
got := new(v1alpha2.ScopeDefinition)
|
||||
err := util.GetDefinition(ctx, &tclient, got, "mockdefinition")
|
||||
t.Log(fmt.Sprint("Running test: ", name))
|
||||
assert.Equal(t, tc.want.err, err)
|
||||
assert.Equal(t, tc.want.spd, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,23 +24,24 @@ type Template struct {
|
||||
}
|
||||
|
||||
// GetScopeGVK Get ScopeDefinition
|
||||
func GetScopeGVK(cli client.Client, dm discoverymapper.DiscoveryMapper,
|
||||
func GetScopeGVK(ctx context.Context, cli client.Reader, dm discoverymapper.DiscoveryMapper,
|
||||
name string) (schema.GroupVersionKind, error) {
|
||||
var gvk schema.GroupVersionKind
|
||||
sd := new(v1alpha2.ScopeDefinition)
|
||||
if err := cli.Get(context.Background(), client.ObjectKey{
|
||||
Name: name,
|
||||
}, sd); err != nil {
|
||||
err := GetDefinition(ctx, cli, sd, name)
|
||||
if err != nil {
|
||||
return gvk, err
|
||||
}
|
||||
|
||||
return GetGVKFromDefinition(dm, sd.Spec.Reference)
|
||||
}
|
||||
|
||||
// LoadTemplate Get template according to key
|
||||
func LoadTemplate(cli client.Reader, key string, kd types.CapType) (*Template, error) {
|
||||
func LoadTemplate(ctx context.Context, cli client.Reader, key string, kd types.CapType) (*Template, error) {
|
||||
switch kd {
|
||||
case types.TypeWorkload:
|
||||
wd, err := GetWorkloadDefinition(context.TODO(), cli, key)
|
||||
wd := new(v1alpha2.WorkloadDefinition)
|
||||
err := GetDefinition(ctx, cli, wd, key)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "LoadTemplate [%s] ", key)
|
||||
}
|
||||
@@ -59,7 +60,8 @@ func LoadTemplate(cli client.Reader, key string, kd types.CapType) (*Template, e
|
||||
return tmpl, nil
|
||||
|
||||
case types.TypeTrait:
|
||||
td, err := GetTraitDefinition(context.TODO(), cli, key)
|
||||
td := new(v1alpha2.TraitDefinition)
|
||||
err := GetDefinition(ctx, cli, td, key)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "LoadTemplate [%s] ", key)
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: default
|
||||
annotations:
|
||||
definition.oam.dev/description: "Long-running scalable backend worker without network endpoint"
|
||||
spec:
|
||||
@@ -89,7 +90,7 @@ spec:
|
||||
},
|
||||
}
|
||||
|
||||
temp, err := LoadTemplate(&tclient, "worker", types.TypeWorkload)
|
||||
temp, err := LoadTemplate(context.TODO(), &tclient, "worker", types.TypeWorkload)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -172,6 +173,7 @@ metadata:
|
||||
definition.oam.dev/description: "Configures K8s ingress and service to enable web traffic for your service.
|
||||
Please use route trait in cap center for advanced usage."
|
||||
name: ingress
|
||||
namespace: default
|
||||
spec:
|
||||
status:
|
||||
customStatus: |-
|
||||
@@ -204,7 +206,7 @@ spec:
|
||||
},
|
||||
}
|
||||
|
||||
temp, err := LoadTemplate(&tclient, "ingress", types.TypeTrait)
|
||||
temp, err := LoadTemplate(context.TODO(), &tclient, "ingress", types.TypeTrait)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
6
pkg/oam/var.go
Normal file
6
pkg/oam/var.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package oam
|
||||
|
||||
// SystemDefinitonNamespace golbal value for controller and webhook systemlevel namespace
|
||||
var (
|
||||
SystemDefinitonNamespace string = "vela-system"
|
||||
)
|
||||
@@ -106,7 +106,7 @@ var _ = BeforeSuite(func(done Done) {
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
var DefinitionNamespace = "testdef"
|
||||
var DefinitionNamespace = "vela-system"
|
||||
var _ = AfterSuite(func() {
|
||||
By("tearing down the test environment")
|
||||
_ = k8sClient.Delete(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: DefinitionNamespace}})
|
||||
|
||||
1
pkg/plugins/testdata/ingressDef.yaml
vendored
1
pkg/plugins/testdata/ingressDef.yaml
vendored
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: ingress.test
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
|
||||
2
pkg/plugins/testdata/manualscalars.yaml
vendored
2
pkg/plugins/testdata/manualscalars.yaml
vendored
@@ -2,7 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: scaler
|
||||
namespace: default
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- core.oam.dev/v1alpha2.ContainerizedWorkload
|
||||
|
||||
1
pkg/plugins/testdata/traitDef.yaml
vendored
1
pkg/plugins/testdata/traitDef.yaml
vendored
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: routes.test
|
||||
namespace: vela-system
|
||||
labels:
|
||||
usecase: forplugintest
|
||||
spec:
|
||||
|
||||
1
pkg/plugins/testdata/websvcWorkloadDef.yaml
vendored
1
pkg/plugins/testdata/websvcWorkloadDef.yaml
vendored
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: webservice.testapps
|
||||
namespace: vela-system
|
||||
labels:
|
||||
usecase: forplugintest
|
||||
spec:
|
||||
|
||||
1
pkg/plugins/testdata/workloadDef.yaml
vendored
1
pkg/plugins/testdata/workloadDef.yaml
vendored
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: deployments.testapps
|
||||
namespace: vela-system
|
||||
labels:
|
||||
usecase: forplugintest
|
||||
spec:
|
||||
|
||||
@@ -3,22 +3,23 @@ package apply
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
// Applicator applies new state to an object or create it if not exist.
|
||||
// It employes the same mechanism as `kubectl apply`, that is, for each resource being applied,
|
||||
// It uses the same mechanism as `kubectl apply`, that is, for each resource being applied,
|
||||
// computing a three-way diff merge in client side based on its current state, modified stated,
|
||||
// and last-applied-state which is tracked through an specific annotaion.
|
||||
// and last-applied-state which is tracked through an specific annotation.
|
||||
// If the resource doesn't exist before, Apply will create it.
|
||||
type Applicator interface {
|
||||
Apply(context.Context, runtime.Object, ...ApplyOption) error
|
||||
@@ -32,20 +33,23 @@ type ApplyOption func(ctx context.Context, existing, desired runtime.Object) err
|
||||
|
||||
// NewAPIApplicator creates an Applicator that applies state to an
|
||||
// object or creates the object if not exist.
|
||||
func NewAPIApplicator(c client.Client) *APIApplicator {
|
||||
func NewAPIApplicator(c client.Client, log logging.Logger) *APIApplicator {
|
||||
return &APIApplicator{
|
||||
creatorFn(createOrGetExisting),
|
||||
patcherFn(threeWayMergePatch), c}
|
||||
creator: creatorFn(createOrGetExisting),
|
||||
patcher: patcherFn(threeWayMergePatch),
|
||||
c: c,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
type creator interface {
|
||||
createOrGetExisting(context.Context, client.Client, runtime.Object, ...ApplyOption) (runtime.Object, error)
|
||||
createOrGetExisting(context.Context, logging.Logger, client.Client, runtime.Object, ...ApplyOption) (runtime.Object, error)
|
||||
}
|
||||
|
||||
type creatorFn func(context.Context, client.Client, runtime.Object, ...ApplyOption) (runtime.Object, error)
|
||||
type creatorFn func(context.Context, logging.Logger, client.Client, runtime.Object, ...ApplyOption) (runtime.Object, error)
|
||||
|
||||
func (fn creatorFn) createOrGetExisting(ctx context.Context, c client.Client, o runtime.Object, ao ...ApplyOption) (runtime.Object, error) {
|
||||
return fn(ctx, c, o, ao...)
|
||||
func (fn creatorFn) createOrGetExisting(ctx context.Context, log logging.Logger, c client.Client, o runtime.Object, ao ...ApplyOption) (runtime.Object, error) {
|
||||
return fn(ctx, log, c, o, ao...)
|
||||
}
|
||||
|
||||
type patcher interface {
|
||||
@@ -62,12 +66,23 @@ func (fn patcherFn) patch(c, m runtime.Object) (client.Patch, error) {
|
||||
type APIApplicator struct {
|
||||
creator
|
||||
patcher
|
||||
c client.Client
|
||||
c client.Client
|
||||
log logging.Logger
|
||||
}
|
||||
|
||||
// loggingApply will record a log with desired object applied
|
||||
func loggingApply(log logging.Logger, msg string, desired runtime.Object) {
|
||||
d, ok := desired.(metav1.Object)
|
||||
if !ok {
|
||||
log.Debug(msg, "resource", desired.GetObjectKind().GroupVersionKind().String())
|
||||
return
|
||||
}
|
||||
log.Debug(msg, "name", d.GetName(), "resource", desired.GetObjectKind().GroupVersionKind().String())
|
||||
}
|
||||
|
||||
// Apply applies new state to an object or create it if not exist
|
||||
func (a *APIApplicator) Apply(ctx context.Context, desired runtime.Object, ao ...ApplyOption) error {
|
||||
existing, err := a.createOrGetExisting(ctx, a.c, desired, ao...)
|
||||
existing, err := a.createOrGetExisting(ctx, a.log, a.c, desired, ao...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -79,6 +94,7 @@ func (a *APIApplicator) Apply(ctx context.Context, desired runtime.Object, ao ..
|
||||
if err := executeApplyOptions(ctx, existing, desired, ao); err != nil {
|
||||
return err
|
||||
}
|
||||
loggingApply(a.log, "patching object", desired)
|
||||
patch, err := a.patcher.patch(existing, desired)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot calculate patch by computing a three way diff")
|
||||
@@ -88,7 +104,7 @@ func (a *APIApplicator) Apply(ctx context.Context, desired runtime.Object, ao ..
|
||||
|
||||
// createOrGetExisting will create the object if it does not exist
|
||||
// or get and return the existing object
|
||||
func createOrGetExisting(ctx context.Context, c client.Client, desired runtime.Object, ao ...ApplyOption) (runtime.Object, error) {
|
||||
func createOrGetExisting(ctx context.Context, log logging.Logger, c client.Client, desired runtime.Object, ao ...ApplyOption) (runtime.Object, error) {
|
||||
m, ok := desired.(oam.Object)
|
||||
if !ok {
|
||||
return nil, errors.New("cannot access object metadata")
|
||||
@@ -102,6 +118,7 @@ func createOrGetExisting(ctx context.Context, c client.Client, desired runtime.O
|
||||
if err := addLastAppliedConfigAnnotation(desired); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loggingApply(log, "creating object", desired)
|
||||
return nil, errors.Wrap(c.Create(ctx, desired), "cannot create object")
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev"
|
||||
oamstd "github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -56,7 +57,7 @@ var _ = BeforeSuite(func(done Done) {
|
||||
rawClient, err = client.New(cfg, client.Options{Scheme: testScheme})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(rawClient).ShouldNot(BeNil())
|
||||
k8sApplicator = NewAPIApplicator(rawClient)
|
||||
k8sApplicator = NewAPIApplicator(rawClient, logging.NewNopLogger())
|
||||
|
||||
By("Create test namespace")
|
||||
applyNS = corev1.Namespace{
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/logging"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/pkg/errors"
|
||||
@@ -117,13 +118,14 @@ func TestAPIApplicator(t *testing.T) {
|
||||
for caseName, tc := range cases {
|
||||
t.Run(caseName, func(t *testing.T) {
|
||||
a := &APIApplicator{
|
||||
creator: creatorFn(func(_ context.Context, _ client.Client, _ runtime.Object, _ ...ApplyOption) (runtime.Object, error) {
|
||||
creator: creatorFn(func(_ context.Context, _ logging.Logger, _ client.Client, _ runtime.Object, _ ...ApplyOption) (runtime.Object, error) {
|
||||
return tc.args.existing, tc.args.creatorErr
|
||||
}),
|
||||
patcher: patcherFn(func(c, m runtime.Object) (client.Patch, error) {
|
||||
return nil, tc.args.patcherErr
|
||||
}),
|
||||
c: tc.c,
|
||||
c: tc.c,
|
||||
log: logging.NewNopLogger(),
|
||||
}
|
||||
result := a.Apply(ctx, tc.args.desired, tc.args.ao...)
|
||||
if diff := cmp.Diff(tc.want, result, test.EquateErrors()); diff != "" {
|
||||
@@ -284,7 +286,7 @@ func TestCreator(t *testing.T) {
|
||||
|
||||
for caseName, tc := range cases {
|
||||
t.Run(caseName, func(t *testing.T) {
|
||||
result, err := createOrGetExisting(ctx, tc.c, tc.args.desired, tc.args.ao...)
|
||||
result, err := createOrGetExisting(ctx, logging.NewNopLogger(), tc.c, tc.args.desired, tc.args.ao...)
|
||||
if diff := cmp.Diff(tc.want.existing, result); diff != "" {
|
||||
t.Errorf("\n%s\ncreateOrGetExisting(...): -want , +got \n%s\n", tc.reason, diff)
|
||||
}
|
||||
|
||||
@@ -19,11 +19,14 @@ package application
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -59,10 +62,15 @@ func TestAPIs(t *testing.T) {
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
|
||||
|
||||
By("bootstrapping test environment")
|
||||
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{
|
||||
CRDDirectoryPaths: []string{filepath.Join("../../../../..", "charts", "vela-core", "crds")},
|
||||
CRDDirectoryPaths: []string{yamlPath},
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -86,6 +94,8 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(decoder).ToNot(BeNil())
|
||||
|
||||
ctx := context.Background()
|
||||
ns := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}}
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(BeNil())
|
||||
wd := &v1alpha2.WorkloadDefinition{}
|
||||
wDDefJson, _ := yaml.YAMLToJSON([]byte(wDDefYaml))
|
||||
Expect(json.Unmarshal(wDDefJson, wd)).Should(BeNil())
|
||||
@@ -111,6 +121,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: vela-system
|
||||
annotations:
|
||||
definition.oam.dev/description: "Long-running scalable backend worker without network endpoint"
|
||||
spec:
|
||||
@@ -163,6 +174,7 @@ metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: scaler
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ admission.Handler = &ValidatingHandler{}
|
||||
@@ -53,10 +54,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)
|
||||
}
|
||||
|
||||
ctx = util.SetNnamespaceInCtx(ctx, app.Namespace)
|
||||
switch req.Operation {
|
||||
case admissionv1beta1.Create:
|
||||
if allErrs := h.ValidateCreate(app); len(allErrs) > 0 {
|
||||
if allErrs := h.ValidateCreate(ctx, app); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, allErrs.ToAggregate())
|
||||
}
|
||||
case admissionv1beta1.Update:
|
||||
@@ -65,7 +66,7 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
return admission.Errored(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
if allErrs := h.ValidateUpdate(app, oldApp); len(allErrs) > 0 {
|
||||
if allErrs := h.ValidateUpdate(ctx, app, oldApp); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, allErrs.ToAggregate())
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
@@ -9,20 +11,20 @@ import (
|
||||
)
|
||||
|
||||
// ValidateCreate validates the Application on creation
|
||||
func (h *ValidatingHandler) ValidateCreate(app *v1alpha2.Application) field.ErrorList {
|
||||
func (h *ValidatingHandler) ValidateCreate(ctx context.Context, app *v1alpha2.Application) field.ErrorList {
|
||||
var componentErrs field.ErrorList
|
||||
// try to generate an app file
|
||||
appParser := appfile.NewApplicationParser(h.Client, h.dm)
|
||||
if _, err := appParser.GenerateAppFile(app.Name, app); err != nil {
|
||||
if _, err := appParser.GenerateAppFile(ctx, app.Name, app); err != nil {
|
||||
componentErrs = append(componentErrs, field.Invalid(field.NewPath("spec"), app, err.Error()))
|
||||
}
|
||||
return componentErrs
|
||||
}
|
||||
|
||||
// ValidateUpdate validates the Application on update
|
||||
func (h *ValidatingHandler) ValidateUpdate(newApp, oldApp *v1alpha2.Application) field.ErrorList {
|
||||
func (h *ValidatingHandler) ValidateUpdate(ctx context.Context, newApp, oldApp *v1alpha2.Application) field.ErrorList {
|
||||
// check if the newApp is valid
|
||||
componentErrs := h.ValidateCreate(newApp)
|
||||
componentErrs := h.ValidateCreate(ctx, newApp)
|
||||
// one can't add a rollout annotation to an existing application
|
||||
if _, exist := oldApp.GetAnnotations()[oam.AnnotationAppRollout]; !exist {
|
||||
if _, exist := newApp.GetAnnotations()[oam.AnnotationAppRollout]; exist {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -94,6 +95,7 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
return admission.ValidationResponse(true, "")
|
||||
}
|
||||
vAppConfig := &ValidatingAppConfig{}
|
||||
ctx = util.SetNnamespaceInCtx(ctx, obj.Namespace)
|
||||
if err := vAppConfig.PrepareForValidation(ctx, h.Client, h.Mapper, obj); err != nil {
|
||||
klog.Info("failed preparing information before validation ", " name: ", obj.Name, " errMsg: ", err.Error())
|
||||
return admission.Denied(err.Error())
|
||||
|
||||
50
test/compatibility-test/convert/main.go
Normal file
50
test/compatibility-test/convert/main.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package main // #nosec
|
||||
|
||||
// generate compatibility testdata
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var srcdir, dstdir string
|
||||
if len(os.Args) > 1 {
|
||||
srcdir = os.Args[1]
|
||||
dstdir = os.Args[2]
|
||||
}
|
||||
err := filepath.Walk(srcdir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
/* #nosec */
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to read file", err)
|
||||
return err
|
||||
}
|
||||
fileName := info.Name()
|
||||
var newdata string
|
||||
if fileName == "core.oam.dev_workloaddefinitions.yaml" || fileName == "core.oam.dev_traitdefinitions.yaml" || fileName == "core.oam.dev_scopedefinitions.yaml" {
|
||||
newdata = strings.ReplaceAll(string(data), "scope: Namespaced", "scope: Cluster")
|
||||
} else {
|
||||
newdata = string(data)
|
||||
}
|
||||
dstpath := dstdir + "/" + fileName
|
||||
/* #nosec */
|
||||
if err = ioutil.WriteFile(dstpath, []byte(newdata), 0644); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to write file:", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,8 @@ var _ = Describe("Finalizer for HealthScope in ApplicationConfiguration", func()
|
||||
// create health scope definition
|
||||
sd := v1alpha2.ScopeDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "healthscopes.core.oam.dev",
|
||||
Name: "healthscopes.core.oam.dev",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.ScopeDefinitionSpec{
|
||||
AllowComponentOverlap: true,
|
||||
|
||||
@@ -533,7 +533,7 @@ var _ = Describe("Component revision", func() {
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "manualscalertraits2.core.oam.dev",
|
||||
Namespace: namespace,
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
RevisionEnabled: true,
|
||||
|
||||
@@ -75,8 +75,9 @@ var _ = Describe("ContainerizedWorkload", func() {
|
||||
label = map[string]string{fakeLabelKey: "containerized-workload"}
|
||||
wd = v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "containerizedworkloads.core.oam.dev",
|
||||
Labels: label,
|
||||
Name: "containerizedworkloads.core.oam.dev",
|
||||
Namespace: "vela-system",
|
||||
Labels: label,
|
||||
},
|
||||
Spec: v1alpha2.WorkloadDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
|
||||
@@ -70,7 +70,8 @@ var _ = Describe("HealthScope", func() {
|
||||
// create health scope definition
|
||||
sd := v1alpha2.ScopeDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "healthscope.core.oam.dev",
|
||||
Name: "healthscope.core.oam.dev",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.ScopeDefinitionSpec{
|
||||
AllowComponentOverlap: true,
|
||||
@@ -106,8 +107,9 @@ var _ = Describe("HealthScope", func() {
|
||||
// create a workload definition
|
||||
wd := v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "containerizedworkloads.core.oam.dev",
|
||||
Labels: label,
|
||||
Name: "containerizedworkloads.core.oam.dev",
|
||||
Namespace: "vela-system",
|
||||
Labels: label,
|
||||
},
|
||||
Spec: v1alpha2.WorkloadDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
|
||||
@@ -63,8 +63,9 @@ var _ = Describe("Test kubernetes native workloads", func() {
|
||||
// create a workload definition for
|
||||
wd := v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "deployments.apps",
|
||||
Labels: label,
|
||||
Name: "deployments.apps",
|
||||
Namespace: "vela-system",
|
||||
Labels: label,
|
||||
},
|
||||
Spec: v1alpha2.WorkloadDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
|
||||
@@ -106,8 +106,9 @@ var _ = BeforeSuite(func(done Done) {
|
||||
// Create manual scaler trait definition
|
||||
manualscalertrait = v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "manualscalertraits.core.oam.dev",
|
||||
Labels: map[string]string{"trait": "manualscalertrait"},
|
||||
Name: "manualscalertraits.core.oam.dev",
|
||||
Namespace: "vela-system",
|
||||
Labels: map[string]string{"trait": "manualscalertrait"},
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
WorkloadRefPath: "spec.workloadRef",
|
||||
@@ -129,8 +130,9 @@ var _ = BeforeSuite(func(done Done) {
|
||||
|
||||
extendedmanualscalertrait = v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "manualscalertraits-extended.core.oam.dev",
|
||||
Labels: map[string]string{"trait": "manualscalertrait"},
|
||||
Name: "manualscalertraits-extended.core.oam.dev",
|
||||
Namespace: "vela-system",
|
||||
Labels: map[string]string{"trait": "manualscalertrait"},
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
WorkloadRefPath: "spec.workloadRef",
|
||||
@@ -148,8 +150,9 @@ var _ = BeforeSuite(func(done Done) {
|
||||
// create workload definition for 'containerizedworkload'
|
||||
wd := v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "containerizedworkloads.core.oam.dev",
|
||||
Labels: label,
|
||||
Name: "containerizedworkloads.core.oam.dev",
|
||||
Namespace: "vela-system",
|
||||
Labels: label,
|
||||
},
|
||||
Spec: v1alpha2.WorkloadDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
@@ -173,7 +176,8 @@ var _ = BeforeSuite(func(done Done) {
|
||||
// create workload definition for 'deployments'
|
||||
wdDeploy := v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "deployments.apps",
|
||||
Name: "deployments.apps",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
Spec: v1alpha2.WorkloadDefinitionSpec{
|
||||
Reference: v1alpha2.DefinitionReference{
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: bars.example.com
|
||||
namespace: vela-system
|
||||
spec:
|
||||
definitionRef:
|
||||
name: bars.example.com
|
||||
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: bars.example.com
|
||||
namespace: vela-system
|
||||
spec:
|
||||
revisionEnabled: true
|
||||
definitionRef:
|
||||
|
||||
@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: bars.example.com
|
||||
namespace: vela-system
|
||||
spec:
|
||||
definitionRef:
|
||||
name: bars.example.com
|
||||
106
test/e2e-test/testdata/rollout/clonesetDefinition.yaml
vendored
Normal file
106
test/e2e-test/testdata/rollout/clonesetDefinition.yaml
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT.
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: clonesetservice
|
||||
namespace: vela-system
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
|
||||
If workload type is skipped for any service defined in Appfile, it will be defaulted to `webservice` type."
|
||||
spec:
|
||||
definitionRef:
|
||||
name: clonesets.apps.kruise.io
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "apps.kruise.io/v1alpha1"
|
||||
kind: "CloneSet"
|
||||
metadata: labels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
spec: {
|
||||
replicas: parameter.replicas
|
||||
selector: matchLabels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
template: {
|
||||
metadata: labels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
spec: {
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
|
||||
if parameter["env"] != _|_ {
|
||||
env: parameter.env
|
||||
}
|
||||
|
||||
if context["config"] != _|_ {
|
||||
env: context.config
|
||||
}
|
||||
|
||||
ports: [{
|
||||
containerPort: parameter.port
|
||||
}]
|
||||
|
||||
if parameter["cpu"] != _|_ {
|
||||
resources: {
|
||||
limits:
|
||||
cpu: parameter.cpu
|
||||
requests:
|
||||
cpu: parameter.cpu
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
if parameter["updateStrategyType"] != _|_ {
|
||||
updateStrategy: {
|
||||
type: parameter.updateStrategyType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
|
||||
// +usage=Commands to run in the container
|
||||
cmd?: [...string]
|
||||
|
||||
// +usage=Which port do you want customer traffic sent to
|
||||
// +short=p
|
||||
port: *80 | int
|
||||
// +usage=Define arguments by using environment variables
|
||||
env?: [...{
|
||||
// +usage=Environment variable name
|
||||
name: string
|
||||
// +usage=The value of the environment variable
|
||||
value?: string
|
||||
// +usage=Specifies a source the value of this var should come from
|
||||
valueFrom?: {
|
||||
// +usage=Selects a key of a secret in the pod's namespace
|
||||
secretKeyRef: {
|
||||
// +usage=The name of the secret in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the secret to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
// +usage=Number of CPU units for the service, like `0.5` (0.5 CPU core), `1` (1 CPU core)
|
||||
cpu?: string
|
||||
// +usage=Cloneset updateStrategy, candidates are `ReCreate`/`InPlaceIfPossible`/`InPlaceOnly`
|
||||
updateStrategyType?: string
|
||||
// +usage=Number of pods in the cloneset
|
||||
replicas: *5 | int
|
||||
}
|
||||
@@ -30,6 +30,7 @@ type wdModifier func(*v1alpha2.WorkloadDefinition)
|
||||
func wdNameAndDef(n string) wdModifier {
|
||||
return func(wd *v1alpha2.WorkloadDefinition) {
|
||||
wd.ObjectMeta.Name = n
|
||||
wd.ObjectMeta.Namespace = "vela-system"
|
||||
wd.Spec.Reference = v1alpha2.DefinitionReference{
|
||||
Name: n,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user