mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-01 09:10:43 +00:00
Compare commits
59 Commits
v1.3.2
...
v1.4.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d28fb35eb | ||
|
|
d7c6f6cc73 | ||
|
|
4162c413b3 | ||
|
|
172d41583f | ||
|
|
58edb344f6 | ||
|
|
1125f87fd7 | ||
|
|
520e67f63b | ||
|
|
7eb0002692 | ||
|
|
1237f7350e | ||
|
|
7907618a6a | ||
|
|
54e333148f | ||
|
|
c126a5b272 | ||
|
|
3eb1f53606 | ||
|
|
426b22d2e5 | ||
|
|
007f13d2ee | ||
|
|
81d479aedf | ||
|
|
25e33cdd6d | ||
|
|
900653a2da | ||
|
|
e3612ac352 | ||
|
|
62fb10625d | ||
|
|
571e154af3 | ||
|
|
2ac4ddad03 | ||
|
|
c5e1855a55 | ||
|
|
cb4a9ea1a8 | ||
|
|
21216055fb | ||
|
|
eb60d94a06 | ||
|
|
8998537dc8 | ||
|
|
4ddfe32fc4 | ||
|
|
28e2284284 | ||
|
|
62ecc70ade | ||
|
|
5857aa8790 | ||
|
|
49646ddc8e | ||
|
|
707905d877 | ||
|
|
7d3ef0595a | ||
|
|
af6dc4bda3 | ||
|
|
f44bd7c6dd | ||
|
|
eaec8348d9 | ||
|
|
2849dfc1fb | ||
|
|
d657ea4daf | ||
|
|
68500b3f17 | ||
|
|
c33eaa0609 | ||
|
|
7a0d2b552b | ||
|
|
385b2462e9 | ||
|
|
0c35753530 | ||
|
|
0e97aa2291 | ||
|
|
7fcb89906c | ||
|
|
86ef2d68e0 | ||
|
|
2e57be1022 | ||
|
|
ad01f3062a | ||
|
|
b6fac3f4d5 | ||
|
|
2eb7826070 | ||
|
|
5f7371815c | ||
|
|
d6b96fee5a | ||
|
|
85c673a574 | ||
|
|
7e6d9ccc73 | ||
|
|
e65dcf12db | ||
|
|
fd5faed71a | ||
|
|
b1823084af | ||
|
|
83fe4a160e |
38
.github/workflows/apiserver-test.yaml
vendored
38
.github/workflows/apiserver-test.yaml
vendored
@@ -6,7 +6,9 @@ on:
|
||||
- master
|
||||
- release-*
|
||||
- apiserver
|
||||
workflow_dispatch: {}
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch: { }
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
@@ -18,6 +20,8 @@ env:
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
|
||||
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -35,10 +39,36 @@ jobs:
|
||||
do_not_skip: '["workflow_dispatch", "schedule", "push"]'
|
||||
concurrent_skipping: false
|
||||
|
||||
set-k8s-matrix:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.set-k8s-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: mukunku/tag-exists-action@v1.0.0
|
||||
id: checkTag
|
||||
with:
|
||||
tag: 'v1'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- id: set-k8s-matrix
|
||||
run: |
|
||||
echo ${{ steps.checkTag.outputs.exists }}
|
||||
if [ "${{ steps.checkTag.outputs.exists }}" = "true" ]; then
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSIONS }}"
|
||||
else
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
|
||||
apiserver-unit-tests:
|
||||
runs-on: aliyun
|
||||
needs: detect-noop
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ${{ fromJson(needs.set-k8s-matrix.outputs.matrix) }}
|
||||
|
||||
steps:
|
||||
- name: Set up Go
|
||||
@@ -65,7 +95,7 @@ jobs:
|
||||
- name: Setup Kind Cluster (Worker)
|
||||
run: |
|
||||
kind delete cluster --name worker
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca --name worker
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }} --name worker
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
kind get kubeconfig --name worker --internal > /tmp/worker.kubeconfig
|
||||
@@ -74,7 +104,7 @@ jobs:
|
||||
- name: Setup Kind Cluster (Hub)
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
|
||||
37
.github/workflows/e2e-multicluster-test.yml
vendored
37
.github/workflows/e2e-multicluster-test.yml
vendored
@@ -5,6 +5,8 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch: {}
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -16,6 +18,8 @@ env:
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
|
||||
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -33,10 +37,37 @@ jobs:
|
||||
do_not_skip: '["workflow_dispatch", "schedule", "push"]'
|
||||
concurrent_skipping: false
|
||||
|
||||
set-k8s-matrix:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.set-k8s-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: mukunku/tag-exists-action@v1.0.0
|
||||
id: checkTag
|
||||
with:
|
||||
tag: 'v1'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- id: set-k8s-matrix
|
||||
run: |
|
||||
echo ${{ steps.checkTag.outputs.exists }}
|
||||
if [ "${{ steps.checkTag.outputs.exists }}" = "true" ]; then
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSIONS }}"
|
||||
else
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
|
||||
e2e-multi-cluster-tests:
|
||||
runs-on: aliyun
|
||||
needs: detect-noop
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ${{ fromJson(needs.set-k8s-matrix.outputs.matrix) }}
|
||||
|
||||
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -60,7 +91,7 @@ jobs:
|
||||
- name: Setup Kind Cluster (Worker)
|
||||
run: |
|
||||
kind delete cluster --name worker
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca --name worker
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }} --name worker
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
kind get kubeconfig --name worker --internal > /tmp/worker.kubeconfig
|
||||
@@ -69,7 +100,7 @@ jobs:
|
||||
- name: Setup Kind Cluster (Hub)
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
|
||||
33
.github/workflows/e2e-rollout-test.yml
vendored
33
.github/workflows/e2e-rollout-test.yml
vendored
@@ -5,6 +5,8 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch: {}
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -16,6 +18,8 @@ env:
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
|
||||
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -33,10 +37,35 @@ jobs:
|
||||
do_not_skip: '["workflow_dispatch", "schedule", "push"]'
|
||||
concurrent_skipping: false
|
||||
|
||||
set-k8s-matrix:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.set-k8s-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: mukunku/tag-exists-action@v1.0.0
|
||||
id: checkTag
|
||||
with:
|
||||
tag: 'v1'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- id: set-k8s-matrix
|
||||
run: |
|
||||
echo ${{ steps.checkTag.outputs.exists }}
|
||||
if [ "${{ steps.checkTag.outputs.exists }}" = "true" ]; then
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSIONS }}"
|
||||
else
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
e2e-rollout-tests:
|
||||
runs-on: aliyun
|
||||
needs: detect-noop
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ${{ fromJson(needs.set-k8s-matrix.outputs.matrix) }}
|
||||
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -60,7 +89,7 @@ jobs:
|
||||
- name: Setup Kind Cluster
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
|
||||
33
.github/workflows/e2e-test.yml
vendored
33
.github/workflows/e2e-test.yml
vendored
@@ -5,6 +5,8 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch: {}
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -16,6 +18,8 @@ env:
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
|
||||
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -33,10 +37,35 @@ jobs:
|
||||
do_not_skip: '["workflow_dispatch", "schedule", "push"]'
|
||||
concurrent_skipping: false
|
||||
|
||||
set-k8s-matrix:
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
matrix: ${{ steps.set-k8s-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: mukunku/tag-exists-action@v1.0.0
|
||||
id: checkTag
|
||||
with:
|
||||
tag: 'v1'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- id: set-k8s-matrix
|
||||
run: |
|
||||
echo ${{ steps.checkTag.outputs.exists }}
|
||||
if [ "${{ steps.checkTag.outputs.exists }}" = "true" ]; then
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSIONS }}"
|
||||
else
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
e2e-tests:
|
||||
runs-on: aliyun
|
||||
needs: detect-noop
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ${{ fromJson(needs.set-k8s-matrix.outputs.matrix) }}
|
||||
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -60,7 +89,7 @@ jobs:
|
||||
- name: Setup Kind Cluster
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
|
||||
4
.github/workflows/unit-test.yml
vendored
4
.github/workflows/unit-test.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
restore-keys: ${{ runner.os }}-pkg-
|
||||
|
||||
- name: Install ginkgo
|
||||
run: |
|
||||
run: |
|
||||
sudo apt-get install -y golang-ginkgo-dev
|
||||
|
||||
- name: Setup Kind Cluster
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
version: 3.1.0
|
||||
kubebuilderOnly: false
|
||||
kubernetesVersion: v1.21.2
|
||||
|
||||
|
||||
- name: Run Make test
|
||||
run: make test
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ Reviewers:
|
||||
- devholic
|
||||
- fourierr
|
||||
- JooKS-me
|
||||
- s4rd1nh4
|
||||
|
||||
Approvers:
|
||||
- Somefive (Multi-Cluster)
|
||||
|
||||
@@ -45,7 +45,7 @@ Full documentation is available on the [KubeVela website](https://kubevela.io/).
|
||||
|
||||
## Community
|
||||
|
||||
- Slack: [CNCF Slack](https://slack.cncf.io/) #kubevela channel (*English*)
|
||||
- Slack: [CNCF Slack kubevela channel](https://cloud-native.slack.com/archives/C01BLQ3HTJA) (*English*)
|
||||
- Gitter: [oam-dev](https://gitter.im/oam-dev/community) (*English*)
|
||||
- [DingTalk Group](https://page.dingtalk.com/wow/dingtalk/act/en-home): `23310022` (*Chinese*)
|
||||
- Wechat Group (*Chinese*): Broker wechat to add you into the user group.
|
||||
|
||||
22
apis/core.oam.dev/common/register.go
Normal file
22
apis/core.oam.dev/common/register.go
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package common
|
||||
|
||||
const (
|
||||
// Group api group name
|
||||
Group = "core.oam.dev"
|
||||
)
|
||||
@@ -345,6 +345,8 @@ type WorkflowStatus struct {
|
||||
Mode WorkflowMode `json:"mode"`
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
SuspendState string `json:"suspendState,omitempty"`
|
||||
|
||||
Suspend bool `json:"suspend"`
|
||||
Terminated bool `json:"terminated"`
|
||||
Finished bool `json:"finished"`
|
||||
@@ -496,6 +498,8 @@ const (
|
||||
PolicyResourceCreator ResourceCreatorRole = "policy"
|
||||
// WorkflowResourceCreator create the resource in workflow.
|
||||
WorkflowResourceCreator ResourceCreatorRole = "workflow"
|
||||
// DebugResourceCreator create the debug resource.
|
||||
DebugResourceCreator ResourceCreatorRole = "debug"
|
||||
)
|
||||
|
||||
// OAMObjectReference defines the object reference for an oam resource
|
||||
|
||||
@@ -33,11 +33,22 @@ type GarbageCollectPolicySpec struct {
|
||||
// outdated resources will be kept until resourcetracker be deleted manually
|
||||
KeepLegacyResource bool `json:"keepLegacyResource,omitempty"`
|
||||
|
||||
// Order defines the order of garbage collect
|
||||
Order GarbageCollectOrder `json:"order,omitempty"`
|
||||
|
||||
// Rules defines list of rules to control gc strategy at resource level
|
||||
// if one resource is controlled by multiple rules, first rule will be used
|
||||
Rules []GarbageCollectPolicyRule `json:"rules,omitempty"`
|
||||
}
|
||||
|
||||
// GarbageCollectOrder is the order of garbage collect
|
||||
type GarbageCollectOrder string
|
||||
|
||||
const (
|
||||
// OrderDependency is the order of dependency
|
||||
OrderDependency GarbageCollectOrder = "dependency"
|
||||
)
|
||||
|
||||
// GarbageCollectPolicyRule defines a single garbage-collect policy rule
|
||||
type GarbageCollectPolicyRule struct {
|
||||
Selector GarbageCollectPolicyRuleSelector `json:"selector"`
|
||||
@@ -45,12 +56,13 @@ type GarbageCollectPolicyRule struct {
|
||||
}
|
||||
|
||||
// GarbageCollectPolicyRuleSelector select the targets of the rule
|
||||
// if both traitTypes and componentTypes are specified, combination logic is OR
|
||||
// if both traitTypes, oamTypes and componentTypes are specified, combination logic is OR
|
||||
// if one resource is specified with conflict strategies, strategy as component go first.
|
||||
type GarbageCollectPolicyRuleSelector struct {
|
||||
CompNames []string `json:"componentNames"`
|
||||
CompTypes []string `json:"componentTypes"`
|
||||
TraitTypes []string `json:"traitTypes"`
|
||||
CompNames []string `json:"componentNames"`
|
||||
CompTypes []string `json:"componentTypes"`
|
||||
OAMResourceTypes []string `json:"oamTypes"`
|
||||
TraitTypes []string `json:"traitTypes"`
|
||||
}
|
||||
|
||||
// GarbageCollectStrategy the strategy for target resource to recycle
|
||||
@@ -69,10 +81,11 @@ const (
|
||||
// FindStrategy find gc strategy for target resource
|
||||
func (in GarbageCollectPolicySpec) FindStrategy(manifest *unstructured.Unstructured) *GarbageCollectStrategy {
|
||||
for _, rule := range in.Rules {
|
||||
var compName, compType, traitType string
|
||||
var compName, compType, oamType, traitType string
|
||||
if labels := manifest.GetLabels(); labels != nil {
|
||||
compName = labels[oam.LabelAppComponent]
|
||||
compType = labels[oam.WorkloadTypeLabel]
|
||||
oamType = labels[oam.LabelOAMResourceType]
|
||||
traitType = labels[oam.TraitTypeLabel]
|
||||
}
|
||||
match := func(src []string, val string) (found bool) {
|
||||
@@ -83,6 +96,7 @@ func (in GarbageCollectPolicySpec) FindStrategy(manifest *unstructured.Unstructu
|
||||
}
|
||||
if match(rule.Selector.CompNames, compName) ||
|
||||
match(rule.Selector.CompTypes, compType) ||
|
||||
match(rule.Selector.OAMResourceTypes, oamType) ||
|
||||
match(rule.Selector.TraitTypes, traitType) {
|
||||
return &rule.Strategy
|
||||
}
|
||||
|
||||
@@ -109,6 +109,18 @@ func TestGarbageCollectPolicySpec_FindStrategy(t *testing.T) {
|
||||
}},
|
||||
expectStrategy: GarbageCollectStrategyNever,
|
||||
},
|
||||
"resource type rule match": {
|
||||
rules: []GarbageCollectPolicyRule{{
|
||||
Selector: GarbageCollectPolicyRuleSelector{OAMResourceTypes: []string{"TRAIT"}},
|
||||
Strategy: GarbageCollectStrategyNever,
|
||||
}},
|
||||
input: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{oam.LabelOAMResourceType: "TRAIT"},
|
||||
},
|
||||
}},
|
||||
expectStrategy: GarbageCollectStrategyNever,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
@@ -21,6 +21,8 @@ const (
|
||||
TopologyPolicyType = "topology"
|
||||
// OverridePolicyType refers to the type of override policy
|
||||
OverridePolicyType = "override"
|
||||
// DebugPolicyType refers to the type of debug policy
|
||||
DebugPolicyType = "debug"
|
||||
)
|
||||
|
||||
// TopologyPolicySpec defines the spec of topology policy
|
||||
|
||||
@@ -19,11 +19,13 @@ package v1alpha1
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
)
|
||||
|
||||
// Package type metadata.
|
||||
const (
|
||||
Group = "core.oam.dev"
|
||||
Group = common.Group
|
||||
Version = "v1alpha1"
|
||||
)
|
||||
|
||||
|
||||
@@ -291,6 +291,11 @@ func (in *GarbageCollectPolicyRuleSelector) DeepCopyInto(out *GarbageCollectPoli
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.OAMResourceTypes != nil {
|
||||
in, out := &in.OAMResourceTypes, &out.OAMResourceTypes
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.TraitTypes != nil {
|
||||
in, out := &in.TraitTypes, &out.TraitTypes
|
||||
*out = make([]string, len(*in))
|
||||
|
||||
@@ -21,11 +21,13 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
)
|
||||
|
||||
// Package type metadata.
|
||||
const (
|
||||
Group = "core.oam.dev"
|
||||
Group = common.Group
|
||||
Version = "v1alpha2"
|
||||
)
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ type PolicyDefinitionStatus struct {
|
||||
// ConditionedStatus reflects the observed status of a resource
|
||||
condition.ConditionedStatus `json:",inline"`
|
||||
|
||||
// ConfigMapRef refer to a ConfigMap which contains OpenAPI V3 JSON schema of Component parameters.
|
||||
ConfigMapRef string `json:"configMapRef,omitempty"`
|
||||
|
||||
// LatestRevision of the component definition
|
||||
// +optional
|
||||
LatestRevision *common.Revision `json:"latestRevision,omitempty"`
|
||||
|
||||
@@ -21,11 +21,13 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
)
|
||||
|
||||
// Package type metadata.
|
||||
const (
|
||||
Group = "core.oam.dev"
|
||||
Group = common.Group
|
||||
Version = "v1beta1"
|
||||
)
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ func (in *ResourceTracker) findMangedResourceIndex(mr ManagedResource) int {
|
||||
}
|
||||
|
||||
// AddManagedResource add object to managed resources, if exists, update
|
||||
func (in *ResourceTracker) AddManagedResource(rsc client.Object, metaOnly bool) (updated bool) {
|
||||
func (in *ResourceTracker) AddManagedResource(rsc client.Object, metaOnly bool, creator common.ResourceCreatorRole) (updated bool) {
|
||||
gvk := rsc.GetObjectKind().GroupVersionKind()
|
||||
mr := ManagedResource{
|
||||
ClusterObjectReference: common.ClusterObjectReference{
|
||||
@@ -210,6 +210,9 @@ func (in *ResourceTracker) AddManagedResource(rsc client.Object, metaOnly bool)
|
||||
if !metaOnly {
|
||||
mr.Data = &runtime.RawExtension{Object: rsc}
|
||||
}
|
||||
if creator != "" {
|
||||
mr.ClusterObjectReference.Creator = creator
|
||||
}
|
||||
if idx := in.findMangedResourceIndex(mr); idx >= 0 {
|
||||
if reflect.DeepEqual(in.Spec.ManagedResources[idx], mr) {
|
||||
return false
|
||||
|
||||
@@ -156,16 +156,16 @@ func TestResourceTracker_ManagedResource(t *testing.T) {
|
||||
r := require.New(t)
|
||||
input := &ResourceTracker{}
|
||||
deploy1 := v12.Deployment{ObjectMeta: v13.ObjectMeta{Name: "deploy1"}}
|
||||
input.AddManagedResource(&deploy1, true)
|
||||
input.AddManagedResource(&deploy1, true, "")
|
||||
r.Equal(1, len(input.Spec.ManagedResources))
|
||||
cm2 := v1.ConfigMap{ObjectMeta: v13.ObjectMeta{Name: "cm2"}}
|
||||
input.AddManagedResource(&cm2, false)
|
||||
input.AddManagedResource(&cm2, false, "")
|
||||
r.Equal(2, len(input.Spec.ManagedResources))
|
||||
pod3 := v1.Pod{ObjectMeta: v13.ObjectMeta{Name: "pod3"}}
|
||||
input.AddManagedResource(&pod3, false)
|
||||
input.AddManagedResource(&pod3, false, "")
|
||||
r.Equal(3, len(input.Spec.ManagedResources))
|
||||
deploy1.Spec.Replicas = pointer.Int32(5)
|
||||
input.AddManagedResource(&deploy1, false)
|
||||
input.AddManagedResource(&deploy1, false, "")
|
||||
r.Equal(3, len(input.Spec.ManagedResources))
|
||||
input.DeleteManagedResource(&cm2, false)
|
||||
r.Equal(3, len(input.Spec.ManagedResources))
|
||||
|
||||
@@ -18,6 +18,13 @@ package types
|
||||
|
||||
import "github.com/oam-dev/kubevela/pkg/oam"
|
||||
|
||||
const (
|
||||
// KubeVelaName name of kubevela
|
||||
KubeVelaName = "kubevela"
|
||||
// VelaCoreName name of vela-core
|
||||
VelaCoreName = "vela-core"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultKubeVelaReleaseName defines the default name of KubeVela Release
|
||||
DefaultKubeVelaReleaseName = "kubevela"
|
||||
@@ -153,3 +160,8 @@ const (
|
||||
// TerrfaormComponentPrefix is the prefix of component type of terraform-xxx
|
||||
TerrfaormComponentPrefix = "terraform-"
|
||||
)
|
||||
|
||||
const (
|
||||
// ClusterGatewayAccessorGroup the group to impersonate which allows the access to the cluster-gateway
|
||||
ClusterGatewayAccessorGroup = "cluster-gateway-accessor"
|
||||
)
|
||||
|
||||
@@ -78,6 +78,22 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `healthCheck.port` | KubeVela health check port | `9440` |
|
||||
|
||||
|
||||
### KubeVela controller optimization parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `optimize.optimizeCachedGvks` | Optimize types of resources to be cached. | `nil` |
|
||||
| `optimize.resourceTrackerListOp` | Optimize ResourceTracker List Op by adding index. | `true` |
|
||||
| `optimize.controllerReconcileLoopReduction` | Optimize ApplicationController reconcile by reducing the number of loops to reconcile application. | `false` |
|
||||
| `optimize.markWithProb` | Optimize ResourceTracker GC by only run mark with probability. Side effect: outdated ResourceTracker might not be able to be removed immediately. | `0.1` |
|
||||
| `optimize.disableComponentRevision` | Optimize componentRevision by disabling the creation and gc | `false` |
|
||||
| `optimize.disableApplicationRevision` | Optimize ApplicationRevision by disabling the creation and gc. | `false` |
|
||||
| `optimize.disableWorkflowRecorder` | Optimize workflow recorder by disabling the creation and gc. | `false` |
|
||||
| `optimize.enableInMemoryWorkflowContext` | Optimize workflow by use in-memory context. | `false` |
|
||||
| `optimize.disableResourceApplyDoubleCheck` | Optimize workflow by ignoring resource double check after apply. | `false` |
|
||||
| `optimize.enableResourceTrackerDeleteOnlyTrigger` | Optimize resourcetracker by only trigger reconcile when resourcetracker is deleted. | `true` |
|
||||
|
||||
|
||||
### MultiCluster parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -106,23 +122,27 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `imagePullSecrets` | Image pull secrets | `[]` |
|
||||
| `nameOverride` | Override name | `""` |
|
||||
| `fullnameOverride` | Fullname override | `""` |
|
||||
| `serviceAccount.create` | Specifies whether a service account should be created | `true` |
|
||||
| `serviceAccount.annotations` | Annotations to add to the service account | `{}` |
|
||||
| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | `nil` |
|
||||
| `nodeSelector` | Node selector | `{}` |
|
||||
| `tolerations` | Tolerations | `[]` |
|
||||
| `affinity` | Affinity | `{}` |
|
||||
| `rbac.create` | Specifies whether a RBAC role should be created | `true` |
|
||||
| `logDebug` | Enable debug logs for development purpose | `false` |
|
||||
| `logFilePath` | If non-empty, write log files in this path | `""` |
|
||||
| `logFileMaxSize` | Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. | `1024` |
|
||||
| `kubeClient.qps` | The qps for reconcile clients, default is 50 | `50` |
|
||||
| `kubeClient.burst` | The burst for reconcile clients, default is 100 | `100` |
|
||||
| Name | Description | Value |
|
||||
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
||||
| `imagePullSecrets` | Image pull secrets | `[]` |
|
||||
| `nameOverride` | Override name | `""` |
|
||||
| `fullnameOverride` | Fullname override | `""` |
|
||||
| `serviceAccount.create` | Specifies whether a service account should be created | `true` |
|
||||
| `serviceAccount.annotations` | Annotations to add to the service account | `{}` |
|
||||
| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | `nil` |
|
||||
| `nodeSelector` | Node selector | `{}` |
|
||||
| `tolerations` | Tolerations | `[]` |
|
||||
| `affinity` | Affinity | `{}` |
|
||||
| `rbac.create` | Specifies whether a RBAC role should be created | `true` |
|
||||
| `logDebug` | Enable debug logs for development purpose | `false` |
|
||||
| `logFilePath` | If non-empty, write log files in this path | `""` |
|
||||
| `logFileMaxSize` | Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. | `1024` |
|
||||
| `kubeClient.qps` | The qps for reconcile clients, default is 50 | `50` |
|
||||
| `kubeClient.burst` | The burst for reconcile clients, default is 100 | `100` |
|
||||
| `authentication.enabled` | Enable authentication for application | `false` |
|
||||
| `authentication.withUser` | Application authentication will impersonate as the request User | `false` |
|
||||
| `authentication.defaultUser` | Application authentication will impersonate as the User if no user provided in Application | `kubevela:vela-core` |
|
||||
| `authentication.groupPattern` | Application authentication will impersonate as the request Group that matches the pattern | `kubevela:*` |
|
||||
|
||||
|
||||
## Uninstallation
|
||||
@@ -164,10 +184,4 @@ To uninstall the KubeVela helm release:
|
||||
$ helm uninstall -n vela-system kubevela
|
||||
```
|
||||
|
||||
Finally, this command will remove all the Kubernetes resources associated with KubeVela and remove this chart release.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Finally, this command will remove all the Kubernetes resources associated with KubeVela and remove this chart release.
|
||||
@@ -934,6 +934,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -2743,6 +2745,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -3386,6 +3390,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
@@ -4682,6 +4690,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
|
||||
@@ -643,6 +643,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -1146,6 +1148,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
|
||||
@@ -636,6 +636,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -244,6 +244,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
|
||||
V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -120,6 +120,32 @@ webhooks:
|
||||
- UPDATE
|
||||
resources:
|
||||
- podspecworkloads
|
||||
- clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ template "kubevela.name" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /mutating-core-oam-dev-v1beta1-applications
|
||||
{{- if .Values.admissionWebhooks.patch.enabled }}
|
||||
failurePolicy: Ignore
|
||||
{{- else }}
|
||||
failurePolicy: Fail
|
||||
{{- end }}
|
||||
name: mutating.core.oam.dev.v1beta1.applications
|
||||
admissionReviewVersions:
|
||||
- v1beta1
|
||||
- v1
|
||||
sideEffects: None
|
||||
rules:
|
||||
- apiGroups:
|
||||
- core.oam.dev
|
||||
apiVersions:
|
||||
- v1beta1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- applications
|
||||
- clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
|
||||
@@ -274,4 +274,30 @@ spec:
|
||||
runAsGroup: 2000
|
||||
runAsNonRoot: true
|
||||
runAsUser: 2000
|
||||
{{ end }}
|
||||
---
|
||||
{{ if and .Values.multicluster.enabled }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:cluster-gateway-access-role
|
||||
rules:
|
||||
- apiGroups: [ "cluster.core.oam.dev" ]
|
||||
resources: [ "clustergateways/proxy" ]
|
||||
verbs: [ "get", "list", "watch", "create", "update", "patch", "delete" ]
|
||||
{{ end }}
|
||||
---
|
||||
{{ if and .Values.multicluster.enabled }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:cluster-gateway-access-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "kubevela.fullname" . }}:cluster-gateway-access-role
|
||||
subjects:
|
||||
- kind: Group
|
||||
name: cluster-gateway-accessor
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
{{ end }}
|
||||
@@ -35,6 +35,9 @@ spec:
|
||||
if parameter.args != _|_ {
|
||||
args: parameter.args
|
||||
}
|
||||
if parameter["env"] != _|_ {
|
||||
env: parameter.env
|
||||
}
|
||||
|
||||
// +patchKey=name
|
||||
volumeMounts: [{
|
||||
@@ -61,6 +64,31 @@ spec:
|
||||
// +usage=Specify the args run in the init container
|
||||
args?: [...string]
|
||||
|
||||
// +usage=Specify the env run in the init container
|
||||
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=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef?: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=Specify the mount name of shared volume
|
||||
mountName: *"workdir" | string
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ spec:
|
||||
if parameter.args != _|_ {
|
||||
args: parameter.args
|
||||
}
|
||||
if parameter["env"] != _|_ {
|
||||
env: parameter.env
|
||||
}
|
||||
if parameter["volumes"] != _|_ {
|
||||
volumeMounts: [ for v in parameter.volumes {
|
||||
{
|
||||
@@ -35,6 +38,13 @@ spec:
|
||||
}
|
||||
}]
|
||||
}
|
||||
if parameter["livenessProbe"] != _|_ {
|
||||
livenessProbe: parameter.livenessProbe
|
||||
}
|
||||
|
||||
if parameter["readinessProbe"] != _|_ {
|
||||
readinessProbe: parameter.readinessProbe
|
||||
}
|
||||
}]
|
||||
}
|
||||
parameter: {
|
||||
@@ -50,10 +60,82 @@ spec:
|
||||
// +usage=Specify the args in the sidecar
|
||||
args?: [...string]
|
||||
|
||||
// +usage=Specify the env in the sidecar
|
||||
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=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef?: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=Specify the shared volume path
|
||||
volumes?: [...{
|
||||
name: string
|
||||
path: string
|
||||
}]
|
||||
|
||||
// +usage=Instructions for assessing whether the container is alive.
|
||||
livenessProbe?: #HealthProbe
|
||||
|
||||
// +usage=Instructions for assessing whether the container is in a suitable state to serve traffic.
|
||||
readinessProbe?: #HealthProbe
|
||||
}
|
||||
#HealthProbe: {
|
||||
|
||||
// +usage=Instructions for assessing container health by executing a command. Either this attribute or the httpGet attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the httpGet attribute and the tcpSocket attribute.
|
||||
exec?: {
|
||||
// +usage=A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures.
|
||||
command: [...string]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by executing an HTTP GET request. Either this attribute or the exec attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the tcpSocket attribute.
|
||||
httpGet?: {
|
||||
// +usage=The endpoint, relative to the port, to which the HTTP GET request should be directed.
|
||||
path: string
|
||||
// +usage=The TCP socket within the container to which the HTTP GET request should be directed.
|
||||
port: int
|
||||
httpHeaders?: [...{
|
||||
name: string
|
||||
value: string
|
||||
}]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by probing a TCP socket. Either this attribute or the exec attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the httpGet attribute.
|
||||
tcpSocket?: {
|
||||
// +usage=The TCP socket within the container that should be probed to assess container health.
|
||||
port: int
|
||||
}
|
||||
|
||||
// +usage=Number of seconds after the container is started before the first probe is initiated.
|
||||
initialDelaySeconds: *0 | int
|
||||
|
||||
// +usage=How often, in seconds, to execute the probe.
|
||||
periodSeconds: *10 | int
|
||||
|
||||
// +usage=Number of seconds after which the probe times out.
|
||||
timeoutSeconds: *1 | int
|
||||
|
||||
// +usage=Minimum consecutive successes for the probe to be considered successful after having failed.
|
||||
successThreshold: *1 | int
|
||||
|
||||
// +usage=Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).
|
||||
failureThreshold: *3 | int
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,17 @@ spec:
|
||||
}
|
||||
},
|
||||
] | []
|
||||
configMountToEnvsList: *[
|
||||
for v in parameter.configMap if v.mountToEnvs != _|_ for k in v.mountToEnvs {
|
||||
{
|
||||
name: k.envName
|
||||
valueFrom: configMapKeyRef: {
|
||||
name: v.name
|
||||
key: k.configMapKey
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
secretVolumeMountsList: *[
|
||||
for v in parameter.secret if v.mountPath != _|_ {
|
||||
{
|
||||
@@ -106,6 +117,17 @@ spec:
|
||||
}
|
||||
},
|
||||
] | []
|
||||
secretMountToEnvsList: *[
|
||||
for v in parameter.secret if v.mountToEnvs != _|_ for k in v.mountToEnvs {
|
||||
{
|
||||
name: k.envName
|
||||
valueFrom: secretKeyRef: {
|
||||
name: v.name
|
||||
key: k.secretKey
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
emptyDirVolumeMountsList: *[
|
||||
for v in parameter.emptyDir {
|
||||
{
|
||||
@@ -128,7 +150,7 @@ spec:
|
||||
|
||||
containers: [{
|
||||
// +patchKey=name
|
||||
env: configMapEnvMountsList + secretEnvMountsList
|
||||
env: configMapEnvMountsList + secretEnvMountsList + configMountToEnvsList + secretMountToEnvsList
|
||||
// +patchKey=name
|
||||
volumeDevices: volumeDevicesList
|
||||
// +patchKey=name
|
||||
@@ -248,6 +270,10 @@ spec:
|
||||
envName: string
|
||||
configMapKey: string
|
||||
}
|
||||
mountToEnvs?: [...{
|
||||
envName: string
|
||||
configMapKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
@@ -267,6 +293,10 @@ spec:
|
||||
envName: string
|
||||
secretKey: string
|
||||
}
|
||||
mountToEnvs?: [...{
|
||||
envName: string
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
|
||||
@@ -11,6 +11,8 @@ spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
// no parameters
|
||||
parameter: {}
|
||||
parameter: {
|
||||
// +usage=Specify the wait duration time to resume workflow such as "30s", "1min" or "2m15s"
|
||||
duration?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
- kind: Group
|
||||
name: core.oam.dev
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
# permissions to do leader election.
|
||||
@@ -121,6 +124,36 @@ spec:
|
||||
- "--webhook-port={{ .Values.webhookService.port }}"
|
||||
- "--webhook-cert-dir={{ .Values.admissionWebhooks.certificate.mountPath }}"
|
||||
{{ end }}
|
||||
{{ if ne .Values.optimize.cachedGvks "" }}
|
||||
- "--optimize-cached-gvks={{ .Values.optimize.cachedGvks }}"
|
||||
{{ end }}
|
||||
{{ if not .Values.optimize.resourceTrackerListOp }}
|
||||
- "--optimize-resource-tracker-list-op=false"
|
||||
{{ end }}
|
||||
{{ if .Values.optimize.controllerReconcileLoopReduction }}
|
||||
- "--optimize-controller-reconcile-loop-reduction"
|
||||
{{ end }}
|
||||
{{ if .Values.optimize.markWithProb }}
|
||||
- "--optimize-mark-with-prob={{ .Values.optimize.markWithProb }}"
|
||||
{{ end }}
|
||||
{{ if .Values.optimize.disableComponentRevision }}
|
||||
- "--optimize-disable-component-revision"
|
||||
{{ end }}
|
||||
{{ if .Values.optimize.disableApplicationRevision }}
|
||||
- "--optimize-disable-application-revision"
|
||||
{{ end }}
|
||||
{{ if .Values.optimize.disableWorkflowRecorder }}
|
||||
- "--optimize-disable-workflow-recorder"
|
||||
{{ end }}
|
||||
{{ if .Values.optimize.enableInMemoryWorkflowContext }}
|
||||
- "--optimize-enable-in-memory-workflow-context"
|
||||
{{ end }}
|
||||
{{ if .Values.optimize.disableResourceApplyDoubleCheck }}
|
||||
- "--optimize-disable-resource-apply-double-check"
|
||||
{{ end }}
|
||||
{{ if not .Values.optimize.enableResourceTrackerDeleteOnlyTrigger }}
|
||||
- "--optimize-enable-resource-tracker-delete-only-trigger=false"
|
||||
{{ end }}
|
||||
- "--health-addr=:{{ .Values.healthCheck.port }}"
|
||||
{{ if ne .Values.disableCaps "" }}
|
||||
- "--disable-caps={{ .Values.disableCaps }}"
|
||||
@@ -139,6 +172,14 @@ spec:
|
||||
- "--max-workflow-wait-backoff-time={{ .Values.workflow.backoff.maxTime.waitState }}"
|
||||
- "--max-workflow-failed-backoff-time={{ .Values.workflow.backoff.maxTime.failedState }}"
|
||||
- "--max-workflow-step-error-retry-times={{ .Values.workflow.step.errorRetryTimes }}"
|
||||
- "--feature-gates=AuthenticateApplication={{- .Values.authentication.enabled | toString -}}"
|
||||
{{ if .Values.authentication.enabled }}
|
||||
{{ if .Values.authentication.withUser }}
|
||||
- "--authentication-with-user"
|
||||
{{ end }}
|
||||
- "--authentication-default-user={{ .Values.authentication.defaultUser }}"
|
||||
- "--authentication-group-pattern={{ .Values.authentication.groupPattern }}"
|
||||
{{ end }}
|
||||
image: {{ .Values.imageRegistry }}{{ .Values.image.repository }}:{{ .Values.image.tag }}
|
||||
imagePullPolicy: {{ quote .Values.image.pullPolicy }}
|
||||
resources:
|
||||
@@ -186,4 +227,4 @@ spec:
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -84,6 +84,28 @@ webhookService:
|
||||
healthCheck:
|
||||
port: 9440
|
||||
|
||||
## @section KubeVela controller optimization parameters
|
||||
##@param optimize.optimizeCachedGvks Optimize types of resources to be cached.
|
||||
##@param optimize.resourceTrackerListOp Optimize ResourceTracker List Op by adding index.
|
||||
##@param optimize.controllerReconcileLoopReduction Optimize ApplicationController reconcile by reducing the number of loops to reconcile application.
|
||||
##@param optimize.markWithProb Optimize ResourceTracker GC by only run mark with probability. Side effect: outdated ResourceTracker might not be able to be removed immediately.
|
||||
##@param optimize.disableComponentRevision Optimize componentRevision by disabling the creation and gc
|
||||
##@param optimize.disableApplicationRevision Optimize ApplicationRevision by disabling the creation and gc.
|
||||
##@param optimize.disableWorkflowRecorder Optimize workflow recorder by disabling the creation and gc.
|
||||
##@param optimize.enableInMemoryWorkflowContext Optimize workflow by use in-memory context.
|
||||
##@param optimize.disableResourceApplyDoubleCheck Optimize workflow by ignoring resource double check after apply.
|
||||
##@param optimize.enableResourceTrackerDeleteOnlyTrigger Optimize resourcetracker by only trigger reconcile when resourcetracker is deleted.
|
||||
optimize:
|
||||
optimizeCachedGvks:
|
||||
resourceTrackerListOp: true
|
||||
controllerReconcileLoopReduction: false
|
||||
markWithProb: 0.1
|
||||
disableComponentRevision: false
|
||||
disableApplicationRevision: false
|
||||
disableWorkflowRecorder: false
|
||||
enableInMemoryWorkflowContext: false
|
||||
disableResourceApplyDoubleCheck: false
|
||||
enableResourceTrackerDeleteOnlyTrigger: true
|
||||
|
||||
## @section MultiCluster parameters
|
||||
|
||||
@@ -210,3 +232,13 @@ admissionWebhooks:
|
||||
kubeClient:
|
||||
qps: 50
|
||||
burst: 100
|
||||
|
||||
## @param authentication.enabled Enable authentication for application
|
||||
## @param authentication.withUser Application authentication will impersonate as the request User
|
||||
## @param authentication.defaultUser Application authentication will impersonate as the User if no user provided in Application
|
||||
## @param authentication.groupPattern Application authentication will impersonate as the request Group that matches the pattern
|
||||
authentication:
|
||||
enabled: false
|
||||
withUser: false
|
||||
defaultUser: kubevela:vela-core
|
||||
groupPattern: kubevela:*
|
||||
|
||||
@@ -125,22 +125,26 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `imagePullSecrets` | Image pull secrets | `[]` |
|
||||
| `nameOverride` | Override name | `""` |
|
||||
| `fullnameOverride` | Fullname override | `""` |
|
||||
| `serviceAccount.create` | Specifies whether a service account should be created | `true` |
|
||||
| `serviceAccount.annotations` | Annotations to add to the service account | `{}` |
|
||||
| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | `nil` |
|
||||
| `nodeSelector` | Node selector | `{}` |
|
||||
| `tolerations` | Tolerations | `[]` |
|
||||
| `affinity` | Affinity | `{}` |
|
||||
| `rbac.create` | Specifies whether a RBAC role should be created | `true` |
|
||||
| `logDebug` | Enable debug logs for development purpose | `false` |
|
||||
| `logFilePath` | If non-empty, write log files in this path | `""` |
|
||||
| `logFileMaxSize` | Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. | `1024` |
|
||||
| `kubeClient.qps` | The qps for reconcile clients, default is 50 | `50` |
|
||||
| `kubeClient.burst` | The burst for reconcile clients, default is 100 | `100` |
|
||||
| Name | Description | Value |
|
||||
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
||||
| `imagePullSecrets` | Image pull secrets | `[]` |
|
||||
| `nameOverride` | Override name | `""` |
|
||||
| `fullnameOverride` | Fullname override | `""` |
|
||||
| `serviceAccount.create` | Specifies whether a service account should be created | `true` |
|
||||
| `serviceAccount.annotations` | Annotations to add to the service account | `{}` |
|
||||
| `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | `nil` |
|
||||
| `nodeSelector` | Node selector | `{}` |
|
||||
| `tolerations` | Tolerations | `[]` |
|
||||
| `affinity` | Affinity | `{}` |
|
||||
| `rbac.create` | Specifies whether a RBAC role should be created | `true` |
|
||||
| `logDebug` | Enable debug logs for development purpose | `false` |
|
||||
| `logFilePath` | If non-empty, write log files in this path | `""` |
|
||||
| `logFileMaxSize` | Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. | `1024` |
|
||||
| `kubeClient.qps` | The qps for reconcile clients, default is 50 | `50` |
|
||||
| `kubeClient.burst` | The burst for reconcile clients, default is 100 | `100` |
|
||||
| `authentication.enabled` | Enable authentication for application | `false` |
|
||||
| `authentication.withUser` | Application authentication will impersonate as the request User | `false` |
|
||||
| `authentication.defaultUser` | Application authentication will impersonate as the User if no user provided in Application | `kubevela:vela-core` |
|
||||
| `authentication.groupPattern` | Application authentication will impersonate as the request Group that matches the pattern | `kubevela:*` |
|
||||
|
||||
|
||||
|
||||
@@ -934,6 +934,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -2743,6 +2745,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -3386,6 +3390,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
@@ -4682,6 +4690,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
|
||||
@@ -643,6 +643,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -1146,6 +1148,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
|
||||
@@ -636,6 +636,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -244,6 +244,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
|
||||
V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -92,6 +92,32 @@ webhooks:
|
||||
- UPDATE
|
||||
resources:
|
||||
- podspecworkloads
|
||||
- clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ template "kubevela.name" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /mutating-core-oam-dev-v1beta1-applications
|
||||
{{- if .Values.admissionWebhooks.patch.enabled }}
|
||||
failurePolicy: Ignore
|
||||
{{- else }}
|
||||
failurePolicy: Fail
|
||||
{{- end }}
|
||||
name: mutating.core.oam.dev.v1beta1.applications
|
||||
admissionReviewVersions:
|
||||
- v1beta1
|
||||
- v1
|
||||
sideEffects: None
|
||||
rules:
|
||||
- apiGroups:
|
||||
- core.oam.dev
|
||||
apiVersions:
|
||||
- v1beta1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- applications
|
||||
- clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
|
||||
@@ -188,4 +188,30 @@ spec:
|
||||
runAsGroup: 2000
|
||||
runAsNonRoot: true
|
||||
runAsUser: 2000
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
---
|
||||
{{ if and .Values.multicluster.enabled }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:cluster-gateway-access-role
|
||||
rules:
|
||||
- apiGroups: [ "cluster.core.oam.dev" ]
|
||||
resources: [ "clustergateways/proxy" ]
|
||||
verbs: [ "get", "list", "watch", "create", "update", "patch", "delete" ]
|
||||
{{ end }}
|
||||
---
|
||||
{{ if and .Values.multicluster.enabled }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:cluster-gateway-access-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "kubevela.fullname" . }}:cluster-gateway-access-role
|
||||
subjects:
|
||||
- kind: Group
|
||||
name: cluster-gateway-accessor
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
{{ end }}
|
||||
@@ -35,6 +35,9 @@ spec:
|
||||
if parameter.args != _|_ {
|
||||
args: parameter.args
|
||||
}
|
||||
if parameter["env"] != _|_ {
|
||||
env: parameter.env
|
||||
}
|
||||
|
||||
// +patchKey=name
|
||||
volumeMounts: [{
|
||||
@@ -61,6 +64,31 @@ spec:
|
||||
// +usage=Specify the args run in the init container
|
||||
args?: [...string]
|
||||
|
||||
// +usage=Specify the env run in the init container
|
||||
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=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef?: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=Specify the mount name of shared volume
|
||||
mountName: *"workdir" | string
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ spec:
|
||||
if parameter.args != _|_ {
|
||||
args: parameter.args
|
||||
}
|
||||
if parameter["env"] != _|_ {
|
||||
env: parameter.env
|
||||
}
|
||||
if parameter["volumes"] != _|_ {
|
||||
volumeMounts: [ for v in parameter.volumes {
|
||||
{
|
||||
@@ -35,6 +38,13 @@ spec:
|
||||
}
|
||||
}]
|
||||
}
|
||||
if parameter["livenessProbe"] != _|_ {
|
||||
livenessProbe: parameter.livenessProbe
|
||||
}
|
||||
|
||||
if parameter["readinessProbe"] != _|_ {
|
||||
readinessProbe: parameter.readinessProbe
|
||||
}
|
||||
}]
|
||||
}
|
||||
parameter: {
|
||||
@@ -50,10 +60,82 @@ spec:
|
||||
// +usage=Specify the args in the sidecar
|
||||
args?: [...string]
|
||||
|
||||
// +usage=Specify the env in the sidecar
|
||||
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=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef?: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=Specify the shared volume path
|
||||
volumes?: [...{
|
||||
name: string
|
||||
path: string
|
||||
}]
|
||||
|
||||
// +usage=Instructions for assessing whether the container is alive.
|
||||
livenessProbe?: #HealthProbe
|
||||
|
||||
// +usage=Instructions for assessing whether the container is in a suitable state to serve traffic.
|
||||
readinessProbe?: #HealthProbe
|
||||
}
|
||||
#HealthProbe: {
|
||||
|
||||
// +usage=Instructions for assessing container health by executing a command. Either this attribute or the httpGet attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the httpGet attribute and the tcpSocket attribute.
|
||||
exec?: {
|
||||
// +usage=A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures.
|
||||
command: [...string]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by executing an HTTP GET request. Either this attribute or the exec attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the tcpSocket attribute.
|
||||
httpGet?: {
|
||||
// +usage=The endpoint, relative to the port, to which the HTTP GET request should be directed.
|
||||
path: string
|
||||
// +usage=The TCP socket within the container to which the HTTP GET request should be directed.
|
||||
port: int
|
||||
httpHeaders?: [...{
|
||||
name: string
|
||||
value: string
|
||||
}]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by probing a TCP socket. Either this attribute or the exec attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the httpGet attribute.
|
||||
tcpSocket?: {
|
||||
// +usage=The TCP socket within the container that should be probed to assess container health.
|
||||
port: int
|
||||
}
|
||||
|
||||
// +usage=Number of seconds after the container is started before the first probe is initiated.
|
||||
initialDelaySeconds: *0 | int
|
||||
|
||||
// +usage=How often, in seconds, to execute the probe.
|
||||
periodSeconds: *10 | int
|
||||
|
||||
// +usage=Number of seconds after which the probe times out.
|
||||
timeoutSeconds: *1 | int
|
||||
|
||||
// +usage=Minimum consecutive successes for the probe to be considered successful after having failed.
|
||||
successThreshold: *1 | int
|
||||
|
||||
// +usage=Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).
|
||||
failureThreshold: *3 | int
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,17 @@ spec:
|
||||
}
|
||||
},
|
||||
] | []
|
||||
configMountToEnvsList: *[
|
||||
for v in parameter.configMap if v.mountToEnvs != _|_ for k in v.mountToEnvs {
|
||||
{
|
||||
name: k.envName
|
||||
valueFrom: configMapKeyRef: {
|
||||
name: v.name
|
||||
key: k.configMapKey
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
secretVolumeMountsList: *[
|
||||
for v in parameter.secret if v.mountPath != _|_ {
|
||||
{
|
||||
@@ -106,6 +117,17 @@ spec:
|
||||
}
|
||||
},
|
||||
] | []
|
||||
secretMountToEnvsList: *[
|
||||
for v in parameter.secret if v.mountToEnvs != _|_ for k in v.mountToEnvs {
|
||||
{
|
||||
name: k.envName
|
||||
valueFrom: secretKeyRef: {
|
||||
name: v.name
|
||||
key: k.secretKey
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
emptyDirVolumeMountsList: *[
|
||||
for v in parameter.emptyDir {
|
||||
{
|
||||
@@ -128,7 +150,7 @@ spec:
|
||||
|
||||
containers: [{
|
||||
// +patchKey=name
|
||||
env: configMapEnvMountsList + secretEnvMountsList
|
||||
env: configMapEnvMountsList + secretEnvMountsList + configMountToEnvsList + secretMountToEnvsList
|
||||
// +patchKey=name
|
||||
volumeDevices: volumeDevicesList
|
||||
// +patchKey=name
|
||||
@@ -248,6 +270,10 @@ spec:
|
||||
envName: string
|
||||
configMapKey: string
|
||||
}
|
||||
mountToEnvs?: [...{
|
||||
envName: string
|
||||
configMapKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
@@ -267,6 +293,10 @@ spec:
|
||||
envName: string
|
||||
secretKey: string
|
||||
}
|
||||
mountToEnvs?: [...{
|
||||
envName: string
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
|
||||
@@ -11,6 +11,8 @@ spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
// no parameters
|
||||
parameter: {}
|
||||
parameter: {
|
||||
// +usage=Specify the wait duration time to resume workflow such as "30s", "1min" or "2m15s"
|
||||
duration?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
- kind: Group
|
||||
name: core.oam.dev
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
---
|
||||
# permissions to do leader election.
|
||||
@@ -142,6 +145,14 @@ spec:
|
||||
- "--max-workflow-wait-backoff-time={{ .Values.workflow.backoff.maxTime.waitState }}"
|
||||
- "--max-workflow-failed-backoff-time={{ .Values.workflow.backoff.maxTime.failedState }}"
|
||||
- "--max-workflow-step-error-retry-times={{ .Values.workflow.step.errorRetryTimes }}"
|
||||
- "--feature-gates=AuthenticateApplication={{- .Values.authentication.enabled | toString -}}"
|
||||
{{ if .Values.authentication.enabled }}
|
||||
{{ if .Values.authentication.withUser }}
|
||||
- "--authentication-with-user"
|
||||
{{ end }}
|
||||
- "--authentication-default-user={{ .Values.authentication.defaultUser }}"
|
||||
- "--authentication-group-pattern={{ .Values.authentication.groupPattern }}"
|
||||
{{ end }}
|
||||
image: {{ .Values.imageRegistry }}{{ .Values.image.repository }}:{{ .Values.image.tag }}
|
||||
imagePullPolicy: {{ quote .Values.image.pullPolicy }}
|
||||
resources:
|
||||
|
||||
@@ -215,3 +215,13 @@ admissionWebhooks:
|
||||
kubeClient:
|
||||
qps: 50
|
||||
burst: 100
|
||||
|
||||
## @param authentication.enabled Enable authentication for application
|
||||
## @param authentication.withUser Application authentication will impersonate as the request User
|
||||
## @param authentication.defaultUser Application authentication will impersonate as the User if no user provided in Application
|
||||
## @param authentication.groupPattern Application authentication will impersonate as the request Group that matches the pattern
|
||||
authentication:
|
||||
enabled: false
|
||||
withUser: false
|
||||
defaultUser: kubevela:vela-core
|
||||
groupPattern: kubevela:*
|
||||
|
||||
@@ -46,6 +46,7 @@ func main() {
|
||||
flag.StringVar(&s.restCfg.LeaderConfig.LockName, "lock-name", "apiserver-lock", "the lease lock resource name")
|
||||
flag.DurationVar(&s.restCfg.LeaderConfig.Duration, "duration", time.Second*5, "the lease lock resource name")
|
||||
flag.DurationVar(&s.restCfg.AddonCacheTime, "addon-cache-duration", time.Minute*10, "how long between two addon cache operation")
|
||||
flag.BoolVar(&s.restCfg.DisableStatisticCronJob, "disable-statistic-cronJob", false, "close the system statistic info calculating cronJob")
|
||||
flag.Parse()
|
||||
|
||||
if len(os.Args) > 2 && os.Args[1] == "build-swagger" {
|
||||
|
||||
@@ -36,6 +36,8 @@ import (
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
|
||||
apicommon "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/auth"
|
||||
ctrlClient "github.com/oam-dev/kubevela/pkg/client"
|
||||
standardcontroller "github.com/oam-dev/kubevela/pkg/controller"
|
||||
@@ -50,6 +52,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcekeeper"
|
||||
pkgutils "github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/system"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
@@ -59,10 +62,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
const (
|
||||
kubevelaName = "kubevela"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = common.Scheme
|
||||
waitSecretTimeout = 90 * time.Second
|
||||
@@ -202,10 +201,22 @@ func main() {
|
||||
klog.InfoS("Vela-Core init", "definition namespace", oam.SystemDefinitonNamespace)
|
||||
|
||||
restConfig := ctrl.GetConfigOrDie()
|
||||
restConfig.UserAgent = kubevelaName + "/" + version.GitRevision
|
||||
restConfig.UserAgent = types.KubeVelaName + "/" + version.GitRevision
|
||||
restConfig.QPS = float32(qps)
|
||||
restConfig.Burst = burst
|
||||
restConfig.Wrap(auth.NewImpersonatingRoundTripper)
|
||||
restConfig.Impersonate.UserName = types.VelaCoreName
|
||||
if sub := pkgutils.GetServiceAccountSubjectFromConfig(restConfig); sub != "" {
|
||||
restConfig.Impersonate.UserName = sub
|
||||
}
|
||||
restConfig.Impersonate.Groups = []string{apicommon.Group}
|
||||
klog.InfoS("Kubernetes Config Loaded",
|
||||
"UserAgent", restConfig.UserAgent,
|
||||
"QPS", restConfig.QPS,
|
||||
"Burst", restConfig.Burst,
|
||||
"Impersonate-User", restConfig.Impersonate.UserName,
|
||||
"Impersonate-Group", strings.Join(restConfig.Impersonate.Groups, ","),
|
||||
)
|
||||
|
||||
// wrapper the round tripper by multi cluster rewriter
|
||||
if enableClusterGateway {
|
||||
@@ -225,7 +236,7 @@ func main() {
|
||||
}
|
||||
ctrl.SetLogger(klogr.New())
|
||||
|
||||
leaderElectionID := util.GenerateLeaderElectionID(kubevelaName, controllerArgs.IgnoreAppWithoutControllerRequirement)
|
||||
leaderElectionID := util.GenerateLeaderElectionID(types.KubeVelaName, controllerArgs.IgnoreAppWithoutControllerRequirement)
|
||||
mgr, err := ctrl.NewManager(restConfig, ctrl.Options{
|
||||
Scheme: scheme,
|
||||
MetricsBindAddress: metricsAddr,
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
By leveraging the garbage-collect policy, users can persist some resources, which skip the normal garbage-collect process when application is updated.
|
||||
|
||||
### traitTypes
|
||||
|
||||
Take the following app as an example, in the garbage-collect policy, a rule is added which marks all the resources created by the `expose` trait to use the `onAppDelete` strategy. This will keep those services until application is deleted.
|
||||
```shell
|
||||
$ cat <<EOF | kubectl apply -f -
|
||||
@@ -78,6 +80,8 @@ hello-world ClusterIP 10.96.160.208 <none> 8000/TCP 5m56s
|
||||
hello-world-new ClusterIP 10.96.20.4 <none> 8000/TCP 13s
|
||||
```
|
||||
|
||||
### componentTypes
|
||||
|
||||
Users can also keep component if they are deploying job-like components. Resources dispatched by `job-like-component` type component will be kept after application is deleted.
|
||||
|
||||
```yaml
|
||||
@@ -100,6 +104,8 @@ spec:
|
||||
strategy: never
|
||||
```
|
||||
|
||||
### componentNames
|
||||
|
||||
A more straightforward way is to specify `compNames` to match specified components.
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
@@ -124,3 +130,74 @@ spec:
|
||||
- example-addon-namespace
|
||||
strategy: never
|
||||
```
|
||||
|
||||
### oamTypes
|
||||
|
||||
Users can also persist resources using `oamTypes`, where the values of `oamTypes` can be `TRAIT` and `WORKLOAD`.
|
||||
|
||||
```shell
|
||||
$ cat <<EOF | kubectl apply -f -
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: garbage-collect-app
|
||||
spec:
|
||||
components:
|
||||
- name: hello-world
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
traits:
|
||||
- type: expose
|
||||
properties:
|
||||
port: [8000]
|
||||
policies:
|
||||
- name: garbage-collect
|
||||
type: garbage-collect
|
||||
properties:
|
||||
rules:
|
||||
- selector:
|
||||
oamTypes:
|
||||
- TRAIT
|
||||
strategy: onAppDelete
|
||||
EOF
|
||||
```
|
||||
|
||||
And then, let's modify the component name.
|
||||
|
||||
```shell
|
||||
$ cat <<EOF | kubectl apply -f -
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: garbage-collect-app
|
||||
spec:
|
||||
components:
|
||||
- name: hello-world-new
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
traits:
|
||||
- type: expose
|
||||
properties:
|
||||
port: [8000]
|
||||
policies:
|
||||
- name: garbage-collect
|
||||
type: garbage-collect
|
||||
properties:
|
||||
rules:
|
||||
- selector:
|
||||
oamTypes:
|
||||
- TRAIT
|
||||
strategy: onAppDelete
|
||||
EOF
|
||||
```
|
||||
|
||||
List the service in cluster, you will find:
|
||||
|
||||
```shell
|
||||
$ kubectl get service
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
hello-world ClusterIP 10.96.31.209 <none> 8000/TCP 31s
|
||||
hello-world-new ClusterIP 10.96.17.103 <none> 8000/TCP 5s
|
||||
```
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# How to garbage collect resources in the order of dependency
|
||||
|
||||
If you want to garbage collect resources in the order of dependency, you can add `order: dependency` in the `garbage-collect` policy.
|
||||
|
||||
> Notice that this order policy is only valid for the resources that are created in the components.
|
||||
|
||||
In the following example, component `test1` depends on `test2`, and `test2` need the output from `test3`.
|
||||
|
||||
So the order of deployment is: `test3 -> test2 -> test1`.
|
||||
|
||||
When we add `order: dependency` in `garbage-collect` policy and delete the application, the order of garbage collect is: `test3 -> test2 -> test1`.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: gc-dependency
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: test1
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
dependsOn:
|
||||
- "test2"
|
||||
- name: test2
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
inputs:
|
||||
- from: test3-output
|
||||
parameterKey: test
|
||||
- name: test3
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
outputs:
|
||||
- name: test3-output
|
||||
valueFrom: output.metadata.name
|
||||
|
||||
policies:
|
||||
- name: gc-dependency
|
||||
type: garbage-collect
|
||||
properties:
|
||||
order: dependency
|
||||
```
|
||||
@@ -32,6 +32,9 @@ spec:
|
||||
mountToEnv:
|
||||
envName: TEST_ENV
|
||||
configMapKey: key1
|
||||
mountToEnvs:
|
||||
- envName: TEST_CM_ENV
|
||||
configMapKey: key2
|
||||
data:
|
||||
key1: value1
|
||||
key2: value2
|
||||
@@ -49,9 +52,15 @@ spec:
|
||||
mountToEnv:
|
||||
envName: TEST_SECRET
|
||||
secretKey: key1
|
||||
mountToEnvs:
|
||||
- envName: TEST_SECRET_ENV_2
|
||||
secretKey: key2
|
||||
- envName: TEST_SECRET_ENV_3
|
||||
secretKey: key3
|
||||
data:
|
||||
key1: dmFsdWUx
|
||||
key2: dmFsdWUy
|
||||
key3: dmFsdWUz
|
||||
emptyDir:
|
||||
- name: test1
|
||||
mountPath: /test/mount/emptydir
|
||||
|
||||
34
go.mod
34
go.mod
@@ -5,6 +5,7 @@ go 1.17
|
||||
require (
|
||||
cuelang.org/go v0.2.2
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1
|
||||
github.com/FogDong/uitable v0.0.5
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8
|
||||
github.com/agiledragon/gomonkey/v2 v2.4.0
|
||||
github.com/alibabacloud-go/cs-20151215/v2 v2.4.5
|
||||
@@ -64,10 +65,9 @@ require (
|
||||
go.mongodb.org/mongo-driver v1.5.1
|
||||
go.uber.org/zap v1.18.1
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/tools v0.1.6 // indirect
|
||||
golang.org/x/tools v0.1.7 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
@@ -97,7 +97,17 @@ require (
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
require github.com/robfig/cron/v3 v3.0.1
|
||||
require (
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/xanzy/go-gitlab v0.60.0
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.81.0 // indirect
|
||||
@@ -142,8 +152,7 @@ require (
|
||||
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
|
||||
github.com/deislabs/oras v0.11.1 // indirect
|
||||
github.com/docker/cli v20.10.5+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.0-beta.1+incompatible // indirect
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible // indirect
|
||||
github.com/docker/docker v20.10.14+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.3 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 // indirect
|
||||
@@ -154,7 +163,7 @@ require (
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.1.0 // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/fatih/camelcase v1.0.0 // indirect
|
||||
github.com/fatih/camelcase v1.0.0
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-logr/zapr v0.4.0 // indirect
|
||||
@@ -171,7 +180,7 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
@@ -186,7 +195,6 @@ require (
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.11.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/pty v1.1.8 // indirect
|
||||
@@ -224,6 +232,7 @@ require (
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 // indirect
|
||||
github.com/russross/blackfriday v1.5.2 // indirect
|
||||
@@ -237,7 +246,6 @@ require (
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tjfoc/gmsm v1.3.2 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
@@ -252,16 +260,14 @@ require (
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/grpc v1.38.0 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
gopkg.in/gorp.v1 v1.7.2 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
|
||||
37
go.sum
37
go.sum
@@ -118,6 +118,8 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20O
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/FogDong/uitable v0.0.5 h1:1bJo/uvhGUC6i8JPHZCr8XKMHiDExE7mQkOCmDl0ryQ=
|
||||
github.com/FogDong/uitable v0.0.5/go.mod h1:1yEaP13SkkBUj3HvqKIUWnsb42XigyZbNle84mc5kLM=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
@@ -409,8 +411,8 @@ github.com/docker/cli v20.10.9+incompatible h1:OJ7YkwQA+k2Oi51lmCojpjiygKpi76P7b
|
||||
github.com/docker/cli v20.10.9+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.0-beta.1+incompatible h1:9MjVa+OTMHm4C0kKZB68jPlDM9Cg75ta4i46Gxxxn8o=
|
||||
github.com/docker/distribution v2.8.0-beta.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
|
||||
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
@@ -781,8 +783,9 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
|
||||
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -895,8 +898,12 @@ github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPA
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
|
||||
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 h1:F1fdYblUEsxKiailtkhCCG2g4bipEgaHiDc8vffNpD4=
|
||||
github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
@@ -906,6 +913,9 @@ github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
@@ -1620,6 +1630,8 @@ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq
|
||||
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/wonderflow/cert-manager-api v1.0.3 h1:xQQMkJNQ12oYyy00jOQUlSKgdraApaURxv3PHFdVTfA=
|
||||
github.com/wonderflow/cert-manager-api v1.0.3/go.mod h1:1Se7MSg11/eNYlo4fWv6vOM55/jTBMOzg2DN1kVFiSc=
|
||||
github.com/xanzy/go-gitlab v0.60.0 h1:HaIlc14k4t9eJjAhY0Gmq2fBHgKd1MthBn3+vzDtsbA=
|
||||
github.com/xanzy/go-gitlab v0.60.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
|
||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||
@@ -1910,9 +1922,12 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM=
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1924,8 +1939,9 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM=
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -2079,8 +2095,9 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -2204,8 +2221,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6 h1:SIasE1FVIQOWz2GEAHFOmoW7xchJcqlucjSULTL0Ag4=
|
||||
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -2250,6 +2267,7 @@ google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk
|
||||
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
@@ -2358,8 +2376,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
|
||||
@@ -934,6 +934,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -2743,6 +2745,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -3386,6 +3390,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
@@ -4682,6 +4690,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
|
||||
@@ -854,6 +854,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -1519,6 +1521,8 @@ spec:
|
||||
type: array
|
||||
suspend:
|
||||
type: boolean
|
||||
suspendState:
|
||||
type: string
|
||||
terminated:
|
||||
type: boolean
|
||||
required:
|
||||
|
||||
@@ -636,6 +636,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -244,6 +244,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
|
||||
V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -47,5 +47,5 @@ VELA_CORE_TEST_IMAGE ?= vela-core-test:$(GIT_COMMIT)
|
||||
VELA_APISERVER_IMAGE ?= apiserver:latest
|
||||
VELA_RUNTIME_ROLLOUT_IMAGE ?= vela-runtime-rollout:latest
|
||||
VELA_RUNTIME_ROLLOUT_TEST_IMAGE ?= vela-runtime-rollout-test:$(GIT_COMMIT)
|
||||
RUNTIME_CLUSTER_CONFIG ?= /tmp/worker.kubeconfig
|
||||
RUNTIME_CLUSTER_CONFIG ?= /tmp/worker.client.kubeconfig
|
||||
RUNTIME_CLUSTER_NAME ?= worker
|
||||
@@ -15,7 +15,7 @@ setup-runtime-e2e-cluster:
|
||||
|
||||
.PHONY: e2e-setup
|
||||
e2e-setup:
|
||||
helm install kruise https://github.com/openkruise/kruise/releases/download/v0.9.0/kruise-chart.tgz --set featureGates="PreDownloadImageForInPlaceUpdate=true"
|
||||
helm install kruise https://github.com/openkruise/charts/releases/download/kruise-1.1.0/kruise-1.1.0.tgz --set featureGates="PreDownloadImageForInPlaceUpdate=true"
|
||||
sh ./hack/e2e/modify_charts.sh
|
||||
helm upgrade --install --create-namespace --namespace vela-system --set image.pullPolicy=IfNotPresent --set image.repository=vela-core-test --set applicationRevisionLimit=5 --set dependCheckWait=10s --set image.tag=$(GIT_COMMIT) --wait kubevela ./charts/vela-core
|
||||
helm upgrade --install --create-namespace --namespace oam-runtime-system --set image.pullPolicy=IfNotPresent --set image.repository=vela-core-test --set dependCheckWait=10s --set image.tag=$(GIT_COMMIT) --wait oam-runtime ./charts/oam-runtime
|
||||
@@ -48,8 +48,8 @@ ADDONSERVER = $(shell pgrep vela_addon_mock_server)
|
||||
e2e-apiserver-test:
|
||||
pkill vela_addon_mock_server || true
|
||||
go run ./e2e/addon/mock/vela_addon_mock_server.go &
|
||||
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_apiserver_test.out ./test/e2e-apiserver-test
|
||||
sleep 15
|
||||
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_apiserver_test.out ./test/e2e-apiserver-test
|
||||
@$(OK) tests pass
|
||||
|
||||
.PHONY: e2e-test
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/google/go-github/v32/github"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
"golang.org/x/oauth2"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -447,6 +448,15 @@ func createGiteeHelper(content *utils.Content, token string) *giteeHelper {
|
||||
}
|
||||
}
|
||||
|
||||
func createGitlabHelper(content *utils.Content, token string) (*gitlabHelper, error) {
|
||||
newClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(content.GitlabContent.Host))
|
||||
|
||||
return &gitlabHelper{
|
||||
Client: newClient,
|
||||
Meta: content,
|
||||
}, err
|
||||
}
|
||||
|
||||
// readRepo will read relative path (relative to Meta.Path)
|
||||
func (h *gitHelper) readRepo(relativePath string) (*github.RepositoryContent, []*github.RepositoryContent, error) {
|
||||
file, items, _, err := h.Client.Repositories.GetContents(context.Background(), h.Meta.GithubContent.Owner, h.Meta.GithubContent.Repo, path.Join(h.Meta.GithubContent.Path, relativePath), nil)
|
||||
|
||||
@@ -147,7 +147,7 @@ func TestGetAddonData(t *testing.T) {
|
||||
server := httptest.NewServer(ossHandler)
|
||||
defer server.Close()
|
||||
|
||||
reader, err := NewAsyncReader(server.URL, "", "", "", ossType)
|
||||
reader, err := NewAsyncReader(server.URL, "", "", "", "", ossType)
|
||||
assert.NoError(t, err)
|
||||
testReaderFunc(t, reader)
|
||||
}
|
||||
@@ -617,9 +617,9 @@ func TestRenderApp4ObservabilityWithK8sData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPatternFromItem(t *testing.T) {
|
||||
ossR, err := NewAsyncReader("http://ep.beijing", "some-bucket", "some-sub-path", "", ossType)
|
||||
ossR, err := NewAsyncReader("http://ep.beijing", "some-bucket", "", "some-sub-path", "", ossType)
|
||||
assert.NoError(t, err)
|
||||
gitR, err := NewAsyncReader("https://github.com/oam-dev/catalog", "", "addons", "", gitType)
|
||||
gitR, err := NewAsyncReader("https://github.com/oam-dev/catalog", "", "", "addons", "", gitType)
|
||||
assert.NoError(t, err)
|
||||
gitItemName := "parameter.cue"
|
||||
gitItemType := FileType
|
||||
@@ -657,7 +657,7 @@ func TestGetPatternFromItem(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGitLabReaderNotPanic(t *testing.T) {
|
||||
_, err := NewAsyncReader("https://gitlab.com/test/catalog", "", "addons", "", gitType)
|
||||
_, err := NewAsyncReader("https://gitlab.com/test/catalog", "", "", "addons", "", gitType)
|
||||
assert.EqualError(t, err, "git type repository only support github for now")
|
||||
}
|
||||
|
||||
|
||||
150
pkg/addon/reader_gitlab.go
Normal file
150
pkg/addon/reader_gitlab.go
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package addon
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/xanzy/go-gitlab"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
var _ AsyncReader = &gitlabReader{}
|
||||
|
||||
// gitlabReader helps get addon's file by git
|
||||
type gitlabReader struct {
|
||||
h *gitlabHelper
|
||||
}
|
||||
|
||||
// gitlabHelper helps get addon's file by git
|
||||
type gitlabHelper struct {
|
||||
Client *gitlab.Client
|
||||
Meta *utils.Content
|
||||
}
|
||||
|
||||
// GitLabItem addon's sub item
|
||||
type GitLabItem struct {
|
||||
basePath string
|
||||
tp string
|
||||
path string
|
||||
name string
|
||||
}
|
||||
|
||||
// GetType get addon's sub item type
|
||||
func (g GitLabItem) GetType() string {
|
||||
return g.tp
|
||||
}
|
||||
|
||||
// GetPath get addon's sub item path
|
||||
func (g GitLabItem) GetPath() string {
|
||||
return g.path[len(g.basePath)+1:]
|
||||
}
|
||||
|
||||
// GetName get addon's sub item name
|
||||
func (g GitLabItem) GetName() string {
|
||||
return g.name
|
||||
}
|
||||
|
||||
// GetRef ref is empty , use default branch master
|
||||
func (g *gitlabReader) GetRef() string {
|
||||
var ref = "master"
|
||||
if g.h.Meta.GitlabContent.Ref != "" {
|
||||
return g.h.Meta.GitlabContent.Ref
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
// GetProjectID get gitlab project id
|
||||
func (g *gitlabReader) GetProjectID() int {
|
||||
return g.h.Meta.GitlabContent.PId
|
||||
}
|
||||
|
||||
// GetProjectPath get gitlab project path
|
||||
func (g *gitlabReader) GetProjectPath() string {
|
||||
return g.h.Meta.GitlabContent.Path
|
||||
}
|
||||
|
||||
// ListAddonMeta relative path to repoURL/basePath
|
||||
func (g *gitlabReader) ListAddonMeta() (addonCandidates map[string]SourceMeta, err error) {
|
||||
addonCandidates = make(map[string]SourceMeta)
|
||||
path := g.GetProjectPath()
|
||||
ref := g.GetRef()
|
||||
tree, _, err := g.h.Client.Repositories.ListTree(g.GetProjectID(), &gitlab.ListTreeOptions{Path: &path, Ref: &ref})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, node := range tree {
|
||||
if node.Type == TreeType {
|
||||
items, err := g.listAddonItem(make([]Item, 0), node.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addonCandidates[node.Name] = SourceMeta{
|
||||
Name: node.Name,
|
||||
Items: items,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return addonCandidates, nil
|
||||
}
|
||||
|
||||
func (g *gitlabReader) listAddonItem(item []Item, path string) ([]Item, error) {
|
||||
ref := g.GetRef()
|
||||
tree, _, err := g.h.Client.Repositories.ListTree(g.GetProjectID(), &gitlab.ListTreeOptions{Path: &path, Ref: &ref})
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
for _, node := range tree {
|
||||
switch node.Type {
|
||||
case TreeType:
|
||||
item, err = g.listAddonItem(item, node.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case BlobType:
|
||||
item = append(item, &GitLabItem{
|
||||
basePath: g.GetProjectPath(),
|
||||
tp: FileType,
|
||||
path: node.Path,
|
||||
name: node.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// ReadFile read file content from gitlab
|
||||
func (g *gitlabReader) ReadFile(path string) (content string, err error) {
|
||||
ref := g.GetRef()
|
||||
getFile, _, err := g.h.Client.RepositoryFiles.GetFile(g.GetProjectID(), g.GetProjectPath()+"/"+path, &gitlab.GetFileOptions{Ref: &ref})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
decodeString, err := base64.StdEncoding.DecodeString(getFile.Content)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(decodeString), nil
|
||||
}
|
||||
|
||||
func (g *gitlabReader) RelativePath(item Item) string {
|
||||
return item.GetPath()
|
||||
}
|
||||
113
pkg/addon/reader_gitlab_test.go
Normal file
113
pkg/addon/reader_gitlab_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package addon
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
var baseUrl = "/api/v4"
|
||||
|
||||
func gitlabSetup() (client *gitlab.Client, mux *http.ServeMux, teardown func()) {
|
||||
// mux is the HTTP request multiplexer used with the test server.
|
||||
mux = http.NewServeMux()
|
||||
|
||||
apiHandler := http.NewServeMux()
|
||||
apiHandler.Handle(baseUrl+"/", http.StripPrefix(baseUrl, mux))
|
||||
|
||||
// server is a test HTTP server used to provide mock API responses.
|
||||
server := httptest.NewServer(apiHandler)
|
||||
|
||||
// client is the Gitlab client being tested and is
|
||||
// configured to use test server.
|
||||
client, err := gitlab.NewClient("", gitlab.WithBaseURL(server.URL+baseUrl+"/"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return client, mux, server.Close
|
||||
}
|
||||
|
||||
func TestGitlabReader(t *testing.T) {
|
||||
client, mux, teardown := gitlabSetup()
|
||||
gitlabPattern := "/projects/9999/repository/files/"
|
||||
mux.HandleFunc(gitlabPattern, func(rw http.ResponseWriter, req *http.Request) {
|
||||
queryPath := strings.TrimPrefix(req.URL.Path, gitlabPattern)
|
||||
localPath := path.Join(testdataPrefix, queryPath)
|
||||
file, err := testdata.ReadFile(localPath)
|
||||
// test if it's a file
|
||||
if err == nil {
|
||||
content := &gitlab.File{
|
||||
FilePath: localPath,
|
||||
FileName: path.Base(queryPath),
|
||||
Size: *Int(len(file)),
|
||||
Encoding: "base64",
|
||||
Ref: "master",
|
||||
Content: base64.StdEncoding.EncodeToString(file),
|
||||
}
|
||||
res, _ := json.Marshal(content)
|
||||
rw.Write(res)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise, it could be directory
|
||||
dir, err := testdata.ReadDir(localPath)
|
||||
if err == nil {
|
||||
contents := make([]*gitlab.TreeNode, 0)
|
||||
for _, item := range dir {
|
||||
tp := "file"
|
||||
if item.IsDir() {
|
||||
tp = "dir"
|
||||
}
|
||||
contents = append(contents, &gitlab.TreeNode{
|
||||
ID: "",
|
||||
Name: item.Name(),
|
||||
Type: tp,
|
||||
Path: localPath + "/" + item.Name(),
|
||||
Mode: "",
|
||||
})
|
||||
}
|
||||
dRes, _ := json.Marshal(contents)
|
||||
rw.Write(dRes)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Write([]byte("invalid gitlab query"))
|
||||
})
|
||||
defer teardown()
|
||||
|
||||
gith := &gitlabHelper{
|
||||
Client: client,
|
||||
Meta: &utils.Content{GitlabContent: utils.GitlabContent{
|
||||
PId: 9999,
|
||||
}},
|
||||
}
|
||||
var r AsyncReader = &gitlabReader{gith}
|
||||
_, err := r.ReadFile("example/metadata.yaml")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -37,10 +37,11 @@ const registriesKey = "registries"
|
||||
type Registry struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
Helm *HelmSource `json:"helm,omitempty"`
|
||||
Git *GitAddonSource `json:"git,omitempty"`
|
||||
OSS *OSSAddonSource `json:"oss,omitempty"`
|
||||
Gitee *GiteeAddonSource `json:"gitee,omitempty"`
|
||||
Helm *HelmSource `json:"helm,omitempty"`
|
||||
Git *GitAddonSource `json:"git,omitempty"`
|
||||
OSS *OSSAddonSource `json:"oss,omitempty"`
|
||||
Gitee *GiteeAddonSource `json:"gitee,omitempty"`
|
||||
Gitlab *GitlabAddonSource `json:"gitlab,omitempty"`
|
||||
}
|
||||
|
||||
// RegistryDataStore CRUD addon registry data in configmap
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
@@ -35,6 +36,10 @@ const (
|
||||
DirType = "dir"
|
||||
// FileType means a file
|
||||
FileType = "file"
|
||||
// BlobType means a blob
|
||||
BlobType = "blob"
|
||||
// TreeType means a tree
|
||||
TreeType = "tree"
|
||||
|
||||
bucketTmpl = "%s://%s.%s"
|
||||
singleOSSFileTmpl = "%s/%s"
|
||||
@@ -63,6 +68,14 @@ type GiteeAddonSource struct {
|
||||
Token string `json:"token,omitempty"`
|
||||
}
|
||||
|
||||
// GitlabAddonSource defines the information about the Gitlab as addon source
|
||||
type GitlabAddonSource struct {
|
||||
URL string `json:"url,omitempty" validate:"required"`
|
||||
Repo string `json:"repo,omitempty" validate:"required"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
}
|
||||
|
||||
// HelmSource defines the information about the helm repo addon source
|
||||
type HelmSource struct {
|
||||
URL string `json:"url,omitempty" validate:"required"`
|
||||
@@ -122,15 +135,16 @@ func pathWithParent(subPath, parent string) string {
|
||||
type ReaderType string
|
||||
|
||||
const (
|
||||
gitType ReaderType = "git"
|
||||
ossType ReaderType = "oss"
|
||||
giteeType ReaderType = "gitee"
|
||||
gitType ReaderType = "git"
|
||||
ossType ReaderType = "oss"
|
||||
giteeType ReaderType = "gitee"
|
||||
gitlabType ReaderType = "gitlab"
|
||||
)
|
||||
|
||||
// NewAsyncReader create AsyncReader from
|
||||
// 1. GitHub url and directory
|
||||
// 2. OSS endpoint and bucket
|
||||
func NewAsyncReader(baseURL, bucket, subPath, token string, rdType ReaderType) (AsyncReader, error) {
|
||||
func NewAsyncReader(baseURL, bucket, repo, subPath, token string, rdType ReaderType) (AsyncReader, error) {
|
||||
|
||||
switch rdType {
|
||||
case gitType:
|
||||
@@ -182,23 +196,63 @@ func NewAsyncReader(baseURL, bucket, subPath, token string, rdType ReaderType) (
|
||||
return &giteeReader{
|
||||
h: gitee,
|
||||
}, nil
|
||||
case gitlabType:
|
||||
baseURL = strings.TrimSuffix(baseURL, ".git")
|
||||
u, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return nil, errors.New("addon registry invalid")
|
||||
}
|
||||
_, content, err := utils.ParseGitlab(u.String(), repo)
|
||||
content.GitlabContent.Path = subPath
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gitlabHelper, err := createGitlabHelper(content, token)
|
||||
if err != nil {
|
||||
return nil, errors.New("addon registry connect fail")
|
||||
}
|
||||
|
||||
err = gitlabHelper.getGitlabProject(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gitlabReader{
|
||||
h: gitlabHelper,
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid addon registry type '%s'", rdType)
|
||||
}
|
||||
|
||||
// getGitlabProject get gitlab project , set project id
|
||||
func (h *gitlabHelper) getGitlabProject(content *utils.Content) error {
|
||||
projectURL := content.GitlabContent.Owner + "/" + content.GitlabContent.Repo
|
||||
projects, _, err := h.Client.Projects.GetProject(projectURL, &gitlab.GetProjectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content.GitlabContent.PId = projects.ID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildReader will build a AsyncReader from registry, AsyncReader are needed to read addon files
|
||||
func (r *Registry) BuildReader() (AsyncReader, error) {
|
||||
if r.OSS != nil {
|
||||
o := r.OSS
|
||||
return NewAsyncReader(o.Endpoint, o.Bucket, o.Path, "", ossType)
|
||||
return NewAsyncReader(o.Endpoint, o.Bucket, "", o.Path, "", ossType)
|
||||
}
|
||||
if r.Git != nil {
|
||||
g := r.Git
|
||||
return NewAsyncReader(g.URL, "", g.Path, g.Token, gitType)
|
||||
return NewAsyncReader(g.URL, "", "", g.Path, g.Token, gitType)
|
||||
}
|
||||
if r.Gitee != nil {
|
||||
g := r.Gitee
|
||||
return NewAsyncReader(g.URL, "", g.Path, g.Token, giteeType)
|
||||
return NewAsyncReader(g.URL, "", "", g.Path, g.Token, giteeType)
|
||||
}
|
||||
if r.Gitlab != nil {
|
||||
g := r.Gitlab
|
||||
return NewAsyncReader(g.URL, "", g.Repo, g.Path, g.Token, gitlabType)
|
||||
}
|
||||
return nil, errors.New("registry don't have enough info to build a reader")
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestPathWithParent(t *testing.T) {
|
||||
|
||||
func TestConvert2OssItem(t *testing.T) {
|
||||
subPath := "sub-addons"
|
||||
reader, err := NewAsyncReader("ep-beijing.com", "bucket", subPath, "", ossType)
|
||||
reader, err := NewAsyncReader("ep-beijing.com", "bucket", "", subPath, "", ossType)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
||||
@@ -74,28 +74,31 @@ type NameAlias struct {
|
||||
|
||||
// CreateAddonRegistryRequest defines the format for addon registry create request
|
||||
type CreateAddonRegistryRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Helm *addon.HelmSource `json:"helm,omitempty"`
|
||||
Git *addon.GitAddonSource `json:"git,omitempty" `
|
||||
Oss *addon.OSSAddonSource `json:"oss,omitempty"`
|
||||
Gitee *addon.GiteeAddonSource `json:"gitee,omitempty" `
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Helm *addon.HelmSource `json:"helm,omitempty"`
|
||||
Git *addon.GitAddonSource `json:"git,omitempty" `
|
||||
Oss *addon.OSSAddonSource `json:"oss,omitempty"`
|
||||
Gitee *addon.GiteeAddonSource `json:"gitee,omitempty" `
|
||||
Gitlab *addon.GitlabAddonSource `json:"gitlab,omitempty" `
|
||||
}
|
||||
|
||||
// UpdateAddonRegistryRequest defines the format for addon registry update request
|
||||
type UpdateAddonRegistryRequest struct {
|
||||
Helm *addon.HelmSource `json:"helm,omitempty"`
|
||||
Git *addon.GitAddonSource `json:"git,omitempty"`
|
||||
Oss *addon.OSSAddonSource `json:"oss,omitempty"`
|
||||
Gitee *addon.GiteeAddonSource `json:"gitee,omitempty" `
|
||||
Helm *addon.HelmSource `json:"helm,omitempty"`
|
||||
Git *addon.GitAddonSource `json:"git,omitempty"`
|
||||
Oss *addon.OSSAddonSource `json:"oss,omitempty"`
|
||||
Gitee *addon.GiteeAddonSource `json:"gitee,omitempty" `
|
||||
Gitlab *addon.GitlabAddonSource `json:"gitlab,omitempty" `
|
||||
}
|
||||
|
||||
// AddonRegistry defines the format for a single addon registry
|
||||
type AddonRegistry struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Helm *addon.HelmSource `json:"helm,omitempty"`
|
||||
Git *addon.GitAddonSource `json:"git,omitempty"`
|
||||
OSS *addon.OSSAddonSource `json:"oss,omitempty"`
|
||||
Gitee *addon.GiteeAddonSource `json:"gitee,omitempty" `
|
||||
Name string `json:"name" validate:"required"`
|
||||
Helm *addon.HelmSource `json:"helm,omitempty"`
|
||||
Git *addon.GitAddonSource `json:"git,omitempty"`
|
||||
OSS *addon.OSSAddonSource `json:"oss,omitempty"`
|
||||
Gitee *addon.GiteeAddonSource `json:"gitee,omitempty" `
|
||||
Gitlab *addon.GitlabAddonSource `json:"gitlab,omitempty" `
|
||||
}
|
||||
|
||||
// ListAddonRegistryResponse list addon registry
|
||||
|
||||
@@ -313,11 +313,12 @@ func (u *defaultAddonHandler) CreateAddonRegistry(ctx context.Context, req apis.
|
||||
|
||||
func convertAddonRegistry(r pkgaddon.Registry) *apis.AddonRegistry {
|
||||
return &apis.AddonRegistry{
|
||||
Name: r.Name,
|
||||
Git: r.Git,
|
||||
Gitee: r.Gitee,
|
||||
OSS: r.OSS,
|
||||
Helm: r.Helm,
|
||||
Name: r.Name,
|
||||
Git: r.Git,
|
||||
Gitee: r.Gitee,
|
||||
OSS: r.OSS,
|
||||
Helm: r.Helm,
|
||||
Gitlab: r.Gitlab,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +344,8 @@ func (u defaultAddonHandler) UpdateAddonRegistry(ctx context.Context, name strin
|
||||
r.OSS = req.Oss
|
||||
case req.Helm != nil:
|
||||
r.Helm = req.Helm
|
||||
case req.Gitlab != nil:
|
||||
r.Gitlab = req.Gitlab
|
||||
}
|
||||
|
||||
err = u.addonRegistryDS.UpdateRegistry(ctx, r)
|
||||
@@ -476,11 +479,12 @@ func (u *defaultAddonHandler) UpdateAddon(ctx context.Context, name string, args
|
||||
|
||||
func addonRegistryModelFromCreateAddonRegistryRequest(req apis.CreateAddonRegistryRequest) pkgaddon.Registry {
|
||||
return pkgaddon.Registry{
|
||||
Name: req.Name,
|
||||
Git: req.Git,
|
||||
OSS: req.Oss,
|
||||
Gitee: req.Gitee,
|
||||
Helm: req.Helm,
|
||||
Name: req.Name,
|
||||
Git: req.Git,
|
||||
OSS: req.Oss,
|
||||
Gitee: req.Gitee,
|
||||
Helm: req.Helm,
|
||||
Gitlab: req.Gitlab,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ func (d *definitionUsecaseImpl) listDefinitions(ctx context.Context, list *unstr
|
||||
|
||||
// DetailDefinition get definition detail
|
||||
func (d *definitionUsecaseImpl) DetailDefinition(ctx context.Context, name, defType string) (*apisv1.DetailDefinitionResponse, error) {
|
||||
if !utils.StringsContain([]string{"component", "trait", "workflowstep"}, defType) {
|
||||
if !utils.StringsContain([]string{"component", "trait", "workflowstep", "policy"}, defType) {
|
||||
return nil, bcode.ErrDefinitionTypeNotSupport
|
||||
}
|
||||
var cm v1.ConfigMap
|
||||
|
||||
@@ -22,11 +22,11 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
)
|
||||
|
||||
// versionPrefix API version prefix.
|
||||
|
||||
@@ -182,6 +182,8 @@ type Appfile struct {
|
||||
|
||||
parser *Parser
|
||||
app *v1beta1.Application
|
||||
|
||||
Debug bool
|
||||
}
|
||||
|
||||
// GeneratePolicyManifests generates policy manifests from an appFile
|
||||
|
||||
@@ -345,6 +345,8 @@ func (p *Parser) parsePoliciesFromRevision(ctx context.Context, af *Appfile) (er
|
||||
case v1alpha1.EnvBindingPolicyType:
|
||||
case v1alpha1.TopologyPolicyType:
|
||||
case v1alpha1.OverridePolicyType:
|
||||
case v1alpha1.DebugPolicyType:
|
||||
af.Debug = true
|
||||
default:
|
||||
w, err := p.makeWorkloadFromRevision(policy.Name, policy.Type, types.TypePolicy, policy.Properties, af.AppRevision)
|
||||
if err != nil {
|
||||
@@ -367,6 +369,8 @@ func (p *Parser) parsePolicies(ctx context.Context, af *Appfile) (err error) {
|
||||
case v1alpha1.ApplyOncePolicyType:
|
||||
case v1alpha1.EnvBindingPolicyType:
|
||||
case v1alpha1.TopologyPolicyType:
|
||||
case v1alpha1.DebugPolicyType:
|
||||
af.Debug = true
|
||||
case v1alpha1.OverridePolicyType:
|
||||
compDefs, traitDefs, err := policypkg.ParseOverridePolicyRelatedDefinitions(ctx, p.client, af.app, policy)
|
||||
if err != nil {
|
||||
|
||||
37
pkg/auth/flags.go
Normal file
37
pkg/auth/flags.go
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultAuthenticateGroupPattern default value of groups patterns for authentication
|
||||
DefaultAuthenticateGroupPattern = types.KubeVelaName + ":*"
|
||||
)
|
||||
|
||||
var (
|
||||
// AuthenticationWithUser flag for enable the authentication of User in requests
|
||||
AuthenticationWithUser = false
|
||||
// AuthenticationDefaultUser the default user to use while no User is set in application
|
||||
AuthenticationDefaultUser = user.Anonymous
|
||||
// AuthenticationGroupPattern pattern for the authentication of Group in requests
|
||||
AuthenticationGroupPattern = DefaultAuthenticateGroupPattern
|
||||
)
|
||||
@@ -22,13 +22,17 @@ import (
|
||||
"net/http"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/client-go/transport"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
impersonateKey = "impersonate"
|
||||
)
|
||||
|
||||
var _ utilnet.RoundTripperWrapper = &impersonatingRoundTripper{}
|
||||
|
||||
type impersonatingRoundTripper struct {
|
||||
@@ -45,18 +49,22 @@ func NewImpersonatingRoundTripper(rt http.RoundTripper) http.RoundTripper {
|
||||
|
||||
func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
ctx := req.Context()
|
||||
|
||||
// Skip impersonation on non-local cluster requests
|
||||
if !multicluster.IsInLocalCluster(ctx) {
|
||||
return rt.rt.RoundTrip(req)
|
||||
req = req.Clone(ctx)
|
||||
userInfo, exists := request.UserFrom(ctx)
|
||||
if exists && userInfo != nil {
|
||||
if name := userInfo.GetName(); name != "" {
|
||||
req.Header.Set(transport.ImpersonateUserHeader, name)
|
||||
req.Header.Set(transport.ImpersonateGroupHeader, types.ClusterGatewayAccessorGroup)
|
||||
for _, group := range userInfo.GetGroups() {
|
||||
if group != types.ClusterGatewayAccessorGroup {
|
||||
req.Header.Add(transport.ImpersonateGroupHeader, group)
|
||||
}
|
||||
}
|
||||
q := req.URL.Query()
|
||||
q.Add(impersonateKey, "true")
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
}
|
||||
|
||||
sa := oamutil.GetServiceAccountInContext(ctx)
|
||||
if sa == "" {
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
req = req.Clone(req.Context())
|
||||
req.Header.Set(transport.ImpersonateUserHeader, sa)
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,16 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
authv1 "k8s.io/api/authentication/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/transport"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
type testRoundTripper struct {
|
||||
@@ -42,30 +48,51 @@ func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
func TestImpersonatingRoundTripper(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AuthenticateApplication, true)()
|
||||
AuthenticationWithUser = true
|
||||
defer func() {
|
||||
AuthenticationWithUser = false
|
||||
}()
|
||||
testSets := map[string]struct {
|
||||
ctxFn func(context.Context) context.Context
|
||||
expected string
|
||||
ctxFn func(context.Context) context.Context
|
||||
expectedUser string
|
||||
expectedGroup []string
|
||||
}{
|
||||
"with service account": {
|
||||
ctxFn: func(ctx context.Context) context.Context {
|
||||
ctx = oamutil.SetServiceAccountInContext(ctx, "vela-system", "default")
|
||||
return ctx
|
||||
app := &v1beta1.Application{}
|
||||
app.SetNamespace("vela-system")
|
||||
v1.SetMetaDataAnnotation(&app.ObjectMeta, oam.AnnotationApplicationServiceAccountName, "default")
|
||||
return ContextWithUserInfo(ctx, app)
|
||||
},
|
||||
expected: "system:serviceaccount:vela-system:default",
|
||||
expectedUser: "system:serviceaccount:vela-system:default",
|
||||
expectedGroup: []string{types.ClusterGatewayAccessorGroup},
|
||||
},
|
||||
"without service account and app": {
|
||||
ctxFn: func(ctx context.Context) context.Context {
|
||||
return ContextWithUserInfo(ctx, nil)
|
||||
},
|
||||
expectedUser: "",
|
||||
expectedGroup: []string{types.ClusterGatewayAccessorGroup},
|
||||
},
|
||||
"without service account": {
|
||||
ctxFn: func(ctx context.Context) context.Context {
|
||||
return ctx
|
||||
return ContextWithUserInfo(ctx, &v1beta1.Application{})
|
||||
},
|
||||
expected: "",
|
||||
expectedUser: AuthenticationDefaultUser,
|
||||
expectedGroup: []string{types.ClusterGatewayAccessorGroup},
|
||||
},
|
||||
"ignore if non-local cluster request": {
|
||||
"with user and groups": {
|
||||
ctxFn: func(ctx context.Context) context.Context {
|
||||
ctx = multicluster.ContextWithClusterName(ctx, "test-cluster")
|
||||
ctx = oamutil.SetServiceAccountInContext(ctx, "vela-system", "default")
|
||||
return ctx
|
||||
app := &v1beta1.Application{}
|
||||
SetUserInfoInAnnotation(&app.ObjectMeta, authv1.UserInfo{
|
||||
Username: "username",
|
||||
Groups: []string{"kubevela:group1", "kubevela:group2"},
|
||||
})
|
||||
return ContextWithUserInfo(ctx, app)
|
||||
},
|
||||
expected: "",
|
||||
expectedUser: "username",
|
||||
expectedGroup: []string{types.ClusterGatewayAccessorGroup, "kubevela:group1", "kubevela:group2"},
|
||||
},
|
||||
}
|
||||
for name, ts := range testSets {
|
||||
@@ -76,12 +103,13 @@ func TestImpersonatingRoundTripper(t *testing.T) {
|
||||
rt := &testRoundTripper{}
|
||||
_, err := NewImpersonatingRoundTripper(rt).RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
if ts.expected == "" {
|
||||
if ts.expectedUser == "" {
|
||||
_, ok := rt.Request.Header[transport.ImpersonateUserHeader]
|
||||
require.False(t, ok)
|
||||
return
|
||||
}
|
||||
require.Equal(t, ts.expected, rt.Request.Header.Get(transport.ImpersonateUserHeader))
|
||||
require.Equal(t, ts.expectedUser, rt.Request.Header.Get(transport.ImpersonateUserHeader))
|
||||
require.Equal(t, ts.expectedGroup, rt.Request.Header.Values(transport.ImpersonateGroupHeader))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
92
pkg/auth/userinfo.go
Normal file
92
pkg/auth/userinfo.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
authv1 "k8s.io/api/authentication/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/utils/strings/slices"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
const (
|
||||
groupSeparator = ","
|
||||
)
|
||||
|
||||
// ContextWithUserInfo inject username & group from app annotations into context
|
||||
// If serviceAccount is set and username is empty, identity will user the serviceAccount
|
||||
func ContextWithUserInfo(ctx context.Context, app *v1beta1.Application) context.Context {
|
||||
if app == nil {
|
||||
return ctx
|
||||
}
|
||||
return request.WithUser(ctx, GetUserInfoInAnnotation(&app.ObjectMeta))
|
||||
}
|
||||
|
||||
// SetUserInfoInAnnotation set username and group from userInfo into annotations
|
||||
// it will clear the existing service account annotation in avoid of permission leak
|
||||
func SetUserInfoInAnnotation(obj *metav1.ObjectMeta, userInfo authv1.UserInfo) {
|
||||
if AuthenticationWithUser {
|
||||
metav1.SetMetaDataAnnotation(obj, oam.AnnotationApplicationUsername, userInfo.Username)
|
||||
}
|
||||
re := regexp.MustCompile(strings.ReplaceAll(AuthenticationGroupPattern, "*", ".*"))
|
||||
var groups []string
|
||||
for _, group := range userInfo.Groups {
|
||||
if re.MatchString(group) {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
metav1.SetMetaDataAnnotation(obj, oam.AnnotationApplicationGroup, strings.Join(groups, groupSeparator))
|
||||
}
|
||||
|
||||
// GetUserInfoInAnnotation extract user info from annotations
|
||||
// support compatibility for serviceAccount when name is empty
|
||||
func GetUserInfoInAnnotation(obj *metav1.ObjectMeta) user.Info {
|
||||
annotations := obj.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
|
||||
name := annotations[oam.AnnotationApplicationUsername]
|
||||
if serviceAccountName := annotations[oam.AnnotationApplicationServiceAccountName]; serviceAccountName != "" && name == "" {
|
||||
name = fmt.Sprintf("system:serviceaccount:%s:%s", obj.GetNamespace(), serviceAccountName)
|
||||
}
|
||||
|
||||
if name == "" && utilfeature.DefaultMutableFeatureGate.Enabled(features.AuthenticateApplication) {
|
||||
name = AuthenticationDefaultUser
|
||||
}
|
||||
|
||||
return &user.DefaultInfo{
|
||||
Name: name,
|
||||
Groups: slices.Filter(
|
||||
[]string{},
|
||||
strings.Split(annotations[oam.AnnotationApplicationGroup], groupSeparator),
|
||||
func(s string) bool {
|
||||
return len(strings.TrimSpace(s)) > 0
|
||||
}),
|
||||
}
|
||||
}
|
||||
84
pkg/auth/userinfo_test.go
Normal file
84
pkg/auth/userinfo_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
authv1 "k8s.io/api/authentication/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
func TestContextWithUserInfo(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AuthenticateApplication, true)()
|
||||
AuthenticationWithUser = true
|
||||
defer func() {
|
||||
AuthenticationWithUser = false
|
||||
}()
|
||||
testCases := map[string]struct {
|
||||
UserInfo *authv1.UserInfo
|
||||
ServiceAccount string
|
||||
ExpectUserInfo user.Info
|
||||
}{
|
||||
"empty": {
|
||||
ExpectUserInfo: &user.DefaultInfo{
|
||||
Name: user.Anonymous,
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
"service-account": {
|
||||
ServiceAccount: "sa",
|
||||
ExpectUserInfo: &user.DefaultInfo{
|
||||
Name: "system:serviceaccount:default:sa",
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
"user-with-groups": {
|
||||
UserInfo: &authv1.UserInfo{
|
||||
Username: "user",
|
||||
Groups: []string{"group0", "kubevela:group1", "kubevela:group2"},
|
||||
},
|
||||
ServiceAccount: "override",
|
||||
ExpectUserInfo: &user.DefaultInfo{
|
||||
Name: "user",
|
||||
Groups: []string{"kubevela:group1", "kubevela:group2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, tt := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
r := require.New(t)
|
||||
app := &v1beta1.Application{}
|
||||
app.SetNamespace("default")
|
||||
if tt.UserInfo != nil {
|
||||
SetUserInfoInAnnotation(&app.ObjectMeta, *tt.UserInfo)
|
||||
}
|
||||
if tt.ServiceAccount != "" {
|
||||
metav1.SetMetaDataAnnotation(&app.ObjectMeta, oam.AnnotationApplicationServiceAccountName, tt.ServiceAccount)
|
||||
}
|
||||
r.Equal(tt.ExpectUserInfo, GetUserInfoInAnnotation(&app.ObjectMeta))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ type Meta struct {
|
||||
func (m *Meta) Lookup(field string) cue.Value {
|
||||
f := m.Obj.Lookup(field)
|
||||
if !f.Exists() {
|
||||
m.Err = fmt.Errorf("invalid string argument")
|
||||
m.Err = fmt.Errorf("invalid lookup argument")
|
||||
return cue.Value{}
|
||||
}
|
||||
if err := f.Err(); err != nil {
|
||||
@@ -54,7 +54,7 @@ func (m *Meta) Int64(field string) int64 {
|
||||
f := m.Obj.Lookup(field)
|
||||
value, err := f.Int64()
|
||||
if err != nil {
|
||||
m.Err = fmt.Errorf("invalid string argument, %w", err)
|
||||
m.Err = fmt.Errorf("invalid int64 argument, %w", err)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
}
|
||||
app.Status.SetConditions(condition.ReadyCondition(common.RenderCondition.String()))
|
||||
r.Recorder.Event(app, event.Normal(velatypes.ReasonRendered, velatypes.MessageRendered))
|
||||
wf := workflow.NewWorkflow(app, r.Client, appFile.WorkflowMode)
|
||||
wf := workflow.NewWorkflow(app, r.Client, appFile.WorkflowMode, appFile.Debug, handler.resourceKeeper)
|
||||
workflowState, err := wf.ExecuteSteps(logCtx.Fork("workflow"), handler.currentAppRev, steps)
|
||||
if err != nil {
|
||||
logCtx.Error(err, "[handle workflow]")
|
||||
@@ -213,23 +213,36 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
case common.WorkflowStateInitializing:
|
||||
logCtx.Info("Workflow return state=Initializing")
|
||||
handler.UpdateApplicationRevisionStatus(logCtx, handler.currentAppRev, false, app.Status.Workflow)
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationRendering, false)
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationRendering, false, false)
|
||||
case common.WorkflowStateSuspended:
|
||||
logCtx.Info("Workflow return state=Suspend")
|
||||
doWaiting, durationWaiting, err := wf.HandleSuspendWait(logCtx)
|
||||
if err != nil {
|
||||
return r.endWithNegativeCondition(logCtx, app, condition.ErrorCondition(common.WorkflowCondition.String(), err), common.ApplicationRunningWorkflow)
|
||||
}
|
||||
if doWaiting {
|
||||
if durationWaiting > 0 {
|
||||
_, err = r.gcResourceTrackers(logCtx, handler, common.ApplicationWorkflowSuspending, false, true)
|
||||
return r.result(err).requeue(durationWaiting).ret()
|
||||
}
|
||||
handler.app.Status.Workflow.Suspend = false
|
||||
handler.app.Status.Workflow.SuspendState = ""
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationRunningWorkflow, false, false)
|
||||
}
|
||||
if !workflow.IsFailedAfterRetry(app) {
|
||||
r.stateKeep(logCtx, handler, app)
|
||||
}
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationWorkflowSuspending, false)
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationWorkflowSuspending, false, true)
|
||||
case common.WorkflowStateTerminated:
|
||||
logCtx.Info("Workflow return state=Terminated")
|
||||
handler.UpdateApplicationRevisionStatus(logCtx, handler.latestAppRev, false, app.Status.Workflow)
|
||||
if err := r.doWorkflowFinish(app, wf); err != nil {
|
||||
return r.endWithNegativeCondition(ctx, app, condition.ErrorCondition(common.WorkflowCondition.String(), errors.WithMessage(err, "DoWorkflowFinish")), common.ApplicationRunningWorkflow)
|
||||
}
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationWorkflowTerminated, false)
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationWorkflowTerminated, false, true)
|
||||
case common.WorkflowStateExecuting:
|
||||
logCtx.Info("Workflow return state=Executing")
|
||||
_, err = r.gcResourceTrackers(logCtx, handler, common.ApplicationRunningWorkflow, false)
|
||||
_, err = r.gcResourceTrackers(logCtx, handler, common.ApplicationRunningWorkflow, false, true)
|
||||
return r.result(err).requeue(wf.GetBackoffWaitTime()).ret()
|
||||
case common.WorkflowStateSucceeded:
|
||||
logCtx.Info("Workflow return state=Succeeded")
|
||||
@@ -241,7 +254,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
r.Recorder.Event(app, event.Normal(velatypes.ReasonApplied, velatypes.MessageWorkflowFinished))
|
||||
logCtx.Info("Application manifests has applied by workflow successfully")
|
||||
if !EnableReconcileLoopReduction {
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationWorkflowFinished, false)
|
||||
return r.gcResourceTrackers(logCtx, handler, common.ApplicationWorkflowFinished, false, true)
|
||||
}
|
||||
case common.WorkflowStateFinished:
|
||||
logCtx.Info("Workflow state=Finished")
|
||||
@@ -275,7 +288,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
Reason: condition.ReasonReconcileSuccess,
|
||||
})
|
||||
r.Recorder.Event(app, event.Normal(velatypes.ReasonDeployed, velatypes.MessageDeployed))
|
||||
return r.gcResourceTrackers(logCtx, handler, phase, true)
|
||||
return r.gcResourceTrackers(logCtx, handler, phase, true, true)
|
||||
}
|
||||
|
||||
func (r *Reconciler) stateKeep(logCtx monitorContext.Context, handler *AppHandler, app *v1beta1.Application) {
|
||||
@@ -286,7 +299,7 @@ func (r *Reconciler) stateKeep(logCtx monitorContext.Context, handler *AppHandle
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *AppHandler, phase common.ApplicationPhase, gcOutdated bool) (ctrl.Result, error) {
|
||||
func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *AppHandler, phase common.ApplicationPhase, gcOutdated bool, isPatch bool) (ctrl.Result, error) {
|
||||
subCtx := logCtx.Fork("gc_resourceTrackers", monitorContext.DurationMetric(func(v float64) {
|
||||
metrics.GCResourceTrackersDurationHistogram.WithLabelValues("-").Observe(v)
|
||||
}))
|
||||
@@ -312,7 +325,7 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
|
||||
return r.result(r.patchStatus(logCtx, handler.app, phase)).requeue(baseGCBackoffWaitTime).ret()
|
||||
}
|
||||
logCtx.Info("GarbageCollected resourcetrackers")
|
||||
if phase == common.ApplicationRendering {
|
||||
if !isPatch {
|
||||
return r.result(r.updateStatus(logCtx, handler.app, common.ApplicationRunningWorkflow)).ret()
|
||||
}
|
||||
return r.result(r.patchStatus(logCtx, handler.app, phase)).ret()
|
||||
@@ -371,7 +384,7 @@ func (r *Reconciler) handleFinalizers(ctx monitorContext.Context, app *v1beta1.A
|
||||
if err != nil {
|
||||
return r.result(err).end(true)
|
||||
}
|
||||
result, err := r.gcResourceTrackers(ctx, handler, common.ApplicationDeleting, true)
|
||||
result, err := r.gcResourceTrackers(ctx, handler, common.ApplicationDeleting, true, true)
|
||||
if err != nil {
|
||||
return true, result, err
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/debug"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/tasks/custom"
|
||||
)
|
||||
|
||||
@@ -292,6 +293,35 @@ var _ = Describe("Test Application Controller", func() {
|
||||
},
|
||||
}
|
||||
|
||||
appWithMountToEnvs := &v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-with-mount-to-envs",
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: "myweb",
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
appWithMountToEnvs.Spec.Components[0].Traits = []common.ApplicationTrait{
|
||||
{
|
||||
Type: "storage",
|
||||
Properties: &runtime.RawExtension{Raw: []byte("{\"secret\": [{\"name\": \"myweb-secret\",\"mountToEnv\": {\"envName\": \"firstEnv\",\"secretKey\": \"firstKey\"},\"mountToEnvs\": [{\"envName\": \"secondEnv\",\"secretKey\": \"secondKey\"}],\"data\": {\"firstKey\": \"dmFsdWUwMQo=\",\"secondKey\": \"dmFsdWUwMgo=\"}}]}")},
|
||||
},
|
||||
{
|
||||
Type: "storage",
|
||||
Properties: &runtime.RawExtension{Raw: []byte("{\"configMap\": [{\"name\": \"myweb-cm\",\"mountToEnvs\": [{\"envName\":\"thirdEnv\",\"configMapKey\":\"thirdKey\"},{\"envName\":\"fourthEnv\",\"configMapKey\":\"fourthKey\"}],\"data\": {\"thirdKey\": \"Value03\",\"fourthKey\": \"Value04\"}}]}")},
|
||||
},
|
||||
}
|
||||
|
||||
cd := &v1beta1.ComponentDefinition{}
|
||||
cDDefJson, _ := yaml.YAMLToJSON([]byte(componentDefYaml))
|
||||
k8sObjectsCDJson, _ := yaml.YAMLToJSON([]byte(k8sObjectsComponentDefinitionYaml))
|
||||
@@ -2568,6 +2598,124 @@ var _ = Describe("Test Application Controller", func() {
|
||||
Expect(k8sClient.Delete(ctx, secret)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
|
||||
})
|
||||
|
||||
It("test application with multi-mountToEnv will create application", func() {
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-with-mount-to-envs",
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, ns)).Should(BeNil())
|
||||
|
||||
appWithMountToEnvs.SetNamespace(ns.Name)
|
||||
app := appWithMountToEnvs.DeepCopy()
|
||||
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
|
||||
|
||||
appKey := client.ObjectKey{
|
||||
Name: app.Name,
|
||||
Namespace: app.Namespace,
|
||||
}
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
|
||||
By("Check App running successfully")
|
||||
curApp := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil())
|
||||
Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
|
||||
appRevision := &v1beta1.ApplicationRevision{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: app.Namespace,
|
||||
Name: curApp.Status.LatestRevision.Name,
|
||||
}, appRevision)).Should(BeNil())
|
||||
By("Check affiliated resource tracker is created")
|
||||
expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{})
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Check AppRevision Created with the expected workload spec")
|
||||
appRev := &v1beta1.ApplicationRevision{}
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev)
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Check secret Created with the expected trait-storage spec")
|
||||
secret := &corev1.Secret{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: ns.Name,
|
||||
Name: app.Spec.Components[0].Name + "-secret",
|
||||
}, secret)).Should(BeNil())
|
||||
|
||||
By("Check configMap Created with the expected trait-storage spec")
|
||||
cm := &corev1.ConfigMap{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: ns.Name,
|
||||
Name: app.Spec.Components[0].Name + "-cm",
|
||||
}, cm)).Should(BeNil())
|
||||
|
||||
Expect(k8sClient.Delete(ctx, cm)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, secret)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
|
||||
})
|
||||
|
||||
It("app with debug policy", func() {
|
||||
app := &v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-debug",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: "myworker",
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\",\"env\":[{\"name\":\"firstKey\",\"value\":\"firstValue\"}]}")},
|
||||
},
|
||||
},
|
||||
Policies: []v1beta1.AppPolicy{
|
||||
{
|
||||
Type: "debug",
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
|
||||
|
||||
appKey := client.ObjectKey{
|
||||
Name: app.Name,
|
||||
Namespace: app.Namespace,
|
||||
}
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
|
||||
By("Check App running successfully")
|
||||
curApp := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil())
|
||||
Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
|
||||
By("Check debug Config Map is created")
|
||||
debugCM := &corev1.ConfigMap{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{
|
||||
Name: debug.GenerateContextName(app.Name, "myworker"),
|
||||
Namespace: "default",
|
||||
}, debugCM)).Should(BeNil())
|
||||
|
||||
By("Update the application to update the debug Config Map")
|
||||
app.Spec.Components[0].Properties = &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\",\"env\":[{\"name\":\"firstKey\",\"value\":\"updateValue\"}]}")}
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
updatedCM := &corev1.ConfigMap{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{
|
||||
Name: debug.GenerateContextName(app.Name, "myworker"),
|
||||
Namespace: "default",
|
||||
}, updatedCM)).Should(BeNil())
|
||||
|
||||
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
const (
|
||||
@@ -3604,6 +3752,17 @@ spec:
|
||||
}
|
||||
},
|
||||
] | []
|
||||
configMapMountToEnvsList: *[
|
||||
for v in parameter.configMap if v.mountToEnvs != _|_ for k in v.mountToEnvs {
|
||||
{
|
||||
name: k.envName
|
||||
valueFrom: configMapKeyRef: {
|
||||
name: v.name
|
||||
key: k.configMapKey
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
secretVolumeMountsList: *[
|
||||
for v in parameter.secret if v.mountPath != _|_ {
|
||||
{
|
||||
@@ -3623,6 +3782,17 @@ spec:
|
||||
}
|
||||
},
|
||||
] | []
|
||||
secretMountToEnvsList: *[
|
||||
for v in parameter.secret if v.mountToEnvs != _|_ for k in v.mountToEnvs {
|
||||
{
|
||||
name: k.envName
|
||||
valueFrom: secretKeyRef: {
|
||||
name: v.name
|
||||
key: k.secretKey
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
emptyDirVolumeMountsList: *[
|
||||
for v in parameter.emptyDir {
|
||||
{
|
||||
@@ -3645,7 +3815,7 @@ spec:
|
||||
|
||||
containers: [{
|
||||
// +patchKey=name
|
||||
env: configMapEnvMountsList + secretEnvMountsList
|
||||
env: configMapEnvMountsList + secretEnvMountsList + configMapMountToEnvsList + secretMountToEnvsList
|
||||
// +patchKey=name
|
||||
volumeDevices: volumeDevicesList
|
||||
// +patchKey=name
|
||||
@@ -3765,6 +3935,10 @@ spec:
|
||||
envName: string
|
||||
configMapKey: string
|
||||
}
|
||||
mountToEnvs?: [...{
|
||||
envName: string
|
||||
configMapKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
@@ -3784,6 +3958,10 @@ spec:
|
||||
envName: string
|
||||
secretKey: string
|
||||
}
|
||||
mountToEnvs?: [...{
|
||||
envName: string
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
|
||||
@@ -82,7 +82,7 @@ func NewAppHandler(ctx context.Context, r *Reconciler, app *v1beta1.Application,
|
||||
// Dispatch apply manifests into k8s.
|
||||
func (h *AppHandler) Dispatch(ctx context.Context, cluster string, owner common.ResourceCreatorRole, manifests ...*unstructured.Unstructured) error {
|
||||
manifests = multicluster.ResourcesWithClusterName(cluster, manifests...)
|
||||
if err := h.resourceKeeper.Dispatch(ctx, manifests); err != nil {
|
||||
if err := h.resourceKeeper.Dispatch(ctx, manifests, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, mf := range manifests {
|
||||
|
||||
@@ -473,6 +473,111 @@ var _ = Describe("Test Application with GC options", func() {
|
||||
Expect(len(rtList.Items)).Should(Equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Test Application enable gc option sequential", func() {
|
||||
baseApp := &v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sequential-gc",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: "worker1",
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
|
||||
DependsOn: []string{
|
||||
"worker2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "worker2",
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
|
||||
Inputs: common.StepInputs{
|
||||
{
|
||||
From: "worker3-output",
|
||||
ParameterKey: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "worker3",
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
|
||||
Outputs: common.StepOutputs{
|
||||
{
|
||||
Name: "worker3-output",
|
||||
ValueFrom: "output.metadata.name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Policies: []v1beta1.AppPolicy{{
|
||||
Name: "reverse-dependency",
|
||||
Type: "garbage-collect",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"order": "dependency"}`)},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
It("Test GC with sequential", func() {
|
||||
resourcekeeper.MarkWithProbability = 1.0
|
||||
app := baseApp.DeepCopy()
|
||||
|
||||
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
|
||||
appV1 := new(v1beta1.Application)
|
||||
Eventually(func() error {
|
||||
_, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), appV1); err != nil {
|
||||
return err
|
||||
}
|
||||
if appV1.Status.Phase != common.ApplicationRunning {
|
||||
return errors.New("app is not in running status")
|
||||
}
|
||||
return nil
|
||||
}, 3*time.Second, 300*time.Second).Should(BeNil())
|
||||
|
||||
By("check the resourceTrackers number")
|
||||
listOpts := []client.ListOption{
|
||||
client.MatchingLabels{
|
||||
oam.LabelAppName: app.Name,
|
||||
oam.LabelAppNamespace: app.Namespace,
|
||||
}}
|
||||
|
||||
rtList := &v1beta1.ResourceTrackerList{}
|
||||
Expect(k8sClient.List(ctx, rtList, listOpts...)).Should(BeNil())
|
||||
Expect(len(rtList.Items)).Should(Equal(2))
|
||||
workerList := &v1.DeploymentList{}
|
||||
Expect(k8sClient.List(ctx, workerList, listOpts...)).Should(BeNil())
|
||||
Expect(len(workerList.Items)).Should(Equal(3))
|
||||
|
||||
By("delete application")
|
||||
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
|
||||
By("worker3 will be deleted")
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)})
|
||||
Expect(k8sClient.List(ctx, workerList, listOpts...)).Should(BeNil())
|
||||
Expect(len(workerList.Items)).Should(Equal(2))
|
||||
By("worker2 will be deleted")
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)})
|
||||
Expect(k8sClient.List(ctx, workerList, listOpts...)).Should(BeNil())
|
||||
Expect(len(workerList.Items)).Should(Equal(1))
|
||||
By("worker1 will be deleted")
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)})
|
||||
Expect(k8sClient.List(ctx, workerList, listOpts...)).Should(BeNil())
|
||||
Expect(len(workerList.Items)).Should(Equal(0))
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)})
|
||||
Expect(k8sClient.List(ctx, rtList, listOpts...)).Should(BeNil())
|
||||
Expect(len(rtList.Items)).Should(Equal(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const (
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application/assemble"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/process"
|
||||
monitorContext "github.com/oam-dev/kubevela/pkg/monitor/context"
|
||||
"github.com/oam-dev/kubevela/pkg/monitor/metrics"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
@@ -56,7 +57,7 @@ var (
|
||||
|
||||
// GenerateApplicationSteps generate application steps.
|
||||
// nolint:gocyclo
|
||||
func (h *AppHandler) GenerateApplicationSteps(ctx context.Context,
|
||||
func (h *AppHandler) GenerateApplicationSteps(ctx monitorContext.Context,
|
||||
app *v1beta1.Application,
|
||||
appParser *appfile.Parser,
|
||||
af *appfile.Appfile,
|
||||
@@ -68,7 +69,7 @@ func (h *AppHandler) GenerateApplicationSteps(ctx context.Context,
|
||||
appParser, appRev, af), h.renderComponentFunc(appParser, appRev, af))
|
||||
http.Install(handlerProviders, h.r.Client, app.Namespace)
|
||||
pCtx := process.NewContext(generateContextDataFromApp(app, appRev.Name))
|
||||
taskDiscover := tasks.NewTaskDiscoverFromRevision(handlerProviders, h.r.pd, appRev, h.r.dm, pCtx)
|
||||
taskDiscover := tasks.NewTaskDiscoverFromRevision(ctx, handlerProviders, h.r.pd, appRev, h.r.dm, pCtx)
|
||||
multiclusterProvider.Install(handlerProviders, h.r.Client, app)
|
||||
terraformProvider.Install(handlerProviders, app, func(comp common.ApplicationComponent) (*appfile.Workload, error) {
|
||||
return appParser.ParseWorkloadFromRevision(comp, appRev)
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
monitorContext "github.com/oam-dev/kubevela/pkg/monitor/context"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
@@ -114,7 +115,8 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
taskRunner, err := handler.GenerateApplicationSteps(ctx, app, appParser, af, appRev)
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(taskRunner)).Should(BeEquivalentTo(2))
|
||||
Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
|
||||
@@ -155,7 +157,8 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
taskRunner, err := handler.GenerateApplicationSteps(ctx, app, appParser, af, appRev)
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(taskRunner)).Should(BeEquivalentTo(2))
|
||||
Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
|
||||
@@ -274,7 +277,8 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
taskRunner, err := handler.GenerateApplicationSteps(ctx, app, appParser, af, appRev)
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(taskRunner)).Should(BeEquivalentTo(2))
|
||||
Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
|
||||
@@ -314,7 +318,8 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
_, err = handler.GenerateApplicationSteps(ctx, app, appParser, af, appRev)
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
_, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
|
||||
@@ -351,7 +356,8 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
_, err = handler.GenerateApplicationSteps(ctx, app, appParser, af, appRev)
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
_, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -755,6 +755,7 @@ func (h *AppHandler) FinalizeAndApplyAppRevision(ctx context.Context) error {
|
||||
appRev.SetGroupVersionKind(v1beta1.ApplicationRevisionGroupVersionKind)
|
||||
// pass application's annotations & labels to app revision
|
||||
appRev.SetAnnotations(h.app.GetAnnotations())
|
||||
delete(appRev.Annotations, oam.AnnotationLastAppliedConfiguration)
|
||||
appRev.SetLabels(h.app.GetLabels())
|
||||
util.AddLabels(appRev, map[string]string{
|
||||
oam.LabelAppName: h.app.GetName(),
|
||||
|
||||
@@ -124,6 +124,29 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
r.record.Event(&policydefinition, event.Warning("failed to garbage collect DefinitionRevision of type PolicyDefinition", err))
|
||||
}
|
||||
|
||||
def := utils.NewCapabilityPolicyDef(&policydefinition)
|
||||
def.Name = req.NamespacedName.Name
|
||||
// Store the parameter of policyDefinition to configMap
|
||||
cmName, err := def.StoreOpenAPISchema(ctx, r.Client, r.pd, req.Namespace, req.Name, defRev.Name)
|
||||
if err != nil {
|
||||
klog.InfoS("Could not capability in ConfigMap", "err", err)
|
||||
r.record.Event(&(policydefinition), event.Warning("Could not store capability in ConfigMap", err))
|
||||
return ctrl.Result{}, util.PatchCondition(ctx, r, &(policydefinition),
|
||||
condition.ReconcileError(fmt.Errorf(util.ErrStoreCapabilityInConfigMap, def.Name, err)))
|
||||
}
|
||||
|
||||
if policydefinition.Status.ConfigMapRef != cmName {
|
||||
policydefinition.Status.ConfigMapRef = cmName
|
||||
if err := r.UpdateStatus(ctx, &policydefinition); err != nil {
|
||||
klog.InfoS("Could not update policyDefinition Status", "err", err)
|
||||
r.record.Event(&policydefinition, event.Warning("cannot update PolicyDefinition Status", err))
|
||||
return ctrl.Result{}, util.PatchCondition(ctx, r, &policydefinition,
|
||||
condition.ReconcileError(fmt.Errorf(util.ErrUpdatePolicyDefinition, policydefinition.Name, err)))
|
||||
}
|
||||
klog.InfoS("Successfully updated the status.configMapRef of the PolicyDefinition", "policyDefinition",
|
||||
klog.KRef(req.Namespace, req.Name), "status.configMapRef", cmName)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
package policydefinition
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/testutil"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Apply PolicyDefinition to store its schema to ConfigMap Test", func() {
|
||||
ctx := context.Background()
|
||||
var ns corev1.Namespace
|
||||
|
||||
Context("When the PolicyDefinition is valid, but the namespace doesn't exist, should occur errors", func() {
|
||||
It("Apply PolicyDefinition", func() {
|
||||
By("Apply PolicyDefinition")
|
||||
var validPolicyDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply raw kubernetes objects for your policy
|
||||
name: apply-object
|
||||
namespace: not-exist
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1alpha1"
|
||||
kind: "EnvBinding"
|
||||
spec: {
|
||||
engine: parameter.engine
|
||||
appTemplate: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: context.appName
|
||||
namespace: context.namespace
|
||||
}
|
||||
spec: {
|
||||
components: context.components
|
||||
}
|
||||
}
|
||||
envs: parameter.envs
|
||||
}
|
||||
}
|
||||
|
||||
#Env: {
|
||||
name: string
|
||||
patch: components: [...{
|
||||
name: string
|
||||
type: string
|
||||
properties: {...}
|
||||
}]
|
||||
placement: clusterSelector: {
|
||||
labels?: [string]: string
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
engine: *"ocm" | string
|
||||
envs: [...#Env]
|
||||
}
|
||||
`
|
||||
|
||||
var def v1beta1.PolicyDefinition
|
||||
Expect(yaml.Unmarshal([]byte(validPolicyDefinition), &def)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &def)).Should(Not(Succeed()))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the PolicyDefinition is valid, should create a ConfigMap", func() {
|
||||
var PolicyDefinitionName = "policy-obj"
|
||||
var namespace = "ns-plc-def-1"
|
||||
req := reconcile.Request{NamespacedName: client.ObjectKey{Name: PolicyDefinitionName, Namespace: namespace}}
|
||||
|
||||
It("Apply PolicyDefinition", func() {
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
By("Create a namespace")
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Apply PolicyDefinition")
|
||||
var validPolicyDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply raw kubernetes objects for your Policy
|
||||
name: policy-obj
|
||||
namespace: ns-plc-def-1
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1alpha1"
|
||||
kind: "EnvBinding"
|
||||
spec: {
|
||||
engine: parameter.engine
|
||||
appTemplate: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: context.appName
|
||||
namespace: context.namespace
|
||||
}
|
||||
spec: {
|
||||
components: context.components
|
||||
}
|
||||
}
|
||||
envs: parameter.envs
|
||||
}
|
||||
}
|
||||
|
||||
#Env: {
|
||||
name: string
|
||||
patch: components: [...{
|
||||
name: string
|
||||
type: string
|
||||
properties: {...}
|
||||
}]
|
||||
placement: clusterSelector: {
|
||||
labels?: [string]: string
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
engine: *"ocm" | string
|
||||
envs: [...#Env]
|
||||
}
|
||||
`
|
||||
|
||||
var def v1beta1.PolicyDefinition
|
||||
Expect(yaml.Unmarshal([]byte(validPolicyDefinition), &def)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &def)).Should(Succeed())
|
||||
testutil.ReconcileRetry(&r, req)
|
||||
|
||||
By("Check whether ConfigMap is created")
|
||||
var cm corev1.ConfigMap
|
||||
name := fmt.Sprintf("policy-%s%s", types.CapabilityConfigMapNamePrefix, PolicyDefinitionName)
|
||||
Eventually(func() bool {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &cm)
|
||||
return err == nil
|
||||
}, 30*time.Second, time.Second).Should(BeTrue())
|
||||
Expect(cm.Data[types.OpenapiV3JSONSchema]).Should(Not(Equal("")))
|
||||
Expect(cm.Labels["definition.oam.dev/name"]).Should(Equal(PolicyDefinitionName))
|
||||
|
||||
By("Check whether ConfigMapRef refer to right")
|
||||
Eventually(func() string {
|
||||
_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: def.Namespace, Name: def.Name}, &def)
|
||||
return def.Status.ConfigMapRef
|
||||
}, 30*time.Second, time.Second).Should(Equal(name))
|
||||
|
||||
By("Delete the policy")
|
||||
Expect(k8sClient.Delete(ctx, &def)).Should(Succeed())
|
||||
testutil.ReconcileRetry(&r, req)
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the PolicyDefinition is invalid, should report issues", func() {
|
||||
var invalidPolicyDefinitionName = "invalid-plc1"
|
||||
var namespace = "ns-plc-def2"
|
||||
BeforeEach(func() {
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
By("Create a namespace")
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
})
|
||||
|
||||
It("Applying invalid PolicyDefinition", func() {
|
||||
By("Apply the PolicyDefinition")
|
||||
var invalidPolicyDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply raw kubernetes objects for your policy
|
||||
name: invalid-plc1
|
||||
namespace: ns-plc-def2
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1alpha1"
|
||||
kind: "EnvBinding"
|
||||
spec: {
|
||||
engine: parameter.engine
|
||||
appTemplate: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: context.appName
|
||||
namespace: context.namespace
|
||||
}
|
||||
spec: {
|
||||
components: context.components
|
||||
}
|
||||
}
|
||||
envs: parameter.envs
|
||||
}
|
||||
}
|
||||
|
||||
#Env: {
|
||||
name: string
|
||||
patch: components: [...{
|
||||
name: string
|
||||
type: string
|
||||
properties: {...}
|
||||
}]
|
||||
placement: clusterSelector: {
|
||||
labels?: [string]: string
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
`
|
||||
|
||||
var invalidDef v1beta1.PolicyDefinition
|
||||
Expect(yaml.Unmarshal([]byte(invalidPolicyDefinition), &invalidDef)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &invalidDef)).Should(Succeed())
|
||||
gotPolicyDefinition := &v1beta1.PolicyDefinition{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: invalidPolicyDefinitionName, Namespace: namespace}, gotPolicyDefinition)).Should(BeNil())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -19,6 +19,8 @@ package controller
|
||||
import (
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/auth"
|
||||
ctrlClient "github.com/oam-dev/kubevela/pkg/client"
|
||||
"github.com/oam-dev/kubevela/pkg/component"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application"
|
||||
@@ -52,4 +54,9 @@ func AddAdmissionFlags() {
|
||||
flag.BoolVar(&resourcekeeper.AllowCrossNamespaceResource, "allow-cross-namespace-resource", true, "If set to false, application can only apply resources within its namespace. Default to be true.")
|
||||
flag.StringVar(&resourcekeeper.AllowResourceTypes, "allow-resource-types", "", "If not empty, application can only apply resources with specified types. For example, --allow-resource-types=whitelist:Deployment.v1.apps,Job.v1.batch")
|
||||
flag.StringVar(&component.RefObjectsAvailableScope, "ref-objects-available-scope", component.RefObjectsAvailableScopeGlobal, "The available scope for ref-objects component to refer objects. Should be one of `namespace`, `cluster`, `global`")
|
||||
|
||||
// auth flags
|
||||
flag.BoolVar(&auth.AuthenticationWithUser, "authentication-with-user", false, "If set to true, User will be carried on application. Resource requests will be impersonated as the User.")
|
||||
flag.StringVar(&auth.AuthenticationDefaultUser, "authentication-default-user", types.KubeVelaName+":"+types.VelaCoreName, "The User to impersonate when the User of application is not set.")
|
||||
flag.StringVar(&auth.AuthenticationGroupPattern, "authentication-group-pattern", auth.DefaultAuthenticateGroupPattern, "During authentication, only groups with specified pattern will be carried on application. Resource requests will be impersonated as these selected groups.")
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ const (
|
||||
typeTraitDefinition = "trait"
|
||||
typeComponentDefinition = "component"
|
||||
typeWorkflowStepDefinition = "workflowstep"
|
||||
typePolicyStepDefinition = "policy"
|
||||
)
|
||||
|
||||
// ErrNoSectionParameterInCue means there is not parameter section in Cue template of a workload
|
||||
@@ -567,6 +568,76 @@ func (def *CapabilityStepDefinition) StoreOpenAPISchema(ctx context.Context, k8s
|
||||
return cmName, nil
|
||||
}
|
||||
|
||||
// CapabilityPolicyDefinition is the Capability struct for PolicyDefinition
|
||||
type CapabilityPolicyDefinition struct {
|
||||
Name string `json:"name"`
|
||||
PolicyDefinition v1beta1.PolicyDefinition `json:"policyDefinition"`
|
||||
|
||||
CapabilityBaseDefinition
|
||||
}
|
||||
|
||||
// NewCapabilityPolicyDef will create a CapabilityPolicyDefinition
|
||||
func NewCapabilityPolicyDef(policydefinition *v1beta1.PolicyDefinition) CapabilityPolicyDefinition {
|
||||
var def CapabilityPolicyDefinition
|
||||
def.Name = policydefinition.Name
|
||||
def.PolicyDefinition = *policydefinition.DeepCopy()
|
||||
return def
|
||||
}
|
||||
|
||||
// GetOpenAPISchema gets OpenAPI v3 schema by StepDefinition name
|
||||
func (def *CapabilityPolicyDefinition) GetOpenAPISchema(pd *packages.PackageDiscover, name string) ([]byte, error) {
|
||||
capability, err := appfile.ConvertTemplateJSON2Object(name, nil, def.PolicyDefinition.Spec.Schematic)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert WorkflowStepDefinition to Capability Object")
|
||||
}
|
||||
return getOpenAPISchema(capability, pd)
|
||||
}
|
||||
|
||||
// StoreOpenAPISchema stores OpenAPI v3 schema from StepDefinition in ConfigMap
|
||||
func (def *CapabilityPolicyDefinition) StoreOpenAPISchema(ctx context.Context, k8sClient client.Client,
|
||||
pd *packages.PackageDiscover, namespace, name, revName string) (string, error) {
|
||||
var jsonSchema []byte
|
||||
var err error
|
||||
|
||||
jsonSchema, err = def.GetOpenAPISchema(pd, name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate OpenAPI v3 JSON schema for capability %s: %w", def.Name, err)
|
||||
}
|
||||
|
||||
policyDefinition := def.PolicyDefinition
|
||||
ownerReference := []metav1.OwnerReference{{
|
||||
APIVersion: policyDefinition.APIVersion,
|
||||
Kind: policyDefinition.Kind,
|
||||
Name: policyDefinition.Name,
|
||||
UID: policyDefinition.GetUID(),
|
||||
Controller: pointer.BoolPtr(true),
|
||||
BlockOwnerDeletion: pointer.BoolPtr(true),
|
||||
}}
|
||||
cmName, err := def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, policyDefinition.Name, typePolicyStepDefinition, policyDefinition.Labels, nil, jsonSchema, ownerReference)
|
||||
if err != nil {
|
||||
return cmName, err
|
||||
}
|
||||
|
||||
// Create a configmap to store parameter for each definitionRevision
|
||||
defRev := new(v1beta1.DefinitionRevision)
|
||||
if err = k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revName}, defRev); err != nil {
|
||||
return "", err
|
||||
}
|
||||
ownerReference = []metav1.OwnerReference{{
|
||||
APIVersion: defRev.APIVersion,
|
||||
Kind: defRev.Kind,
|
||||
Name: defRev.Name,
|
||||
UID: defRev.GetUID(),
|
||||
Controller: pointer.BoolPtr(true),
|
||||
BlockOwnerDeletion: pointer.BoolPtr(true),
|
||||
}}
|
||||
_, err = def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, revName, typePolicyStepDefinition, defRev.Spec.PolicyDefinition.Labels, nil, jsonSchema, ownerReference)
|
||||
if err != nil {
|
||||
return cmName, err
|
||||
}
|
||||
return cmName, nil
|
||||
}
|
||||
|
||||
// CapabilityBaseDefinition is the base struct for CapabilityWorkloadDefinition and CapabilityTraitDefinition
|
||||
type CapabilityBaseDefinition struct {
|
||||
}
|
||||
|
||||
@@ -458,6 +458,9 @@ func (val *Value) fields() ([]*field, error) {
|
||||
no, err := attr.Int(0)
|
||||
if err != nil {
|
||||
no = 100
|
||||
if v.Name == "#do" || v.Name == "#provider" {
|
||||
no = 0
|
||||
}
|
||||
}
|
||||
fields = append(fields, &field{
|
||||
no: no,
|
||||
|
||||
311
pkg/definition/go_gen.go
Normal file
311
pkg/definition/go_gen.go
Normal file
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package definition
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"github.com/fatih/camelcase"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
velacue "github.com/oam-dev/kubevela/pkg/cue"
|
||||
)
|
||||
|
||||
// StructParameter is a parameter that can be printed as a struct.
|
||||
type StructParameter struct {
|
||||
types.Parameter
|
||||
// GoType is the same to parameter.Type but can be print in Go
|
||||
GoType string
|
||||
Fields []Field
|
||||
}
|
||||
|
||||
// Field is a field of a struct.
|
||||
type Field struct {
|
||||
Name string
|
||||
// GoType is the same to parameter.Type but can be print in Go
|
||||
GoType string
|
||||
OmitEmpty bool
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
WellKnownAbbreviations = map[string]bool{
|
||||
"API": true,
|
||||
"DB": true,
|
||||
"HTTP": true,
|
||||
"HTTPS": true,
|
||||
"ID": true,
|
||||
"JSON": true,
|
||||
"OS": true,
|
||||
"SQL": true,
|
||||
"SSH": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"XML": true,
|
||||
"YAML": true,
|
||||
|
||||
"CPU": true,
|
||||
"PVC": true,
|
||||
}
|
||||
|
||||
DefaultNamer = NewFieldNamer("")
|
||||
)
|
||||
|
||||
// A FieldNamer generates a Go field name from a CUE label.
|
||||
type FieldNamer interface {
|
||||
FieldName(label string) string
|
||||
SetPrefix(string)
|
||||
}
|
||||
|
||||
// NewFieldNamer returns a new FieldNamer.
|
||||
func NewFieldNamer(prefix string) FieldNamer {
|
||||
return &AbbrFieldNamer{Prefix: prefix, Abbreviations: WellKnownAbbreviations}
|
||||
}
|
||||
|
||||
var structs []StructParameter
|
||||
|
||||
// GeneratorParameterStructs generates structs for parameters in cue.
|
||||
func GeneratorParameterStructs(param cue.Value) ([]StructParameter, error) {
|
||||
structs = []StructParameter{}
|
||||
err := parseParameters(param, "Parameter")
|
||||
return structs, err
|
||||
}
|
||||
|
||||
// NewStructParameter creates a StructParameter
|
||||
func NewStructParameter() StructParameter {
|
||||
return StructParameter{
|
||||
Parameter: types.Parameter{},
|
||||
GoType: "",
|
||||
Fields: []Field{},
|
||||
}
|
||||
}
|
||||
|
||||
// parseParameters will be called recursively to parse parameters
|
||||
func parseParameters(paraValue cue.Value, paramKey string) error {
|
||||
param := NewStructParameter()
|
||||
param.Name = paramKey
|
||||
param.Type = paraValue.IncompleteKind()
|
||||
param.Short, param.Usage, param.Alias, param.Ignore = velacue.RetrieveComments(paraValue)
|
||||
if def, ok := paraValue.Default(); ok && def.IsConcrete() {
|
||||
param.Default = velacue.GetDefault(def)
|
||||
}
|
||||
|
||||
// only StructKind will be separated go struct, other will be just a field
|
||||
if param.Type == cue.StructKind {
|
||||
arguments, err := paraValue.Struct()
|
||||
if err != nil {
|
||||
return fmt.Errorf("augument not as struct: %w", err)
|
||||
}
|
||||
if arguments.Len() == 0 { // in cue, empty struct like: foo: map[string]int
|
||||
tl := paraValue.Template()
|
||||
if tl != nil { // map type
|
||||
// TODO: kind maybe not simple type like string/int, if it is a struct, parseParameters should be called
|
||||
kind, err := trimIncompleteKind(tl("").IncompleteKind().String())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid parameter kind")
|
||||
}
|
||||
param.GoType = fmt.Sprintf("map[string]%s", kind)
|
||||
}
|
||||
}
|
||||
for i := 0; i < arguments.Len(); i++ {
|
||||
var subParam Field
|
||||
fi := arguments.Field(i)
|
||||
if fi.IsDefinition {
|
||||
continue
|
||||
}
|
||||
val := fi.Value
|
||||
name := fi.Name
|
||||
subParam.Name = name
|
||||
subParam.OmitEmpty = fi.IsOptional
|
||||
switch val.IncompleteKind() {
|
||||
case cue.StructKind:
|
||||
if subField, err := val.Struct(); err == nil && subField.Len() == 0 { // err cannot be not nil,so ignore it
|
||||
if mapValue, ok := val.Elem(); ok {
|
||||
// In the future we could recursively call to support complex map-value(struct or list)
|
||||
subParam.GoType = fmt.Sprintf("map[string]%s", mapValue.IncompleteKind().String())
|
||||
} else {
|
||||
// element in struct not defined, use interface{}
|
||||
subParam.GoType = "map[string]interface{}"
|
||||
}
|
||||
} else {
|
||||
if err := parseParameters(val, name); err != nil {
|
||||
return err
|
||||
}
|
||||
subParam.GoType = DefaultNamer.FieldName(name)
|
||||
}
|
||||
case cue.ListKind:
|
||||
elem, success := val.Elem()
|
||||
if !success {
|
||||
// fail to get elements, use the value of ListKind to be the type
|
||||
subParam.GoType = val.IncompleteKind().String()
|
||||
break
|
||||
}
|
||||
switch elem.Kind() {
|
||||
case cue.StructKind:
|
||||
subParam.GoType = fmt.Sprintf("[]%s", DefaultNamer.FieldName(name))
|
||||
if err := parseParameters(elem, name); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
subParam.GoType = fmt.Sprintf("[]%s", elem.IncompleteKind().String())
|
||||
}
|
||||
default:
|
||||
subParam.GoType = val.IncompleteKind().String()
|
||||
}
|
||||
param.Fields = append(param.Fields, subParam)
|
||||
}
|
||||
}
|
||||
structs = append(structs, param)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenGoCodeFromParams generates go code from parameters
|
||||
func GenGoCodeFromParams(parameters []StructParameter) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, parameter := range parameters {
|
||||
if parameter.Usage == "" {
|
||||
parameter.Usage = "-"
|
||||
}
|
||||
fmt.Fprintf(&buf, "// %s %s\n", DefaultNamer.FieldName(parameter.Name), parameter.Usage)
|
||||
genField(parameter, &buf)
|
||||
}
|
||||
source, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
fmt.Println("Failed to format source:", err)
|
||||
}
|
||||
|
||||
return string(source), nil
|
||||
}
|
||||
|
||||
// PrintParamGosStruct prints the StructParameter in Golang struct format
|
||||
func PrintParamGosStruct(parameters []StructParameter) {
|
||||
code, err := GenGoCodeFromParams(parameters)
|
||||
if err != nil {
|
||||
fmt.Println("Fail to gen code, err:", err)
|
||||
}
|
||||
fmt.Print(code)
|
||||
}
|
||||
|
||||
func genField(param StructParameter, buffer *bytes.Buffer) {
|
||||
fieldName := DefaultNamer.FieldName(param.Name)
|
||||
if param.Type == cue.StructKind { // only struct kind will be separated struct
|
||||
// cue struct can be Go map or struct
|
||||
if strings.HasPrefix(param.GoType, "map[string]") {
|
||||
fmt.Fprintf(buffer, "type %s %s", fieldName, param.GoType)
|
||||
} else {
|
||||
fmt.Fprintf(buffer, "type %s struct {\n", fieldName)
|
||||
for _, f := range param.Fields {
|
||||
jsonTag := f.Name
|
||||
if f.OmitEmpty {
|
||||
jsonTag = fmt.Sprintf("%s,omitempty", jsonTag)
|
||||
}
|
||||
fmt.Fprintf(buffer, " %s %s `json:\"%s\"`\n", DefaultNamer.FieldName(f.Name), f.GoType, jsonTag)
|
||||
}
|
||||
|
||||
fmt.Fprintf(buffer, "}\n")
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(buffer, "type %s %s\n", fieldName, param.GoType)
|
||||
}
|
||||
}
|
||||
|
||||
// trimIncompleteKind allows 2 types of incomplete kind, return the non-null one, more than two types of incomplete kind will return error
|
||||
// 1. (null|someKind)
|
||||
// 2. someKind
|
||||
func trimIncompleteKind(mask string) (string, error) {
|
||||
mask = strings.Trim(mask, "()")
|
||||
ks := strings.Split(mask, "|")
|
||||
if len(ks) == 1 {
|
||||
return ks[0], nil
|
||||
}
|
||||
if len(ks) == 2 && ks[0] == "null" {
|
||||
return ks[1], nil
|
||||
}
|
||||
return "", fmt.Errorf("invalid incomplete kind: %s", mask)
|
||||
|
||||
}
|
||||
|
||||
// An AbbrFieldNamer generates Go field names from Go
|
||||
// struct field while keeping abbreviations uppercased.
|
||||
type AbbrFieldNamer struct {
|
||||
// Prefix is a prefix to add to all field names with first char capitalized automatically.
|
||||
Prefix string
|
||||
prefixWithFirstCharCapitalized string
|
||||
Abbreviations map[string]bool
|
||||
}
|
||||
|
||||
// SetPrefix set a prefix to namer.
|
||||
func (a *AbbrFieldNamer) SetPrefix(s string) {
|
||||
a.Prefix = s
|
||||
}
|
||||
|
||||
// FieldName implements FieldNamer.FieldName.
|
||||
func (a *AbbrFieldNamer) FieldName(field string) string {
|
||||
if a.prefixWithFirstCharCapitalized == "" && a.Prefix != "" {
|
||||
a.prefixWithFirstCharCapitalized = strings.ToUpper(a.Prefix[:1]) + a.Prefix[1:]
|
||||
}
|
||||
components := SplitComponents(field)
|
||||
for i, component := range components {
|
||||
switch {
|
||||
case component == "":
|
||||
// do nothing
|
||||
case a.Abbreviations[strings.ToUpper(component)]:
|
||||
components[i] = strings.ToUpper(component)
|
||||
case component == strings.ToUpper(component):
|
||||
runes := []rune(component)
|
||||
components[i] = string(runes[0]) + strings.ToLower(string(runes[1:]))
|
||||
default:
|
||||
runes := []rune(component)
|
||||
runes[0] = unicode.ToUpper(runes[0])
|
||||
components[i] = string(runes)
|
||||
}
|
||||
}
|
||||
runes := []rune(strings.Join(components, ""))
|
||||
for i, r := range runes {
|
||||
if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '_' {
|
||||
runes[i] = '_'
|
||||
}
|
||||
}
|
||||
fieldName := string(runes)
|
||||
if !unicode.IsLetter(runes[0]) && runes[0] != '_' {
|
||||
fieldName = "_" + fieldName
|
||||
}
|
||||
if a.prefixWithFirstCharCapitalized != "" {
|
||||
fieldName = a.prefixWithFirstCharCapitalized + fieldName
|
||||
}
|
||||
return fieldName
|
||||
}
|
||||
|
||||
// SplitComponents splits name into components. name may be kebab case, snake
|
||||
// case, or camel case.
|
||||
func SplitComponents(name string) []string {
|
||||
switch {
|
||||
case strings.ContainsRune(name, '-'):
|
||||
return strings.Split(name, "-")
|
||||
case strings.ContainsRune(name, '_'):
|
||||
return strings.Split(name, "_")
|
||||
default:
|
||||
return camelcase.Split(name)
|
||||
}
|
||||
}
|
||||
360
pkg/definition/go_gen_test.go
Normal file
360
pkg/definition/go_gen_test.go
Normal file
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package definition
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"github.com/stretchr/testify/assert"
|
||||
assert2 "gotest.tools/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
func TestDefaultFieldNamer(t *testing.T) {
|
||||
for name, expected := range map[string]string{
|
||||
"id": "ID",
|
||||
"foo": "Foo",
|
||||
"foo_bar": "FooBar",
|
||||
"fooBar": "FooBar",
|
||||
"FOO_BAR": "FooBar",
|
||||
"FOO_BAR_ID": "FooBarID",
|
||||
"123": "_123",
|
||||
"A|B": "A_B",
|
||||
} {
|
||||
assert.Equal(t, expected, DefaultNamer.FieldName(name))
|
||||
}
|
||||
// test add prefix to name
|
||||
namer := NewFieldNamer("prefix")
|
||||
for name, expected := range map[string]string{
|
||||
"id": "PrefixID",
|
||||
"foo": "PrefixFoo",
|
||||
"foo_bar": "PrefixFooBar",
|
||||
"fooBar": "PrefixFooBar",
|
||||
"FOO_BAR": "PrefixFooBar",
|
||||
"FOO_BAR_ID": "PrefixFooBarID",
|
||||
"123": "Prefix_123",
|
||||
"A|B": "PrefixA_B",
|
||||
} {
|
||||
assert.Equal(t, expected, namer.FieldName(name))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTrimIncompleteKind(t *testing.T) {
|
||||
incompleteKinds := []struct {
|
||||
kind string
|
||||
expected string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
kind: "string",
|
||||
expected: "string",
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
kind: "(null|string)",
|
||||
expected: "string",
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
kind: "(null|string|int)",
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
for _, k := range incompleteKinds {
|
||||
actual, err := trimIncompleteKind(k.kind)
|
||||
if k.err {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, k.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneratorParameterStructs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
cue string
|
||||
expected []StructParameter
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
name: "go struct",
|
||||
cue: defWithStruct,
|
||||
err: false,
|
||||
expected: structsWithStruct,
|
||||
},
|
||||
{
|
||||
name: "go list",
|
||||
cue: defWithList,
|
||||
err: false,
|
||||
expected: structsWithList,
|
||||
},
|
||||
{
|
||||
name: "go struct list",
|
||||
cue: defWithStructList,
|
||||
err: false,
|
||||
expected: structsWithStructList,
|
||||
},
|
||||
{
|
||||
name: "go map",
|
||||
cue: defWithMap,
|
||||
err: false,
|
||||
expected: structsWithMap,
|
||||
},
|
||||
{
|
||||
name: "map element not defined",
|
||||
cue: defWithEmptyMap,
|
||||
err: false,
|
||||
expected: structWithInterface,
|
||||
},
|
||||
{
|
||||
name: "omitempty",
|
||||
cue: defWithOptional,
|
||||
err: false,
|
||||
expected: structWithOptional,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
value, err := common.GetCUEParameterValue(tc.cue, nil)
|
||||
assert.NoError(t, err)
|
||||
actual, err := GeneratorParameterStructs(value)
|
||||
if tc.err {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert2.DeepEqual(t, tc.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenGoCodeFromParams(t *testing.T) {
|
||||
testCases := []struct {
|
||||
structs []StructParameter
|
||||
result string
|
||||
}{
|
||||
{structs: structsWithStruct, result: resultWithStruct},
|
||||
{structs: structsWithList, result: resultWithList},
|
||||
{structs: structsWithStructList, result: resultWithStructList},
|
||||
{structs: structsWithMap, result: resultWithMap},
|
||||
{structs: structWithInterface, result: resultWithInterface},
|
||||
{structs: structWithOptional, result: resultWithOptional},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
actual, err := GenGoCodeFromParams(tc.structs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tc.result, actual)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
defWithStruct = `
|
||||
parameter: {
|
||||
// +usage=Specify the mapping relationship between the http path and the workload port
|
||||
http: {
|
||||
path: int
|
||||
}
|
||||
}`
|
||||
defWithList = `
|
||||
parameter: {
|
||||
http: [string]: int
|
||||
}`
|
||||
defWithStructList = `
|
||||
parameter: {
|
||||
emptyDir: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
}`
|
||||
defWithMap = `parameter: [string]: string | null`
|
||||
defWithEmptyMap = `
|
||||
parameter: {
|
||||
data: {}
|
||||
}`
|
||||
defWithOptional = `
|
||||
parameter:{
|
||||
data?: int
|
||||
}`
|
||||
|
||||
structsWithStruct = []StructParameter{
|
||||
{
|
||||
Parameter: types.Parameter{
|
||||
Name: "http",
|
||||
Type: cue.StructKind,
|
||||
Usage: "Specify the mapping relationship between the http path and the workload port",
|
||||
},
|
||||
Fields: []Field{
|
||||
{
|
||||
Name: "path",
|
||||
GoType: "int",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Parameter: types.Parameter{
|
||||
Type: cue.StructKind,
|
||||
Name: "Parameter",
|
||||
},
|
||||
Fields: []Field{
|
||||
{
|
||||
Name: "http",
|
||||
GoType: "HTTP",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
structsWithList = []StructParameter{
|
||||
{
|
||||
Parameter: types.Parameter{
|
||||
Type: cue.StructKind,
|
||||
Name: "Parameter",
|
||||
},
|
||||
Fields: []Field{
|
||||
{
|
||||
Name: "http",
|
||||
GoType: "map[string]int",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
structsWithStructList = []StructParameter{
|
||||
{
|
||||
Parameter: types.Parameter{
|
||||
Type: cue.StructKind,
|
||||
Name: "emptyDir",
|
||||
},
|
||||
Fields: []Field{
|
||||
{Name: "name", GoType: "string"},
|
||||
{Name: "mountPath", GoType: "string"},
|
||||
{Name: "medium", GoType: "string"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Parameter: types.Parameter{
|
||||
Type: cue.StructKind,
|
||||
Name: "Parameter",
|
||||
},
|
||||
Fields: []Field{
|
||||
{
|
||||
Name: "emptyDir",
|
||||
GoType: "[]EmptyDir",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
structsWithMap = []StructParameter{
|
||||
{
|
||||
Parameter: types.Parameter{
|
||||
Type: cue.StructKind,
|
||||
Name: "Parameter",
|
||||
},
|
||||
GoType: "map[string]string",
|
||||
Fields: []Field{},
|
||||
},
|
||||
}
|
||||
structWithInterface = []StructParameter{
|
||||
{
|
||||
Parameter: types.Parameter{
|
||||
Type: cue.StructKind,
|
||||
Name: "Parameter",
|
||||
},
|
||||
GoType: "",
|
||||
Fields: []Field{
|
||||
{Name: "data", GoType: "map[string]interface{}"},
|
||||
},
|
||||
},
|
||||
}
|
||||
structWithOptional = []StructParameter{
|
||||
{
|
||||
Parameter: types.Parameter{
|
||||
Type: cue.StructKind,
|
||||
Name: "Parameter",
|
||||
},
|
||||
GoType: "",
|
||||
Fields: []Field{
|
||||
{Name: "data", GoType: "int", OmitEmpty: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resultWithStruct = "// HTTP Specify the mapping relationship between the http path and the workload port\ntype HTTP struct {\n\tPath int `json:\"path\"`\n}\n\n// Parameter -\ntype Parameter struct {\n\tHTTP HTTP `json:\"http\"`\n}\n"
|
||||
resultWithList = "// Parameter -\ntype Parameter struct {\n\tHTTP map[string]int `json:\"http\"`\n}\n"
|
||||
resultWithStructList = "// EmptyDir -\ntype EmptyDir struct {\n\tName string `json:\"name\"`\n\tMountPath string `json:\"mountPath\"`\n\tMedium string `json:\"medium\"`\n}\n\n// Parameter -\ntype Parameter struct {\n\tEmptyDir []EmptyDir `json:\"emptyDir\"`\n}\n"
|
||||
resultWithMap = "// Parameter -\ntype Parameter map[string]string"
|
||||
resultWithInterface = "// Parameter -\ntype Parameter struct {\n\tData map[string]interface{} `json:\"data\"`\n}\n"
|
||||
resultWithOptional = "// Parameter -\ntype Parameter struct {\n\tData int `json:\"data,omitempty\"`\n}\n"
|
||||
)
|
||||
|
||||
func TestGenAllDef(t *testing.T) {
|
||||
skipDefs := []string{
|
||||
// non-concrete structs like
|
||||
// foo: string|{secretRef: string}
|
||||
"container-image.cue",
|
||||
"export2config.cue",
|
||||
"webhook.cue",
|
||||
"notification.cue",
|
||||
"webhook-notification.cue",
|
||||
"env.cue",
|
||||
"command.cue",
|
||||
|
||||
// not supported
|
||||
"json-merge-patch.cue",
|
||||
"json-patch.cue",
|
||||
|
||||
// no args
|
||||
"apply-application.cue",
|
||||
"apply-application-in-parallel.cue",
|
||||
}
|
||||
glob, err := filepath.Glob("../../vela-templates/definitions/*/*.cue")
|
||||
assert.NoError(t, err)
|
||||
for _, f := range glob {
|
||||
if !stringInSlice(filepath.Base(f), skipDefs) {
|
||||
t.Run(filepath.Base(f), func(t *testing.T) {
|
||||
file, err := ioutil.ReadFile(f)
|
||||
assert.NoError(t, err)
|
||||
def := Definition{Unstructured: unstructured.Unstructured{}}
|
||||
err = def.FromCUEString(string(file), nil)
|
||||
assert.NoError(t, err)
|
||||
templateString, _, err := unstructured.NestedString(def.Object, DefinitionTemplateKeys...)
|
||||
assert.NoError(t, err)
|
||||
value, err := common.GetCUEParameterValue(templateString, nil)
|
||||
assert.NoError(t, err)
|
||||
_, err = GeneratorParameterStructs(value)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// Compatibility Features
|
||||
|
||||
// DeprecatedPolicySpec enable the use of deprecated policy spec
|
||||
DeprecatedPolicySpec featuregate.Feature = "DeprecatedPolicySpec"
|
||||
// LegacyObjectTypeIdentifier enable the use of legacy object type identifier for selecting ref-object
|
||||
@@ -31,6 +33,11 @@ const (
|
||||
DeprecatedObjectLabelSelector featuregate.Feature = "DeprecatedObjectLabelSelector"
|
||||
// LegacyResourceTrackerGC enable the gc of legacy resource tracker in managed clusters
|
||||
LegacyResourceTrackerGC featuregate.Feature = "LegacyResourceTrackerGC"
|
||||
|
||||
// Edge Features
|
||||
|
||||
// AuthenticateApplication enable the authentication for application
|
||||
AuthenticateApplication featuregate.Feature = "AuthenticateApplication"
|
||||
)
|
||||
|
||||
var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
|
||||
@@ -38,6 +45,7 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
|
||||
LegacyObjectTypeIdentifier: {Default: false, PreRelease: featuregate.Alpha},
|
||||
DeprecatedObjectLabelSelector: {Default: false, PreRelease: featuregate.Alpha},
|
||||
LegacyResourceTrackerGC: {Default: true, PreRelease: featuregate.Alpha},
|
||||
AuthenticateApplication: {Default: false, PreRelease: featuregate.Alpha},
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -36,14 +36,6 @@ func GetCluster(o client.Object) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetServiceAccountNameFromAnnotations extracts the service account name from the given object's annotations.
|
||||
func GetServiceAccountNameFromAnnotations(o client.Object) string {
|
||||
if annotations := o.GetAnnotations(); annotations != nil {
|
||||
return annotations[AnnotationServiceAccountName]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetPublishVersion get PublishVersion from object
|
||||
func GetPublishVersion(o client.Object) string {
|
||||
if annotations := o.GetAnnotations(); annotations != nil {
|
||||
|
||||
@@ -199,7 +199,13 @@ const (
|
||||
// AnnotationControllerRequirement indicates the controller version that can process the application.
|
||||
AnnotationControllerRequirement = "app.oam.dev/controller-version-require"
|
||||
|
||||
// AnnotationServiceAccountName indicates the name of the ServiceAccount to use to apply Components and run Workflow.
|
||||
// AnnotationApplicationServiceAccountName indicates the name of the ServiceAccount to use to apply Components and run Workflow.
|
||||
// ServiceAccount will be used in the local cluster only.
|
||||
AnnotationServiceAccountName = "app.oam.dev/service-account-name"
|
||||
AnnotationApplicationServiceAccountName = "app.oam.dev/service-account-name"
|
||||
|
||||
// AnnotationApplicationUsername indicates the username of the Application to use to apply resources
|
||||
AnnotationApplicationUsername = "app.oam.dev/username"
|
||||
|
||||
// AnnotationApplicationGroup indicates the group of the Application to use to apply resources
|
||||
AnnotationApplicationGroup = "app.oam.dev/group"
|
||||
)
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/fnv"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -67,9 +66,6 @@ const (
|
||||
|
||||
// DummyTraitMessage is a message for trait which don't have definition found
|
||||
DummyTraitMessage = "No TraitDefinition found, all framework capabilities will work as default"
|
||||
|
||||
// DefinitionNamespaceEnv is env key for specifying a namespace to fetch definition
|
||||
DefinitionNamespaceEnv = "DEFINITION_NAMESPACE"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -307,35 +303,8 @@ func SetNamespaceInCtx(ctx context.Context, namespace string) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
// GetServiceAccountInContext returns the name of the service account which reconciles the app from the context.
|
||||
func GetServiceAccountInContext(ctx context.Context) string {
|
||||
if serviceAccount, ok := ctx.Value(ServiceAccountContextKey).(string); ok {
|
||||
return serviceAccount
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SetServiceAccountInContext sets the name of the service account which reconciles the app.
|
||||
func SetServiceAccountInContext(ctx context.Context, namespace, name string) context.Context {
|
||||
if name == "" {
|
||||
// We may set `default` service account when the service account name is omitted.
|
||||
// However, setting `default` service account will break existing cluster-scoped applications,
|
||||
// so it would be better to give users a migration term.
|
||||
// TODO(devholic): Use `default` service account if omitted.
|
||||
return ctx
|
||||
}
|
||||
return context.WithValue(ctx, ServiceAccountContextKey, fmt.Sprintf("system:serviceaccount:%s:%s", namespace, name))
|
||||
}
|
||||
|
||||
// GetDefinition get definition from two level namespace
|
||||
func GetDefinition(ctx context.Context, cli client.Reader, definition client.Object, definitionName string) error {
|
||||
if dns := os.Getenv(DefinitionNamespaceEnv); dns != "" {
|
||||
if err := cli.Get(ctx, types.NamespacedName{Name: definitionName, Namespace: dns}, definition); err == nil {
|
||||
return nil
|
||||
} else if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
appNs := GetDefinitionNamespaceWithCtx(ctx)
|
||||
if err := cli.Get(ctx, types.NamespacedName{Name: definitionName, Namespace: appNs}, definition); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -1659,13 +1658,6 @@ func TestGetDefinition(t *testing.T) {
|
||||
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) {
|
||||
|
||||
@@ -21,13 +21,13 @@ import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
|
||||
"github.com/onsi/gomega/format"
|
||||
"github.com/onsi/gomega/types"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
)
|
||||
|
||||
// JSONMarshal returns the JSON encoding
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
errors2 "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcetracker"
|
||||
@@ -41,7 +42,7 @@ func (h *resourceKeeper) DispatchComponentRevision(ctx context.Context, cr *v1.C
|
||||
obj.SetName(cr.Name)
|
||||
obj.SetNamespace(cr.Namespace)
|
||||
obj.SetLabels(cr.Labels)
|
||||
if err = resourcetracker.RecordManifestsInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, []*unstructured.Unstructured{obj}, true); err != nil {
|
||||
if err = resourcetracker.RecordManifestsInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, []*unstructured.Unstructured{obj}, true, common.WorkflowResourceCreator); err != nil {
|
||||
return errors.Wrapf(err, "failed to record componentrevision %s/%s/%s", oam.GetCluster(cr), cr.Namespace, cr.Name)
|
||||
}
|
||||
if err = h.Client.Create(multicluster.ContextWithClusterName(ctx, oam.GetCluster(cr)), cr); err != nil {
|
||||
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/auth"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcetracker"
|
||||
)
|
||||
|
||||
@@ -87,7 +87,7 @@ func (h *resourceKeeper) delete(ctx context.Context, manifest *unstructured.Unst
|
||||
}
|
||||
// 2. delete manifests
|
||||
deleteCtx := multicluster.ContextWithClusterName(ctx, oam.GetCluster(manifest))
|
||||
deleteCtx = oamutil.SetServiceAccountInContext(deleteCtx, h.app.Namespace, oam.GetServiceAccountNameFromAnnotations(h.app))
|
||||
deleteCtx = auth.ContextWithUserInfo(deleteCtx, h.app)
|
||||
if err = h.Client.Delete(deleteCtx, manifest); err != nil && !kerrors.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "cannot delete manifest, name: %s apiVersion: %s kind: %s", manifest.GetName(), manifest.GetAPIVersion(), manifest.GetKind())
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user