Compare commits

...

25 Commits

Author SHA1 Message Date
Jianbo Sun
3bc014bc8c Merge pull request #1169 from legendtkl/release-0.3
fix arm64 build failed
2021-03-11 10:25:09 +08:00
Weiping Cai
bb9f5402b3 fix arm64 build failed and block our release pipeline
Signed-off-by: Weiping Cai <weiping.cai@daocloud.io>
2021-03-10 22:44:08 +08:00
Jianbo Sun
37bd952486 Merge pull request #1156 from wangyikewxgm/cp1085
cherry-pick  pr 1085 to release-0.3
2021-03-09 10:55:10 +08:00
wangyike
5c33598fe9 modify controller,webhook,api,chart
solve failed test

add compatibility test for old crd

add app ns for cli

modify compatibility test

solve compatibility problem

add testing for  GetDefinition func with cluster scope CRD

generate code for compatibility-test

move testdata generate to makefile

optimize ci pipeline for compatibility-test
2021-03-09 10:10:48 +08:00
Jianbo Sun
54e806a94c Merge pull request #1152 from yangsoon/fix-workflow
cherry-pick(#1143) delete image after e2e test
2021-03-08 11:48:58 +08:00
yangsoon
740b85c6e1 fix issue #1140 delete image after e2e test 2021-03-08 11:24:42 +08:00
Jianbo Sun
8ceb64d16c Merge pull request #1136 from wonderflow/fixgc
fix garbage collection for apply once force model
2021-03-05 10:51:21 +08:00
天元
5bf11d4142 fix revision enable workload check and prevent GC if replica>0 2021-03-04 18:05:17 +08:00
天元
0229ac775b reduce reconcile too frequency, error will requeue automatically 2021-03-04 16:00:45 +08:00
Jianbo Sun
52e3893bf3 Merge pull request #1114 from wonderflow/fix1
update CRD and fix workload not create first time
2021-03-01 17:58:11 +08:00
天元
419afd27b5 update CRD and fix workload not create first time 2021-03-01 15:05:19 +08:00
Jianbo Sun
52550bb7b5 parent override child when annotation/labels conflicts && one revision will apply once only in force mode && AC.status CRD updated (#1109)
* when annotation/labels passthrough from parent to child conflicts, the parent will override the child.
e.g. 1) AC will override its component/workload; 2) workload will override child-resource; 3) AC will override its trait

* apply once only force will block workload apply when revision not changed even ac generation updated
2021-02-28 22:38:18 -08:00
Jianbo Sun
d6f3f995cd Merge pull request #1111 from captainroy-hy/chp-1041
cherry-pick(1041&1036) fix unit test
2021-02-26 18:57:10 +08:00
roy wang
dc6ab6b8d6 fix unstable unit test
Signed-off-by: roywang <seiwy2010@gmail.com>
2021-02-26 19:18:15 +09:00
roywang
613f1f9b53 quick fix unstable unit test
Signed-off-by: roywang <seiwy2010@gmail.com>
2021-02-26 18:46:10 +09:00
Jianbo Sun
eefc72b0c6 Merge pull request #1099 from captainroy-hy/ut-apply-once
add unit test
2021-02-26 10:26:09 +08:00
Jianbo Sun
1ac66bb3e4 fix component custom revison loop infinitely create revision (#1101) 2021-02-24 23:50:45 -08:00
roywang
ac6d6d1a73 add unit test
Signed-off-by: roywang <seiwy2010@gmail.com>
2021-02-25 02:59:19 +09:00
Jianbo Sun
46f7472bd5 fix apply only once observedGeneration should mark after meet all dependency requirements && add log for apply only once (#1094) 2021-02-24 01:13:27 -08:00
Jianbo Sun
d872430b08 Merge pull request #1095 from wonderflow/cherryp
cherry-pick: remove go-header package dependency
2021-02-23 20:10:30 +08:00
天元
9d2cf16141 remove go-header package dependency 2021-02-23 19:14:57 +08:00
Jianbo Sun
8c9fab1aa1 Merge pull request #1096 from wonderflow/ci
enable CI for release-*
2021-02-23 19:12:44 +08:00
天元
e213779235 enable CI for release-* 2021-02-23 17:53:49 +08:00
Jianbo Sun
e4bf16b618 Merge pull request #1034 from oam-dev/master
merge master to release-0.3 align with v0.3.3
2021-02-07 17:49:09 +08:00
Jianbo Sun
9c64afa1f6 Merge pull request #982 from wonderflow/fix
bump #981 into release-0.3
2021-02-01 14:53:02 +08:00
92 changed files with 2711 additions and 453 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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) .

View File

@@ -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"`

View File

@@ -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

View File

@@ -17,7 +17,7 @@ spec:
listKind: ScopeDefinitionList
plural: scopedefinitions
singular: scopedefinition
scope: Cluster
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.definitionRef.name

View File

@@ -17,7 +17,7 @@ spec:
listKind: TraitDefinitionList
plural: traitdefinitions
singular: traitdefinition
scope: Cluster
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.definitionRef.name

View File

@@ -17,7 +17,7 @@ spec:
listKind: WorkloadDefinitionList
plural: workloaddefinitions
singular: workloaddefinition
scope: Cluster
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.definitionRef.name

View File

@@ -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

View File

@@ -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

View File

@@ -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: |-

View File

@@ -5,6 +5,7 @@ metadata:
annotations:
definition.oam.dev/description: "Configures replicas for your service."
name: scaler
namespace: {{.Values.systemDefinitionNamespace}}
spec:
appliesToWorkloads:
- webservice

View File

@@ -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:

View File

@@ -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."

View File

@@ -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:

View File

@@ -84,3 +84,5 @@ certificate:
secretName: webhook-server-cert
mountPath: /etc/k8s-webhook-certs
caBundle: replace-me
systemDefinitionNamespace: vela-system

View File

@@ -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,

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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: |-

View File

@@ -4,6 +4,7 @@ metadata:
annotations:
definition.oam.dev/description: "Configures replicas for your service."
name: scaler
namespace: {{.Values.systemDefinitionNamespace}}
spec:
appliesToWorkloads:
- webservice

View File

@@ -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:

View File

@@ -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."

View File

@@ -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:

View File

@@ -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

View File

@@ -21,7 +21,7 @@ spec:
listKind: ScopeDefinitionList
plural: scopedefinitions
singular: scopedefinition
scope: Cluster
scope: Namespaced
subresources: {}
validation:
openAPIV3Schema:

View File

@@ -21,7 +21,7 @@ spec:
listKind: TraitDefinitionList
plural: traitdefinitions
singular: traitdefinition
scope: Cluster
scope: Namespaced
subresources: {}
validation:
openAPIV3Schema:

View File

@@ -21,7 +21,7 @@ spec:
listKind: WorkloadDefinitionList
plural: workloaddefinitions
singular: workloaddefinition
scope: Cluster
scope: Namespaced
subresources: {}
validation:
openAPIV3Schema:

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)))
})
})

View File

@@ -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")
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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))

View File

@@ -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: |-

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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() {

View File

@@ -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{}

View File

@@ -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)

View File

@@ -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")

View File

@@ -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.

View File

@@ -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")
}

View File

@@ -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 ===================

View File

@@ -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{

View File

@@ -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.")

View File

@@ -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"
})
})

View File

@@ -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{}))

View File

@@ -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))
}

View File

@@ -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

View File

@@ -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")},
}

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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,
}

View File

@@ -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 {

View File

@@ -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())

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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
View File

@@ -0,0 +1,6 @@
package oam
// SystemDefinitonNamespace golbal value for controller and webhook systemlevel namespace
var (
SystemDefinitonNamespace string = "vela-system"
)

View File

@@ -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}})

View File

@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: ingress.test
namespace: vela-system
spec:
appliesToWorkloads:
- webservice

View File

@@ -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

View File

@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: routes.test
namespace: vela-system
labels:
usecase: forplugintest
spec:

View File

@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: webservice.testapps
namespace: vela-system
labels:
usecase: forplugintest
spec:

View File

@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: deployments.testapps
namespace: vela-system
labels:
usecase: forplugintest
spec:

View File

@@ -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")
}

View File

@@ -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{

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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:

View File

@@ -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 {

View File

@@ -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())

View 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)
}
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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{

View File

@@ -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{

View File

@@ -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{

View File

@@ -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{

View File

@@ -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

View File

@@ -2,6 +2,7 @@ apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: bars.example.com
namespace: vela-system
spec:
revisionEnabled: true
definitionRef:

View File

@@ -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

View 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
}

View File

@@ -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,
}