mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-23 22:33:58 +00:00
Compare commits
39 Commits
v1.7.2
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4209080adc | ||
|
|
61348b9d45 | ||
|
|
aa5e825683 | ||
|
|
95d04c370d | ||
|
|
a583f66b0d | ||
|
|
7ae1aff648 | ||
|
|
0ff40d75e5 | ||
|
|
3d410fed5f | ||
|
|
f8285df49d | ||
|
|
e4cd1ffd1d | ||
|
|
592f8b8e8f | ||
|
|
94c215c361 | ||
|
|
1723a1795d | ||
|
|
0861498ab8 | ||
|
|
52c9a8f0a3 | ||
|
|
c7f337fd35 | ||
|
|
9dca76e0de | ||
|
|
348f540a82 | ||
|
|
1c2df10299 | ||
|
|
918ed9727b | ||
|
|
fa78cb7632 | ||
|
|
f3cdbcf203 | ||
|
|
7bd2cf4dbc | ||
|
|
969babdd9e | ||
|
|
94c46a179b | ||
|
|
bec288c6b4 | ||
|
|
9a44be9788 | ||
|
|
9c7f4b7e03 | ||
|
|
539f1ed02b | ||
|
|
9e95122387 | ||
|
|
f49f11dd72 | ||
|
|
191d9038f1 | ||
|
|
e10e43b6c8 | ||
|
|
b9cc523267 | ||
|
|
7f1743ef58 | ||
|
|
dd39c38cf1 | ||
|
|
0851454c6f | ||
|
|
cd3577db53 | ||
|
|
d578adfe6e |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: 1.14.1
|
||||
args: release --rm-dist
|
||||
args: release --rm-dist --timeout 60m
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Since goreleaser haven't supported aliyun OSS, we need to upload the release manually
|
||||
|
||||
@@ -33,8 +33,8 @@ spec:
|
||||
arch: amd64
|
||||
{{addURIAndSha "https://github.com/oam-dev/kubevela/releases/download/{{ .TagName }}/kubectl-vela-{{ .TagName }}-windows-amd64.zip" .TagName }}
|
||||
files:
|
||||
- from: "*/kubectl-vela"
|
||||
to: "kubectl-vela.exe"
|
||||
- from: "*/kubectl-vela.exe"
|
||||
to: "."
|
||||
- from: "*/LICENSE"
|
||||
to: "."
|
||||
bin: "kubectl-vela.exe"
|
||||
|
||||
@@ -116,7 +116,7 @@ spec:
|
||||
#ECProvider: {
|
||||
type: "ec"
|
||||
apiKey: *"" | string
|
||||
name: "ec-provider" | string
|
||||
name: *"ec-provider" | string
|
||||
}
|
||||
#GCPProvider: {
|
||||
credentials: string
|
||||
|
||||
@@ -72,7 +72,7 @@ spec:
|
||||
}]
|
||||
}
|
||||
}
|
||||
parameter: *#PatchParams | close({
|
||||
parameter: #PatchParams | close({
|
||||
// +usage=Specify the container image for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -53,7 +53,10 @@ spec:
|
||||
}
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
isHealth: *false | bool
|
||||
if status != _|_ if status.loadBalancer != _|_ if status.loadBalancer.ingress != _|_ if len(status.loadBalancer.ingress) > 0 if status.loadBalancer.ingress[0].ip != _|_ {
|
||||
isHealth: true
|
||||
}
|
||||
if !isHealth {
|
||||
message: "ExternalIP: Pending"
|
||||
}
|
||||
@@ -62,10 +65,15 @@ spec:
|
||||
}
|
||||
}
|
||||
healthPolicy: |-
|
||||
isHealth: *true | bool
|
||||
service: context.outputs.service
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
isHealth: *false | bool
|
||||
if status != _|_ if status.loadBalancer != _|_ if status.loadBalancer.ingress != _|_ if len(status.loadBalancer.ingress) > 0 if status.loadBalancer.ingress[0].ip != _|_ {
|
||||
isHealth: true
|
||||
}
|
||||
}
|
||||
if service.spec.type != "LoadBalancer" {
|
||||
isHealth: true
|
||||
}
|
||||
|
||||
|
||||
@@ -113,19 +113,20 @@ spec:
|
||||
}
|
||||
if context.outputs.ingress.status.loadBalancer.ingress != _|_ {
|
||||
let igs = context.outputs.ingress.status.loadBalancer.ingress
|
||||
let host = context.outputs.ingress.spec.rules[0].host
|
||||
if igs[0].ip != _|_ {
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
|
||||
}
|
||||
if igs[0].host == _|_ {
|
||||
if host == _|_ {
|
||||
message: "Host not specified, visit the cluster or load balancer in front of the cluster with IP: " + igs[0].ip
|
||||
}
|
||||
}
|
||||
if igs[0].ip == _|_ {
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
|
||||
}
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Host not specified, visit the cluster or load balancer in front of the cluster"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,44 +56,41 @@ spec:
|
||||
// +usage=Specify the message that you want to sent, refer to [dingtalk messaging](https://developers.dingtalk.com/document/robots/custom-robot-access/title-72m-8ag-pqw)
|
||||
message: {
|
||||
// +usage=Specify the message content of dingtalk notification
|
||||
text?: *null | close({
|
||||
text?: close({
|
||||
content: string
|
||||
})
|
||||
// +usage=msgType can be text, link, mardown, actionCard, feedCard
|
||||
msgtype: *"text" | "link" | "markdown" | "actionCard" | "feedCard"
|
||||
link?: *null | close({
|
||||
#link: {
|
||||
text?: string
|
||||
title?: string
|
||||
messageUrl?: string
|
||||
picUrl?: string
|
||||
})
|
||||
markdown?: *null | close({
|
||||
}
|
||||
|
||||
link?: #link
|
||||
markdown?: close({
|
||||
text: string
|
||||
title: string
|
||||
})
|
||||
at?: *null | close({
|
||||
atMobiles?: *null | [...string]
|
||||
isAtAll?: bool
|
||||
at?: close({
|
||||
atMobiles?: [...string]
|
||||
isAtAll?: bool
|
||||
})
|
||||
actionCard?: *null | close({
|
||||
actionCard?: close({
|
||||
text: string
|
||||
title: string
|
||||
hideAvatar: string
|
||||
btnOrientation: string
|
||||
singleTitle: string
|
||||
singleURL: string
|
||||
btns: *null | close([...*null | close({
|
||||
btns?: [...close({
|
||||
title: string
|
||||
actionURL: string
|
||||
})])
|
||||
})]
|
||||
})
|
||||
feedCard?: *null | close({
|
||||
links: *null | close([...*null | close({
|
||||
text?: string
|
||||
title?: string
|
||||
messageUrl?: string
|
||||
picUrl?: string
|
||||
})])
|
||||
feedCard?: close({
|
||||
links: [...#link]
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -114,11 +111,11 @@ spec:
|
||||
// +usage=Specify the message that you want to sent, refer to [slack messaging](https://api.slack.com/reference/messaging/payload)
|
||||
message: {
|
||||
// +usage=Specify the message text for slack notification
|
||||
text: string
|
||||
blocks?: *null | close([...block])
|
||||
attachments?: *null | close({
|
||||
blocks?: *null | close([...block])
|
||||
color?: string
|
||||
text: string
|
||||
blocks?: [...block]
|
||||
attachments?: close({
|
||||
blocks?: [...block]
|
||||
color?: string
|
||||
})
|
||||
thread_ts?: string
|
||||
// +usage=Specify the message text format in markdown for slack notification
|
||||
|
||||
@@ -13,7 +13,7 @@ spec:
|
||||
template: |
|
||||
#PolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector: [...#RuleSelector]
|
||||
selector: #RuleSelector
|
||||
}
|
||||
#RuleSelector: {
|
||||
// +usage=Select resources by component names
|
||||
|
||||
@@ -323,7 +323,7 @@ spec:
|
||||
envName: string
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
|
||||
@@ -272,8 +272,8 @@ spec:
|
||||
- "--feature-gates=ZstdResourceTracker={{- .Values.featureGates.zstdResourceTracker | toString -}}"
|
||||
- "--feature-gates=ApplyOnce={{- .Values.featureGates.applyOnce | toString -}}"
|
||||
- "--feature-gates=MultiStageComponentApply= {{- .Values.featureGates.multiStageComponentApply | toString -}}"
|
||||
- "--feature-gates=GzipApplicationRevision={{- .Values.featureGates.gzipResourceTracker | toString -}}"
|
||||
- "--feature-gates=ZstdApplicationRevision={{- .Values.featureGates.zstdResourceTracker | toString -}}"
|
||||
- "--feature-gates=GzipApplicationRevision={{- .Values.featureGates.gzipApplicationRevision | toString -}}"
|
||||
- "--feature-gates=ZstdApplicationRevision={{- .Values.featureGates.zstdApplicationRevision | toString -}}"
|
||||
- "--feature-gates=PreDispatchDryRun={{- .Values.featureGates.preDispatchDryRun | toString -}}"
|
||||
{{ if .Values.authentication.enabled }}
|
||||
{{ if .Values.authentication.withUser }}
|
||||
|
||||
@@ -2,7 +2,7 @@ apiVersion: v1
|
||||
data:
|
||||
template: |
|
||||
import (
|
||||
"vela/ql"
|
||||
"vela/ql"
|
||||
)
|
||||
|
||||
parameter: {
|
||||
@@ -55,9 +55,15 @@ data:
|
||||
phase: pod.object.status.phase
|
||||
// refer to https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
if phase != "Pending" && phase != "Unknown" {
|
||||
podIP: pod.object.status.podIP
|
||||
hostIP: pod.object.status.hostIP
|
||||
nodeName: pod.object.spec.nodeName
|
||||
if pod.object.podIP != _|_ {
|
||||
podIP: pod.object.status.podIP
|
||||
}
|
||||
if pod.object.hostIP != _|_ {
|
||||
hostIP: pod.object.status.hostIP
|
||||
}
|
||||
if pod.object.nodeName != _|_ {
|
||||
nodeName: pod.object.spec.nodeName
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
@@ -65,7 +65,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
||||
| `controllerArgs.reSyncPeriod` | The period for resync the applications | `5m` |
|
||||
| `OAMSpecVer` | OAMSpecVer is the oam spec version controller want to setup | `minimal` |
|
||||
| `disableCaps` | Disable capability | `envbinding,rollout` |
|
||||
| `applyOnceOnly` | Valid applyOnceOnly values: true/false/on/off/force | `off` |
|
||||
| `dependCheckWait` | dependCheckWait is the time to wait for ApplicationConfiguration's dependent-resource ready | `30s` |
|
||||
|
||||
|
||||
@@ -97,20 +96,29 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
||||
| `healthCheck.port` | KubeVela health check port | `9440` |
|
||||
|
||||
|
||||
### KubeVela controller optimization parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `featureGates.applyOnce` | if enabled, the apply-once feature will be applied to all applications, no state-keep and no resource data storage in ResourceTracker | `false` |
|
||||
|
||||
|
||||
### MultiCluster parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------------------------------- | -------------------------------- | -------------------------------- |
|
||||
| `multicluster.enabled` | Whether to enable multi-cluster | `true` |
|
||||
| `multicluster.clusterGateway.replicaCount` | ClusterGateway replica count | `1` |
|
||||
| `multicluster.clusterGateway.port` | ClusterGateway port | `9443` |
|
||||
| `multicluster.clusterGateway.image.repository` | ClusterGateway image repository | `oamdev/cluster-gateway` |
|
||||
| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.7.0` |
|
||||
| `multicluster.clusterGateway.image.pullPolicy` | ClusterGateway image pull policy | `IfNotPresent` |
|
||||
| `multicluster.clusterGateway.resources.limits.cpu` | ClusterGateway cpu limit | `100m` |
|
||||
| `multicluster.clusterGateway.resources.limits.memory` | ClusterGateway memory limit | `200Mi` |
|
||||
| `multicluster.clusterGateway.secureTLS.enabled` | Whether to enable secure TLS | `true` |
|
||||
| `multicluster.clusterGateway.secureTLS.certPath` | Path to the certificate file | `/etc/k8s-cluster-gateway-certs` |
|
||||
| Name | Description | Value |
|
||||
| ------------------------------------------------------- | -------------------------------- | -------------------------------- |
|
||||
| `multicluster.enabled` | Whether to enable multi-cluster | `true` |
|
||||
| `multicluster.clusterGateway.replicaCount` | ClusterGateway replica count | `1` |
|
||||
| `multicluster.clusterGateway.port` | ClusterGateway port | `9443` |
|
||||
| `multicluster.clusterGateway.image.repository` | ClusterGateway image repository | `oamdev/cluster-gateway` |
|
||||
| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.8.0-alpha.3` |
|
||||
| `multicluster.clusterGateway.image.pullPolicy` | ClusterGateway image pull policy | `IfNotPresent` |
|
||||
| `multicluster.clusterGateway.resources.requests.cpu` | ClusterGateway cpu request | `50m` |
|
||||
| `multicluster.clusterGateway.resources.requests.memory` | ClusterGateway memory request | `20Mi` |
|
||||
| `multicluster.clusterGateway.resources.limits.cpu` | ClusterGateway cpu limit | `500m` |
|
||||
| `multicluster.clusterGateway.resources.limits.memory` | ClusterGateway memory limit | `200Mi` |
|
||||
| `multicluster.clusterGateway.secureTLS.enabled` | Whether to enable secure TLS | `true` |
|
||||
| `multicluster.clusterGateway.secureTLS.certPath` | Path to the certificate file | `/etc/k8s-cluster-gateway-certs` |
|
||||
|
||||
|
||||
### Test parameters
|
||||
|
||||
@@ -116,7 +116,7 @@ spec:
|
||||
#ECProvider: {
|
||||
type: "ec"
|
||||
apiKey: *"" | string
|
||||
name: "ec-provider" | string
|
||||
name: *"ec-provider" | string
|
||||
}
|
||||
#GCPProvider: {
|
||||
credentials: string
|
||||
|
||||
@@ -72,7 +72,7 @@ spec:
|
||||
}]
|
||||
}
|
||||
}
|
||||
parameter: *#PatchParams | close({
|
||||
parameter: #PatchParams | close({
|
||||
// +usage=Specify the container image for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -53,7 +53,10 @@ spec:
|
||||
}
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
isHealth: *false | bool
|
||||
if status != _|_ if status.loadBalancer != _|_ if status.loadBalancer.ingress != _|_ if len(status.loadBalancer.ingress) > 0 if status.loadBalancer.ingress[0].ip != _|_ {
|
||||
isHealth: true
|
||||
}
|
||||
if !isHealth {
|
||||
message: "ExternalIP: Pending"
|
||||
}
|
||||
@@ -62,10 +65,15 @@ spec:
|
||||
}
|
||||
}
|
||||
healthPolicy: |-
|
||||
isHealth: *true | bool
|
||||
service: context.outputs.service
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
isHealth: *false | bool
|
||||
if status != _|_ if status.loadBalancer != _|_ if status.loadBalancer.ingress != _|_ if len(status.loadBalancer.ingress) > 0 if status.loadBalancer.ingress[0].ip != _|_ {
|
||||
isHealth: true
|
||||
}
|
||||
}
|
||||
if service.spec.type != "LoadBalancer" {
|
||||
isHealth: true
|
||||
}
|
||||
|
||||
|
||||
@@ -113,19 +113,20 @@ spec:
|
||||
}
|
||||
if context.outputs.ingress.status.loadBalancer.ingress != _|_ {
|
||||
let igs = context.outputs.ingress.status.loadBalancer.ingress
|
||||
let host = context.outputs.ingress.spec.rules[0].host
|
||||
if igs[0].ip != _|_ {
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
|
||||
}
|
||||
if igs[0].host == _|_ {
|
||||
if host == _|_ {
|
||||
message: "Host not specified, visit the cluster or load balancer in front of the cluster with IP: " + igs[0].ip
|
||||
}
|
||||
}
|
||||
if igs[0].ip == _|_ {
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
|
||||
}
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Host not specified, visit the cluster or load balancer in front of the cluster"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,44 +56,41 @@ spec:
|
||||
// +usage=Specify the message that you want to sent, refer to [dingtalk messaging](https://developers.dingtalk.com/document/robots/custom-robot-access/title-72m-8ag-pqw)
|
||||
message: {
|
||||
// +usage=Specify the message content of dingtalk notification
|
||||
text?: *null | close({
|
||||
text?: close({
|
||||
content: string
|
||||
})
|
||||
// +usage=msgType can be text, link, mardown, actionCard, feedCard
|
||||
msgtype: *"text" | "link" | "markdown" | "actionCard" | "feedCard"
|
||||
link?: *null | close({
|
||||
#link: {
|
||||
text?: string
|
||||
title?: string
|
||||
messageUrl?: string
|
||||
picUrl?: string
|
||||
})
|
||||
markdown?: *null | close({
|
||||
}
|
||||
|
||||
link?: #link
|
||||
markdown?: close({
|
||||
text: string
|
||||
title: string
|
||||
})
|
||||
at?: *null | close({
|
||||
atMobiles?: *null | [...string]
|
||||
isAtAll?: bool
|
||||
at?: close({
|
||||
atMobiles?: [...string]
|
||||
isAtAll?: bool
|
||||
})
|
||||
actionCard?: *null | close({
|
||||
actionCard?: close({
|
||||
text: string
|
||||
title: string
|
||||
hideAvatar: string
|
||||
btnOrientation: string
|
||||
singleTitle: string
|
||||
singleURL: string
|
||||
btns: *null | close([...*null | close({
|
||||
btns?: [...close({
|
||||
title: string
|
||||
actionURL: string
|
||||
})])
|
||||
})]
|
||||
})
|
||||
feedCard?: *null | close({
|
||||
links: *null | close([...*null | close({
|
||||
text?: string
|
||||
title?: string
|
||||
messageUrl?: string
|
||||
picUrl?: string
|
||||
})])
|
||||
feedCard?: close({
|
||||
links: [...#link]
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -114,11 +111,11 @@ spec:
|
||||
// +usage=Specify the message that you want to sent, refer to [slack messaging](https://api.slack.com/reference/messaging/payload)
|
||||
message: {
|
||||
// +usage=Specify the message text for slack notification
|
||||
text: string
|
||||
blocks?: *null | close([...block])
|
||||
attachments?: *null | close({
|
||||
blocks?: *null | close([...block])
|
||||
color?: string
|
||||
text: string
|
||||
blocks?: [...block]
|
||||
attachments?: close({
|
||||
blocks?: [...block]
|
||||
color?: string
|
||||
})
|
||||
thread_ts?: string
|
||||
// +usage=Specify the message text format in markdown for slack notification
|
||||
|
||||
@@ -13,7 +13,7 @@ spec:
|
||||
template: |
|
||||
#PolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector: [...#RuleSelector]
|
||||
selector: #RuleSelector
|
||||
}
|
||||
#RuleSelector: {
|
||||
// +usage=Select resources by component names
|
||||
|
||||
@@ -323,7 +323,7 @@ spec:
|
||||
envName: string
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
|
||||
@@ -166,10 +166,8 @@ spec:
|
||||
- "--use-webhook=true"
|
||||
- "--webhook-port={{ .Values.webhookService.port }}"
|
||||
- "--webhook-cert-dir={{ .Values.admissionWebhooks.certificate.mountPath }}"
|
||||
- "--autogen-workload-definition={{ .Values.admissionWebhooks.autoGenWorkloadDefinition }}"
|
||||
{{ end }}
|
||||
- "--health-addr=:{{ .Values.healthCheck.port }}"
|
||||
- "--apply-once-only={{ .Values.applyOnceOnly }}"
|
||||
{{ if ne .Values.disableCaps "" }}
|
||||
- "--disable-caps={{ .Values.disableCaps }}"
|
||||
{{ end }}
|
||||
@@ -188,6 +186,7 @@ spec:
|
||||
- "--max-workflow-step-error-retry-times={{ .Values.workflow.step.errorRetryTimes }}"
|
||||
- "--feature-gates=EnableSuspendOnFailure={{- .Values.workflow.enableSuspendOnFailure | toString -}}"
|
||||
- "--feature-gates=AuthenticateApplication={{- .Values.authentication.enabled | toString -}}"
|
||||
- "--feature-gates=ApplyOnce={{- .Values.featureGates.applyOnce | toString -}}"
|
||||
{{ if .Values.authentication.enabled }}
|
||||
{{ if .Values.authentication.withUser }}
|
||||
- "--authentication-with-user"
|
||||
|
||||
@@ -26,9 +26,6 @@ OAMSpecVer: "minimal"
|
||||
## @param disableCaps Disable capability
|
||||
disableCaps: "envbinding,rollout"
|
||||
|
||||
## @param applyOnceOnly Valid applyOnceOnly values: true/false/on/off/force
|
||||
applyOnceOnly: "off"
|
||||
|
||||
## @param dependCheckWait dependCheckWait is the time to wait for ApplicationConfiguration's dependent-resource ready
|
||||
dependCheckWait: 30s
|
||||
|
||||
@@ -86,6 +83,11 @@ webhookService:
|
||||
healthCheck:
|
||||
port: 9440
|
||||
|
||||
## @section KubeVela controller optimization parameters
|
||||
|
||||
##@param featureGates.applyOnce if enabled, the apply-once feature will be applied to all applications, no state-keep and no resource data storage in ResourceTracker
|
||||
featureGates:
|
||||
applyOnce: false
|
||||
|
||||
## @section MultiCluster parameters
|
||||
|
||||
@@ -95,6 +97,8 @@ healthCheck:
|
||||
## @param multicluster.clusterGateway.image.repository ClusterGateway image repository
|
||||
## @param multicluster.clusterGateway.image.tag ClusterGateway image tag
|
||||
## @param multicluster.clusterGateway.image.pullPolicy ClusterGateway image pull policy
|
||||
## @param multicluster.clusterGateway.resources.requests.cpu ClusterGateway cpu request
|
||||
## @param multicluster.clusterGateway.resources.requests.memory ClusterGateway memory request
|
||||
## @param multicluster.clusterGateway.resources.limits.cpu ClusterGateway cpu limit
|
||||
## @param multicluster.clusterGateway.resources.limits.memory ClusterGateway memory limit
|
||||
## @param multicluster.clusterGateway.secureTLS.enabled Whether to enable secure TLS
|
||||
@@ -106,11 +110,14 @@ multicluster:
|
||||
port: 9443
|
||||
image:
|
||||
repository: oamdev/cluster-gateway
|
||||
tag: v1.7.0
|
||||
tag: v1.8.0-alpha.3
|
||||
pullPolicy: IfNotPresent
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 20Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
cpu: 500m
|
||||
memory: 200Mi
|
||||
secureTLS:
|
||||
enabled: true
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/kubevela/pkg/util/compression"
|
||||
"github.com/kubevela/pkg/util/k8s"
|
||||
"github.com/kubevela/pkg/util/singleton"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/klog/v2"
|
||||
@@ -55,7 +56,7 @@ func (in *SystemCRDValidationHook) Run(ctx context.Context) error {
|
||||
feature.DefaultMutableFeatureGate.Enabled(features.GzipApplicationRevision) {
|
||||
appRev := &v1beta1.ApplicationRevision{}
|
||||
appRev.Name = fmt.Sprintf("core.pre-check.%d", time.Now().UnixNano())
|
||||
appRev.Namespace = types.DefaultKubeVelaNS
|
||||
appRev.Namespace = k8s.GetRuntimeNamespace()
|
||||
key := client.ObjectKeyFromObject(appRev)
|
||||
appRev.SetLabels(map[string]string{oam.LabelPreCheck: types.VelaCoreName})
|
||||
appRev.Spec.Application.Name = appRev.Name
|
||||
|
||||
@@ -74,6 +74,12 @@ var _ = Describe("Addon Test", func() {
|
||||
Expect(output).To(ContainSubstring("enabled successfully."))
|
||||
})
|
||||
|
||||
It("Enable addon with specified registry ", func() {
|
||||
output, err := e2e.LongTimeExec("vela addon enable KubeVela/test-addon", 300*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).To(ContainSubstring("enabled successfully."))
|
||||
})
|
||||
|
||||
It("Disable addon test-addon", func() {
|
||||
output, err := e2e.LongTimeExec("vela addon disable test-addon", 600*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
32
go.mod
32
go.mod
@@ -3,7 +3,7 @@ module github.com/oam-dev/kubevela
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
cuelang.org/go v0.5.0-alpha.1
|
||||
cuelang.org/go v0.5.0-beta.5
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1
|
||||
github.com/FogDong/uitable v0.0.5
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
@@ -54,17 +54,16 @@ require (
|
||||
github.com/hashicorp/hcl/v2 v2.12.0
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174
|
||||
github.com/imdario/mergo v0.3.13
|
||||
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
|
||||
github.com/kubevela/pkg v0.0.0-20230105054759-263dc191bf51
|
||||
github.com/kubevela/pkg v0.0.0-20230316114047-e2b41b377bac
|
||||
github.com/kubevela/prism v1.7.0-alpha.1
|
||||
github.com/kubevela/workflow v0.4.1
|
||||
github.com/kubevela/workflow v0.4.3
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.1
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
|
||||
github.com/oam-dev/cluster-gateway v1.7.0-alpha.1
|
||||
github.com/oam-dev/cluster-register v1.0.4-0.20220928064144-5f76a9d7ca8c
|
||||
github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28
|
||||
github.com/oam-dev/terraform-controller v0.7.8
|
||||
github.com/oam-dev/terraform-controller v0.7.10
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/ginkgo/v2 v2.1.6
|
||||
@@ -88,9 +87,9 @@ require (
|
||||
go.mongodb.org/mongo-driver v1.5.1
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.4.0
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2
|
||||
golang.org/x/term v0.3.0
|
||||
golang.org/x/text v0.5.0
|
||||
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c
|
||||
golang.org/x/term v0.5.0
|
||||
golang.org/x/text v0.7.0
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools v2.2.0+incompatible
|
||||
@@ -100,14 +99,14 @@ require (
|
||||
k8s.io/apiextensions-apiserver v0.25.2
|
||||
k8s.io/apimachinery v0.25.3
|
||||
k8s.io/apiserver v0.25.3
|
||||
k8s.io/cli-runtime v0.25.2
|
||||
k8s.io/cli-runtime v0.25.3
|
||||
k8s.io/client-go v0.25.3
|
||||
k8s.io/component-base v0.25.3
|
||||
k8s.io/helm v2.17.0+incompatible
|
||||
k8s.io/klog/v2 v2.70.1
|
||||
k8s.io/kube-aggregator v0.25.3
|
||||
k8s.io/kubectl v0.25.2
|
||||
k8s.io/metrics v0.25.2
|
||||
k8s.io/kubectl v0.25.3
|
||||
k8s.io/metrics v0.25.3
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
|
||||
open-cluster-management.io/api v0.7.0
|
||||
sigs.k8s.io/controller-runtime v0.12.3
|
||||
@@ -117,6 +116,8 @@ require (
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
@@ -301,14 +302,14 @@ require (
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.3.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/grpc v1.48.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
@@ -348,7 +349,6 @@ require (
|
||||
|
||||
replace (
|
||||
cloud.google.com/go => cloud.google.com/go v0.100.2
|
||||
cuelang.org/go => github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be
|
||||
github.com/docker/cli => github.com/docker/cli v20.10.9+incompatible
|
||||
github.com/docker/docker => github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
|
||||
github.com/wercker/stern => github.com/oam-dev/stern v1.13.2
|
||||
|
||||
55
go.sum
55
go.sum
@@ -34,6 +34,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
|
||||
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs=
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
|
||||
cuelang.org/go v0.5.0-beta.5 h1:TAV4ZjXw2M6xf6jI8XyAAXCqWJ82Y0oxhlf9w3l544A=
|
||||
cuelang.org/go v0.5.0-beta.5/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI=
|
||||
github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
|
||||
@@ -1283,14 +1285,12 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be h1:0xj/Rh4yVy54mUD2nLmAuN1AYgBkkHxBh4PoLGbIg5g=
|
||||
github.com/kubevela/cue v0.4.4-0.20221107123854-a976b0e340be/go.mod h1:Ya12qn7FZc+LSN0qgEhzEpnzQsvnGHVgoDrqe9i3eNg=
|
||||
github.com/kubevela/pkg v0.0.0-20230105054759-263dc191bf51 h1:xrcNNaAjqC6tr1leSYcjLFgrXKpZ8u87jpB5TolhUIc=
|
||||
github.com/kubevela/pkg v0.0.0-20230105054759-263dc191bf51/go.mod h1:ZRnxY/gOcg/8FilZA+eYr+rtVXb1ijT5HFTe8zrv9zo=
|
||||
github.com/kubevela/pkg v0.0.0-20230316114047-e2b41b377bac h1:TLQchMx+BRTnHyebDpOWF2RpF2eTczFBtJro3/H9vwI=
|
||||
github.com/kubevela/pkg v0.0.0-20230316114047-e2b41b377bac/go.mod h1:GilLxt+9L4sU2tLeZAGHga8wiYmjjfPX/Q6JkyuuXSM=
|
||||
github.com/kubevela/prism v1.7.0-alpha.1 h1:oeZFn1Oy6gxSSFzMTfsWjLOCKaaooMVm1JGNK4j4Mlo=
|
||||
github.com/kubevela/prism v1.7.0-alpha.1/go.mod h1:AJSDfdA+RkRSnWx3xEcogbmOTpX+l7RSIwqVHxwUtaI=
|
||||
github.com/kubevela/workflow v0.4.1 h1:lYeWE9KgSSkb368u8G7cGfyzCz41Am8MdxgViRFJxXE=
|
||||
github.com/kubevela/workflow v0.4.1/go.mod h1:AX/WL3G/YBkpmNpA/SKKm9M3Y0T9y95gZA8mFWylkyM=
|
||||
github.com/kubevela/workflow v0.4.3 h1:1WAMb4xrRXQW5hK5bXwV2WFHlxn2+ezkblai8oC5DmY=
|
||||
github.com/kubevela/workflow v0.4.3/go.mod h1:U94Hz5rlHPAatN+Birhumly26zjAguMumdhrYk+e5mo=
|
||||
github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U=
|
||||
github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4=
|
||||
github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
@@ -1435,8 +1435,8 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.1 h1:L60q1+q7cXE4JeEJJKMnh2brFIe3rZxCihYAB61ypAY=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.1/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
@@ -1523,8 +1523,8 @@ github.com/oam-dev/stern v1.13.2 h1:jlGgtJbKmIVhzkH44ft5plkgs8XEfvxbFrQdX60CQR4=
|
||||
github.com/oam-dev/stern v1.13.2/go.mod h1:0pLjZt0amXE/ErF16Rdrgd98H2owN8Hmn3/7CX5+AeA=
|
||||
github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28 h1:tD8HiFKnt0jnwdTWjeqUnfnUYLD/+Nsmj8ZGIxqDWiU=
|
||||
github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28/go.mod h1:Mu8i0/DdplvnjwRbAYPsc8+LRR27n/mp8VWdkN10GzE=
|
||||
github.com/oam-dev/terraform-controller v0.7.8 h1:4Kss5EnMljKecw8o2aPCpZdOTP1L1ODgf8BjmgN/cMI=
|
||||
github.com/oam-dev/terraform-controller v0.7.8/go.mod h1:83eY+m0ehYAnQytilG9TVjuCgDQWl3+pnE6oIJ9EujE=
|
||||
github.com/oam-dev/terraform-controller v0.7.10 h1:e2STz6Od53S4Ra4+QQs65lujF50vIb6eQtTMuvvofjk=
|
||||
github.com/oam-dev/terraform-controller v0.7.10/go.mod h1:xvgChKG0pij0WEKRrX7w30SdVBPVOlRl/+Mv7+2C1cI=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
@@ -2300,8 +2300,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -2319,8 +2319,8 @@ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 h1:+jnHzr9VPj32ykQVai5DNahi9+NSp7yYuCsl5eAQtL0=
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY=
|
||||
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
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=
|
||||
@@ -2484,8 +2484,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -2494,8 +2494,8 @@ golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuX
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180805044716-cb6730876b98/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -2507,8 +2507,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -2813,8 +2813,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/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=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
@@ -2969,8 +2970,8 @@ k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI=
|
||||
k8s.io/apiserver v0.22.6/go.mod h1:OlL1rGa2kKWGj2JEXnwBcul/BwC9Twe95gm4ohtiIIs=
|
||||
k8s.io/apiserver v0.25.3 h1:m7+xGuG5+KYAnEsqaFtDyWMkmMMEOFYlu+NlWv5qSBI=
|
||||
k8s.io/apiserver v0.25.3/go.mod h1:9bT47iM2fzRuhICJpM/RcQR9sqDDfZ7Yw60h0p3JW08=
|
||||
k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc=
|
||||
k8s.io/cli-runtime v0.25.2/go.mod h1:OQx3+/0st6x5YpkkJQlEWLC73V0wHsOFMC1/roxV8Oc=
|
||||
k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8=
|
||||
k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4=
|
||||
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k=
|
||||
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
|
||||
k8s.io/client-go v0.0.0-20191122220542-ed16ecbdf3a0/go.mod h1:tyxNgOmR/Xi39HrlQ/9LQgiHJgBvmY7gp95o5GpBA4o=
|
||||
@@ -3063,11 +3064,11 @@ k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2R
|
||||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA=
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU=
|
||||
k8s.io/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA=
|
||||
k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg=
|
||||
k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U=
|
||||
k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/metrics v0.25.2 h1:105TuPaIFfr4EHzN56WwZJO7r1UesuDytNTzeMqGySo=
|
||||
k8s.io/metrics v0.25.2/go.mod h1:4NDAauOuEJ+NWO2+hWkhFE4rWBx/plLWJOYU3vGl0sA=
|
||||
k8s.io/metrics v0.25.3 h1:fp5RuALkbwI3UbKITdNYu6sa3LF4JPANR/ofq3oe+Fg=
|
||||
k8s.io/metrics v0.25.3/go.mod h1:5j5FKJb8RHsb3Q2PLsD/p1mLiA1fTrl+a62Les+KDhc=
|
||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
|
||||
@@ -387,7 +387,10 @@ func (c *clusterServiceImpl) DeleteKubeCluster(ctx context.Context, clusterName
|
||||
cluster, err := c.getClusterFromDataStore(ctx, clusterName)
|
||||
if err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrClusterNotFoundInDataStore
|
||||
if err = multicluster.DetachCluster(ctx, c.K8sClient, clusterName); err != nil {
|
||||
return nil, bcode.ErrClusterNotFoundInDataStore
|
||||
}
|
||||
return &apis.ClusterBase{Name: clusterName}, nil
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed to found cluster %s in data store", clusterName)
|
||||
}
|
||||
|
||||
@@ -98,6 +98,27 @@ var _ = Describe("Test cluster service function", func() {
|
||||
Expect(err).Should(Equal(bcode.ErrClusterNotFoundInDataStore))
|
||||
})
|
||||
|
||||
It("Test delete kube cluster", func() {
|
||||
service := clusterServiceImpl{
|
||||
Store: ds,
|
||||
caches: cache,
|
||||
K8sClient: k8sClient,
|
||||
}
|
||||
Expect(createClusterSecret("prism-cluster", "prism-alias")).Should(Succeed())
|
||||
Expect(ds.Add(ctx, &model.Cluster{Name: "prism-cluster", Alias: "prism-alias", Icon: "prism-icon"})).Should(Succeed())
|
||||
resp, err := service.DeleteKubeCluster(ctx, "prism-cluster")
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(resp.Name).Should(Equal("prism-cluster"))
|
||||
Expect(resp.Alias).Should(Equal("prism-alias"))
|
||||
Expect(resp.Icon).Should(Equal("prism-icon"))
|
||||
_, err = service.DeleteKubeCluster(ctx, "non-exist-cluster")
|
||||
Expect(err).Should(Equal(bcode.ErrClusterNotFoundInDataStore))
|
||||
Expect(createClusterSecret("secret-exist-cm-non-exist-cluster", "secret-exist-cm-non-exist-cluster")).Should(Succeed())
|
||||
resp, err = service.DeleteKubeCluster(ctx, "secret-exist-cm-non-exist-cluster")
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(resp.Name).Should(Equal("secret-exist-cm-non-exist-cluster"))
|
||||
})
|
||||
|
||||
It("Test list kube clusters", func() {
|
||||
service := clusterServiceImpl{
|
||||
Store: ds,
|
||||
|
||||
@@ -91,7 +91,7 @@ template: {
|
||||
|
||||
parameter: {
|
||||
//+usage=The name of Terraform Provider for Alibaba Cloud
|
||||
name: *"default" | string
|
||||
name: string
|
||||
//+usage=Get ALICLOUD_ACCESS_KEY per this guide https://help.aliyun.com/knowledge_detail/38738.html
|
||||
ALICLOUD_ACCESS_KEY: string
|
||||
//+usage=Get ALICLOUD_SECRET_KEY per this guide https://help.aliyun.com/knowledge_detail/38738.html
|
||||
@@ -144,7 +144,7 @@ var _ = Describe("Test config service", func() {
|
||||
Expect(err).ToNot(BeNil())
|
||||
var paramErr = &script.ParameterError{}
|
||||
Expect(errors.As(err, ¶mErr)).To(Equal(true))
|
||||
Expect(paramErr.Name).To(Equal("ALICLOUD_ACCESS_KEY"))
|
||||
Expect(paramErr.Name).To(Equal("name"))
|
||||
Expect(paramErr.Message).To(Equal("This parameter is required"))
|
||||
|
||||
config, err := configService.CreateConfig(context.TODO(), "", v1.CreateConfigRequest{
|
||||
|
||||
@@ -45,8 +45,9 @@ func NewHelmService() HelmService {
|
||||
type HelmService interface {
|
||||
ListChartNames(ctx context.Context, url string, secretName string, skipCache bool) ([]string, error)
|
||||
ListChartVersions(ctx context.Context, url string, chartName string, secretName string, skipCache bool) (repo.ChartVersions, error)
|
||||
GetChartValues(ctx context.Context, url string, chartName string, version string, secretName string, skipCache bool) (map[string]interface{}, error)
|
||||
ListChartValuesFiles(ctx context.Context, url string, chartName string, version string, secretName string, repoType string, skipCache bool) (map[string]string, error)
|
||||
ListChartRepo(ctx context.Context, projectName string) (*v1.ChartRepoResponseList, error)
|
||||
GetChartValues(ctx context.Context, repoURL string, chartName string, version string, secretName string, repoType string, skipCache bool) (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
type defaultHelmImpl struct {
|
||||
@@ -99,7 +100,7 @@ func (d defaultHelmImpl) ListChartVersions(ctx context.Context, repoURL string,
|
||||
return chartVersions, nil
|
||||
}
|
||||
|
||||
func (d defaultHelmImpl) GetChartValues(ctx context.Context, repoURL string, chartName string, version string, secretName string, skipCache bool) (map[string]interface{}, error) {
|
||||
func (d defaultHelmImpl) ListChartValuesFiles(ctx context.Context, repoURL string, chartName string, version string, secretName string, repoType string, skipCache bool) (map[string]string, error) {
|
||||
if !utils.IsValidURL(repoURL) {
|
||||
return nil, bcode.ErrRepoInvalidURL
|
||||
}
|
||||
@@ -111,13 +112,33 @@ func (d defaultHelmImpl) GetChartValues(ctx context.Context, repoURL string, cha
|
||||
return nil, bcode.ErrRepoBasicAuth
|
||||
}
|
||||
}
|
||||
v, err := d.helper.GetValuesFromChart(repoURL, chartName, version, skipCache, opts)
|
||||
v, err := d.helper.GetValuesFromChart(repoURL, chartName, version, skipCache, repoType, opts)
|
||||
if err != nil {
|
||||
klog.Errorf("cannot fetch chart values repo: %s, chart: %s, version: %s, error: %s", utils.Sanitize(repoURL), utils.Sanitize(chartName), utils.Sanitize(version), err.Error())
|
||||
return nil, bcode.ErrGetChartValues
|
||||
}
|
||||
res := make(map[string]interface{}, len(v))
|
||||
flattenKey("", v, res)
|
||||
return v.Data, nil
|
||||
}
|
||||
|
||||
func (d defaultHelmImpl) GetChartValues(ctx context.Context, repoURL string, chartName string, version string, secretName string, repoType string, skipCache bool) (map[string]interface{}, error) {
|
||||
if !utils.IsValidURL(repoURL) {
|
||||
return nil, bcode.ErrRepoInvalidURL
|
||||
}
|
||||
var opts *common.HTTPOption
|
||||
var err error
|
||||
if len(secretName) != 0 {
|
||||
opts, err = helm.SetHTTPOption(ctx, d.K8sClient, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: secretName})
|
||||
if err != nil {
|
||||
return nil, bcode.ErrRepoBasicAuth
|
||||
}
|
||||
}
|
||||
v, err := d.helper.GetValuesFromChart(repoURL, chartName, version, skipCache, repoType, opts)
|
||||
if err != nil {
|
||||
klog.Errorf("cannot fetch chart values repo: %s, chart: %s, version: %s, error: %s", utils.Sanitize(repoURL), utils.Sanitize(chartName), utils.Sanitize(version), err.Error())
|
||||
return nil, bcode.ErrGetChartValues
|
||||
}
|
||||
res := make(map[string]interface{}, len(v.Values))
|
||||
flattenKey("", v.Values, res)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ var _ = Describe("test helm usecasae", func() {
|
||||
Expect(len(versions)).Should(BeEquivalentTo(1))
|
||||
Expect(versions[0].Version).Should(BeEquivalentTo("8.8.23"))
|
||||
|
||||
values, err := u.GetChartValues(ctx, mockServer.URL, "mysql", "8.8.23", "repo-secret", false)
|
||||
values, err := u.ListChartValuesFiles(ctx, mockServer.URL, "mysql", "8.8.23", "repo-secret", "helm", false)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(values).ShouldNot(BeNil())
|
||||
Expect(len(values)).ShouldNot(BeEquivalentTo(0))
|
||||
@@ -228,7 +228,7 @@ var _ = Describe("test helm usecasae", func() {
|
||||
_, err = u.ListChartVersions(ctx, "http://127.0.0.1:8080", "mysql", "repo-secret-notExist", false)
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
|
||||
_, err = u.GetChartValues(ctx, "http://127.0.0.1:8080", "mysql", "8.8.23", "repo-secret-notExist", false)
|
||||
_, err = u.ListChartValuesFiles(ctx, "http://127.0.0.1:8080", "mysql", "8.8.23", "repo-secret-notExist", "helm", false)
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -259,7 +259,9 @@ func managePrivilegesForTarget(ctx context.Context, cli client.Client, target *m
|
||||
f, msg = auth.RevokePrivileges, "RevokePrivileges"
|
||||
}
|
||||
if err := f(ctx, cli, []auth.PrivilegeDescription{p}, identity, writer); err != nil {
|
||||
return err
|
||||
klog.Warningf("error encountered for %s: %s", msg, err.Error())
|
||||
// for some cluster, authn/authz is not supported, ignore errors
|
||||
return client.IgnoreNotFound(err)
|
||||
}
|
||||
klog.Infof("%s: %s", msg, writer.String())
|
||||
return nil
|
||||
|
||||
@@ -18,6 +18,7 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// guaranteePolicyExist check the slice whether contain the target policy, if not put it in.
|
||||
@@ -57,16 +58,36 @@ func extractPolicyListAndProperty(property map[string]interface{}) ([]string, ma
|
||||
if policies == nil {
|
||||
return nil, property, nil
|
||||
}
|
||||
list, ok := policies.([]interface{})
|
||||
if !ok {
|
||||
list, err := InterfaceSlice(policies)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("the policies incorrect")
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return nil, property, nil
|
||||
}
|
||||
res := []string{}
|
||||
var res []string
|
||||
for _, i := range list {
|
||||
res = append(res, i.(string))
|
||||
}
|
||||
return res, property, nil
|
||||
}
|
||||
|
||||
// InterfaceSlice interface to []interface{}
|
||||
func InterfaceSlice(slice interface{}) ([]interface{}, error) {
|
||||
if arr, ok := slice.([]interface{}); ok {
|
||||
return arr, nil
|
||||
}
|
||||
s := reflect.ValueOf(slice)
|
||||
if s.Kind() != reflect.Slice {
|
||||
return nil, fmt.Errorf("InterfaceSlice() given a non-slice type")
|
||||
}
|
||||
// Keep the distinction between nil and empty slice input
|
||||
if s.IsNil() {
|
||||
return nil, nil
|
||||
}
|
||||
ret := make([]interface{}, s.Len())
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
ret[i] = s.Index(i).Interface()
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -174,61 +174,87 @@ func TestExtractPolicyListAndProperty(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
res struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noError bool
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noUnmarshallError bool
|
||||
noExtractError bool
|
||||
}
|
||||
}{
|
||||
{
|
||||
input: `{"policies": null, "components": null}`,
|
||||
res: struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noUnmarshallError bool
|
||||
noExtractError bool
|
||||
}{policies: nil, properties: map[string]interface{}{
|
||||
"policies": nil,
|
||||
"components": nil,
|
||||
}, noUnmarshallError: true, noExtractError: true},
|
||||
},
|
||||
{
|
||||
input: `{"policies": "policy1", "components": "comp1"}`,
|
||||
res: struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noUnmarshallError bool
|
||||
noExtractError bool
|
||||
}{policies: nil, properties: nil, noUnmarshallError: true, noExtractError: false},
|
||||
},
|
||||
{
|
||||
input: `{"policies":["policy1","policy2"], "components": ["comp1"]}`,
|
||||
res: struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noError bool
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noUnmarshallError bool
|
||||
noExtractError bool
|
||||
}{policies: []string{"policy1", "policy2"}, properties: map[string]interface{}{
|
||||
"policies": []interface{}{"policy1", "policy2"},
|
||||
"components": []interface{}{"comp1"},
|
||||
}, noError: true},
|
||||
}, noUnmarshallError: true, noExtractError: true},
|
||||
},
|
||||
{
|
||||
input: `{"policies":["policy1"], "components": ["comp1"]}`,
|
||||
res: struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noError bool
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noUnmarshallError bool
|
||||
noExtractError bool
|
||||
}{policies: []string{"policy1"}, properties: map[string]interface{}{
|
||||
"policies": []interface{}{"policy1"},
|
||||
"components": []interface{}{"comp1"},
|
||||
}, noError: true},
|
||||
}, noUnmarshallError: true, noExtractError: true},
|
||||
},
|
||||
{
|
||||
input: `{"policies":["policy1", "components": ["comp1"]}`,
|
||||
res: struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noError bool
|
||||
}{noError: false},
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noUnmarshallError bool
|
||||
noExtractError bool
|
||||
}{noUnmarshallError: false, noExtractError: false},
|
||||
},
|
||||
{
|
||||
input: `{}`,
|
||||
res: struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noError bool
|
||||
}{policies: nil, properties: nil, noError: true},
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
noUnmarshallError bool
|
||||
noExtractError bool
|
||||
}{policies: nil, properties: nil, noUnmarshallError: true, noExtractError: true},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
var in = map[string]interface{}{}
|
||||
err := json.Unmarshal([]byte(testCase.input), &in)
|
||||
if testCase.res.noError {
|
||||
if testCase.res.noUnmarshallError {
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.Equal(t, err != nil, true)
|
||||
continue
|
||||
}
|
||||
policy, properties, err := extractPolicyListAndProperty(in)
|
||||
if testCase.res.noError {
|
||||
if testCase.res.noExtractError {
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.Equal(t, err != nil, true)
|
||||
|
||||
@@ -190,7 +190,7 @@ func (c *CR2UX) generateEnv(ctx context.Context, defaultProject string, envNames
|
||||
env.Targets = append(env.Targets, name)
|
||||
}
|
||||
}
|
||||
return env, "", nil
|
||||
return env, env.Project, nil
|
||||
}
|
||||
|
||||
// generate new environment
|
||||
|
||||
@@ -217,6 +217,47 @@ var _ = Describe("Test CR convert to ux", func() {
|
||||
Expect(count).Should(Equal(int64(2)))
|
||||
})
|
||||
|
||||
It("Test to sync the project which existed env belongs", func() {
|
||||
dbNamespace := "update-app-db-ns1-test"
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: dbNamespace})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
cr2ux := newCR2UX(ds)
|
||||
|
||||
projectName := "project-test"
|
||||
|
||||
_, err = cr2ux.projectService.CreateProject(context.TODO(), v1.CreateProjectRequest{
|
||||
Name: projectName,
|
||||
Owner: "admin",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = cr2ux.targetService.CreateTarget(context.TODO(), v1.CreateTargetRequest{
|
||||
Name: "target-test1",
|
||||
Project: projectName,
|
||||
Cluster: &v1.ClusterTarget{
|
||||
ClusterName: "local",
|
||||
Namespace: "target-test1",
|
||||
},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = cr2ux.envService.CreateEnv(context.TODO(), v1.CreateEnvRequest{
|
||||
Name: "env-test1",
|
||||
Project: projectName,
|
||||
Namespace: "env-test1",
|
||||
Targets: []string{"target-test1"},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
app5 := &v1beta1.Application{}
|
||||
Expect(common2.ReadYamlToObject("testdata/test-app5.yaml", app5)).Should(BeNil())
|
||||
app5.Namespace = "env-test1"
|
||||
Expect(cr2ux.AddOrUpdate(context.Background(), app5)).Should(BeNil())
|
||||
|
||||
app, err := cr2ux.applicationService.GetApplication(context.TODO(), app5.Name)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(app.Project).Should(Equal("project-test"))
|
||||
})
|
||||
})
|
||||
|
||||
func newCR2UX(ds datastore.DataStore) *CR2UX {
|
||||
|
||||
16
pkg/apiserver/event/sync/testdata/test-app5.yaml
vendored
Normal file
16
pkg/apiserver/event/sync/testdata/test-app5.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: example-sync-project
|
||||
spec:
|
||||
components:
|
||||
- name: example-exist-env
|
||||
type: webservice
|
||||
properties:
|
||||
image: wordpress
|
||||
traits:
|
||||
- type: gateway
|
||||
properties:
|
||||
domain: testsvc.example.com
|
||||
http:
|
||||
"/": 8000
|
||||
@@ -234,7 +234,7 @@ func (n *pipeline) GetWebServiceRoute() *restful.WebService {
|
||||
Doc("list pipelines").
|
||||
Param(ws.QueryParameter("query", "Fuzzy search based on name or description").DataType("string")).
|
||||
Param(ws.QueryParameter("projectName", "query pipelines within a project").DataType("string")).
|
||||
Param(ws.QueryParameter("detailed", "query pipelines with detail").DataType("bool").DefaultValue("true")).
|
||||
Param(ws.QueryParameter("detailed", "query pipelines with detail").DataType("boolean").DefaultValue("true")).
|
||||
Returns(200, "OK", apis.ListPipelineResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ListPipelineResponse{}).Do(meta))
|
||||
|
||||
@@ -69,9 +69,19 @@ func (h repository) GetWebServiceRoute() *restful.WebService {
|
||||
Writes([]string{}))
|
||||
|
||||
// List available chart versions
|
||||
ws.Route(ws.GET("/charts/{chart}/versions").To(h.listVersions).
|
||||
ws.Route(ws.GET("/chart/versions").To(h.listVersionsFromQuery).
|
||||
Doc("list versions").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("chart", "helm chart").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("repoUrl", "helm repository url").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("secretName", "secret of the repo").DataType("string")).
|
||||
Returns(200, "OK", v1.ChartVersionListResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
|
||||
ws.Route(ws.GET("/charts/{chart}/versions").To(h.listChartVersions).
|
||||
Doc("list versions").Deprecate().
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("repoUrl", "helm repository url").DataType("string")).
|
||||
Param(ws.QueryParameter("secretName", "secret of the repo").DataType("string")).
|
||||
Returns(200, "OK", v1.ChartVersionListResponse{}).
|
||||
@@ -79,14 +89,26 @@ func (h repository) GetWebServiceRoute() *restful.WebService {
|
||||
Writes([]string{}))
|
||||
|
||||
// List available chart versions
|
||||
ws.Route(ws.GET("/charts/{chart}/versions/{version}/values").To(h.chartValues).
|
||||
ws.Route(ws.GET("/chart/values").To(h.chartValues).
|
||||
Doc("get chart value").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("chart", "helm chart").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("version", "helm chart version").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("repoUrl", "helm repository url").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("repoType", "helm repository type").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("secretName", "secret of the repo").DataType("string")).
|
||||
Returns(200, "OK", "").
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(map[string]string{}))
|
||||
|
||||
ws.Route(ws.GET("/charts/{chart}/versions/{version}/values").To(h.getChartValues).
|
||||
Doc("get chart value").Deprecate().
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("repoUrl", "helm repository url").DataType("string")).
|
||||
Param(ws.QueryParameter("secretName", "secret of the repo").DataType("string")).
|
||||
Returns(200, "OK", map[string]interface{}{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
Writes(map[string]interface{}{}))
|
||||
|
||||
ws.Route(ws.GET("/image/repos").To(h.getImageRepos).
|
||||
Doc("get the oci repos").
|
||||
@@ -132,9 +154,9 @@ func (h repository) listCharts(req *restful.Request, res *restful.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h repository) listVersions(req *restful.Request, res *restful.Response) {
|
||||
func (h repository) listVersionsFromQuery(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
chartName := req.PathParameter("chart")
|
||||
chartName := req.QueryParameter("chart")
|
||||
secName := req.QueryParameter("secretName")
|
||||
skipCache, err := isSkipCache(req)
|
||||
if err != nil {
|
||||
@@ -154,7 +176,7 @@ func (h repository) listVersions(req *restful.Request, res *restful.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h repository) chartValues(req *restful.Request, res *restful.Response) {
|
||||
func (h repository) getChartValues(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
secName := req.QueryParameter("secretName")
|
||||
chartName := req.PathParameter("chart")
|
||||
@@ -165,12 +187,57 @@ func (h repository) chartValues(req *restful.Request, res *restful.Response) {
|
||||
return
|
||||
}
|
||||
|
||||
versions, err := h.HelmService.GetChartValues(req.Request.Context(), url, chartName, version, secName, skipCache)
|
||||
values, err := h.HelmService.GetChartValues(req.Request.Context(), url, chartName, version, secName, "helm", skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(versions)
|
||||
err = res.WriteEntity(values)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h repository) listChartVersions(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
chartName := req.PathParameter("chart")
|
||||
secName := req.QueryParameter("secretName")
|
||||
skipCache, err := isSkipCache(req)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, bcode.ErrSkipCacheParameter)
|
||||
return
|
||||
}
|
||||
versions, err := h.HelmService.ListChartVersions(req.Request.Context(), url, chartName, secName, skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(v1.ChartVersionListResponse{Versions: versions})
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h repository) chartValues(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
secName := req.QueryParameter("secretName")
|
||||
chartName := req.QueryParameter("chart")
|
||||
version := req.QueryParameter("version")
|
||||
repoType := req.QueryParameter("repoType")
|
||||
skipCache, err := isSkipCache(req)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, bcode.ErrSkipCacheParameter)
|
||||
return
|
||||
}
|
||||
|
||||
values, err := h.HelmService.ListChartValuesFiles(req.Request.Context(), url, chartName, version, secName, repoType, skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(values)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
|
||||
@@ -69,7 +69,7 @@ func ValidatePayloadType(fl validator.FieldLevel) bool {
|
||||
// ValidateName custom check name field
|
||||
func ValidateName(fl validator.FieldLevel) bool {
|
||||
value := fl.Field().String()
|
||||
if len(value) > 32 || len(value) < 2 {
|
||||
if len(value) > 31 || len(value) < 2 {
|
||||
return false
|
||||
}
|
||||
return nameRegexp.MatchString(value)
|
||||
|
||||
@@ -23,21 +23,21 @@ var (
|
||||
// ErrNoConfigOrTarget means there is no target or config when creating the distribution.
|
||||
ErrNoConfigOrTarget = NewBcode(400, 16002, "you must specify the config name and destination to distribute")
|
||||
|
||||
// ErrConfigExist means the config is exist
|
||||
ErrConfigExist = NewBcode(400, 16003, "the config name is exist")
|
||||
// ErrConfigExist means the config does exist
|
||||
ErrConfigExist = NewBcode(400, 16003, "the config name does exist")
|
||||
|
||||
// ErrChangeTemplate the template of the config can not be change
|
||||
ErrChangeTemplate = NewBcode(400, 16004, "the template of the config can not be change")
|
||||
// ErrChangeTemplate the template of the config can not be changed
|
||||
ErrChangeTemplate = NewBcode(400, 16004, "the template of the config can not be changed")
|
||||
|
||||
// ErrTemplateNotFound means the template is not exist
|
||||
ErrTemplateNotFound = NewBcode(404, 16005, "the template is not exist")
|
||||
// ErrTemplateNotFound means the template does not exist
|
||||
ErrTemplateNotFound = NewBcode(404, 16005, "the template does not exist")
|
||||
|
||||
// ErrConfigNotFound means the config is not exist
|
||||
ErrConfigNotFound = NewBcode(404, 16006, "the config is not exist")
|
||||
// ErrConfigNotFound means the config does not exist
|
||||
ErrConfigNotFound = NewBcode(404, 16006, "the config does not exist")
|
||||
|
||||
// ErrNotFoundDistribution means the distribution is not exist
|
||||
ErrNotFoundDistribution = NewBcode(404, 16007, "the distribution is not exist")
|
||||
// ErrNotFoundDistribution means the distribution does not exist
|
||||
ErrNotFoundDistribution = NewBcode(404, 16007, "the distribution does not exist")
|
||||
|
||||
// ErrChangeSecretType the secret type of the config can not be change
|
||||
ErrChangeSecretType = NewBcode(400, 16008, "the secret type of the config can not be change")
|
||||
// ErrChangeSecretType the secret type of the config can not be changed
|
||||
ErrChangeSecretType = NewBcode(400, 16008, "the secret type of the config can not be changed")
|
||||
)
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/kubevela/workflow/pkg/cue/process"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
velaprocess "github.com/oam-dev/kubevela/pkg/cue/process"
|
||||
)
|
||||
@@ -32,7 +33,7 @@ func (p *Parser) ValidateCUESchematicAppfile(a *Appfile) error {
|
||||
for _, wl := range a.Workloads {
|
||||
// because helm & kube schematic has no CUE template
|
||||
// it only validates CUE schematic workload
|
||||
if wl.CapabilityCategory != types.CUECategory {
|
||||
if wl.CapabilityCategory != types.CUECategory || wl.Type == v1alpha1.RefObjectsComponentType {
|
||||
continue
|
||||
}
|
||||
ctxData := GenerateContextDataFromAppFile(a, wl.Name)
|
||||
|
||||
@@ -76,23 +76,23 @@ var ErrSensitiveConfig = errors.New("the config is sensitive")
|
||||
// ErrNoConfigOrTarget means the config or the target is empty.
|
||||
var ErrNoConfigOrTarget = errors.New("you must specify the config name and destination to distribute")
|
||||
|
||||
// ErrNotFoundDistribution means the app of the distribution is not exist.
|
||||
var ErrNotFoundDistribution = errors.New("the distribution is not found")
|
||||
// ErrNotFoundDistribution means the app of the distribution does not exist.
|
||||
var ErrNotFoundDistribution = errors.New("the distribution does not found")
|
||||
|
||||
// ErrConfigExist means the config is exist.
|
||||
var ErrConfigExist = errors.New("the config is exist")
|
||||
// ErrConfigExist means the config does exist.
|
||||
var ErrConfigExist = errors.New("the config does exist")
|
||||
|
||||
// ErrConfigNotFound means the config is not exist
|
||||
var ErrConfigNotFound = errors.New("the config is not exist")
|
||||
// ErrConfigNotFound means the config does not exist
|
||||
var ErrConfigNotFound = errors.New("the config does not exist")
|
||||
|
||||
// ErrTemplateNotFound means the template is not exist
|
||||
var ErrTemplateNotFound = errors.New("the template is not exist")
|
||||
// ErrTemplateNotFound means the template does not exist
|
||||
var ErrTemplateNotFound = errors.New("the template does not exist")
|
||||
|
||||
// ErrChangeTemplate means the template of the config can not be change
|
||||
var ErrChangeTemplate = errors.New("the template of the config can not be change")
|
||||
// ErrChangeTemplate means the template of the config can not be changed
|
||||
var ErrChangeTemplate = errors.New("the template of the config can not be changed")
|
||||
|
||||
// ErrChangeSecretType means the secret type of the config can not be change
|
||||
var ErrChangeSecretType = errors.New("the secret type of the config can not be change")
|
||||
// ErrChangeSecretType means the secret type of the config can not be changed
|
||||
var ErrChangeSecretType = errors.New("the secret type of the config can not be changed")
|
||||
|
||||
// NamespacedName the namespace and name model
|
||||
type NamespacedName struct {
|
||||
|
||||
@@ -94,7 +94,7 @@ var _ = Describe("Test the config provider", func() {
|
||||
`, nil, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = p.Create(mCtx, new(wfContext.WorkflowContext), v, nil)
|
||||
Expect(strings.Contains(err.Error(), "the template is not exist")).Should(BeTrue())
|
||||
Expect(strings.Contains(err.Error(), "the template does not exist")).Should(BeTrue())
|
||||
|
||||
template, err := p.factory.ParseTemplate("test-image-registry", []byte(templateContent))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -193,7 +193,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
app.Status.SetConditions(condition.ReadyCondition(common.PolicyCondition.String()))
|
||||
r.Recorder.Event(app, event.Normal(velatypes.ReasonPolicyGenerated, velatypes.MessagePolicyGenerated))
|
||||
|
||||
workflowInstance, runners, err := handler.GenerateApplicationSteps(logCtx, app, appParser, appFile, handler.currentAppRev)
|
||||
handler.CheckWorkflowRestart(logCtx, app)
|
||||
|
||||
workflowInstance, runners, err := handler.GenerateApplicationSteps(logCtx, app, appParser, appFile)
|
||||
if err != nil {
|
||||
logCtx.Error(err, "[handle workflow]")
|
||||
r.Recorder.Event(app, event.Warning(velatypes.ReasonFailedWorkflow, err))
|
||||
@@ -449,15 +451,12 @@ func (r *Reconciler) doWorkflowFinish(logCtx monitorContext.Context, app *v1beta
|
||||
wfContext.CleanupMemoryStore(app.Name, app.Namespace)
|
||||
t := time.Since(app.Status.Workflow.StartTime.Time).Seconds()
|
||||
metrics.WorkflowFinishedTimeHistogram.WithLabelValues(string(state)).Observe(t)
|
||||
switch state {
|
||||
case workflowv1alpha1.WorkflowStateSucceeded:
|
||||
if state == workflowv1alpha1.WorkflowStateSucceeded {
|
||||
app.Status.SetConditions(condition.ReadyCondition(common.WorkflowCondition.String()))
|
||||
r.Recorder.Event(app, event.Normal(velatypes.ReasonApplied, velatypes.MessageWorkflowFinished))
|
||||
handler.UpdateApplicationRevisionStatus(logCtx, handler.currentAppRev, true, app.Status.Workflow)
|
||||
logCtx.Info("Application manifests has applied by workflow successfully")
|
||||
default:
|
||||
handler.UpdateApplicationRevisionStatus(logCtx, handler.latestAppRev, false, app.Status.Workflow)
|
||||
}
|
||||
handler.UpdateApplicationRevisionStatus(logCtx, handler.currentAppRev, app.Status.Workflow)
|
||||
logCtx.Info("Application manifests has applied by workflow successfully")
|
||||
}
|
||||
|
||||
func hasHealthCheckPolicy(policies []*appfile.Workload) bool {
|
||||
|
||||
@@ -719,6 +719,76 @@ var _ = Describe("Test Application Controller", func() {
|
||||
}, appRevision)).Should(BeNil())
|
||||
})
|
||||
|
||||
It("revision should be updated if the workflow is restarted", func() {
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vela-test-app-restart-revision",
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, ns)).Should(BeNil())
|
||||
|
||||
app := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vela-test-app-restart-revision",
|
||||
Namespace: "vela-test-app-restart-revision",
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{},
|
||||
Workflow: &v1beta1.Workflow{
|
||||
Steps: []workflowv1alpha1.WorkflowStep{
|
||||
{
|
||||
WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
|
||||
Name: "suspend",
|
||||
Type: "suspend",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Create(ctx, app.DeepCopy())).Should(BeNil())
|
||||
|
||||
appKey := client.ObjectKey{
|
||||
Name: app.Name,
|
||||
Namespace: app.Namespace,
|
||||
}
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
By("Check Application Created with the correct revision")
|
||||
curApp := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil())
|
||||
Expect(curApp.Status.Phase).Should(Equal(common.ApplicationWorkflowSuspending))
|
||||
Expect(curApp.Status.LatestRevision).ShouldNot(BeNil())
|
||||
Expect(curApp.Status.LatestRevision.Revision).Should(BeEquivalentTo(1))
|
||||
|
||||
appRevision := &v1beta1.ApplicationRevision{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: app.Namespace,
|
||||
Name: curApp.Status.LatestRevision.Name,
|
||||
}, appRevision)).Should(BeNil())
|
||||
Expect(appRevision.Status.Workflow).Should(BeNil())
|
||||
|
||||
// update the app
|
||||
curApp.Spec.Workflow.Steps[0].DependsOn = []string{"invalid"}
|
||||
Expect(k8sClient.Update(ctx, curApp)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil())
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: app.Namespace,
|
||||
Name: curApp.Status.LatestRevision.Name,
|
||||
}, appRevision)).Should(BeNil())
|
||||
Expect(appRevision.Status.Workflow).ShouldNot(BeNil())
|
||||
Expect(appRevision.Status.Workflow.Finished).Should(BeTrue())
|
||||
Expect(appRevision.Status.Workflow.Terminated).Should(BeTrue())
|
||||
Expect(appRevision.Status.Workflow.EndTime.IsZero()).ShouldNot(BeTrue())
|
||||
Expect(appRevision.Status.Workflow.Phase).Should(Equal(workflowv1alpha1.WorkflowStateSuspending))
|
||||
|
||||
By("Delete Application, clean the resource")
|
||||
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
|
||||
})
|
||||
|
||||
It("revision should exist in created workload render by context.appRevision", func() {
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
|
||||
@@ -85,9 +85,9 @@ var (
|
||||
func (h *AppHandler) GenerateApplicationSteps(ctx monitorContext.Context,
|
||||
app *v1beta1.Application,
|
||||
appParser *appfile.Parser,
|
||||
af *appfile.Appfile,
|
||||
appRev *v1beta1.ApplicationRevision) (*wfTypes.WorkflowInstance, []wfTypes.TaskRunner, error) {
|
||||
af *appfile.Appfile) (*wfTypes.WorkflowInstance, []wfTypes.TaskRunner, error) {
|
||||
|
||||
appRev := h.currentAppRev
|
||||
appLabels := map[string]string{
|
||||
oam.LabelAppName: app.Name,
|
||||
oam.LabelAppNamespace: app.Namespace,
|
||||
@@ -118,7 +118,7 @@ func (h *AppHandler) GenerateApplicationSteps(ctx monitorContext.Context,
|
||||
})
|
||||
query.Install(handlerProviders, h.r.Client, nil)
|
||||
|
||||
instance := generateWorkflowInstance(af, app, appRev.Name)
|
||||
instance := generateWorkflowInstance(af, app)
|
||||
executor.InitializeWorkflowInstance(instance)
|
||||
runners, err := generator.GenerateRunners(ctx, instance, wfTypes.StepGeneratorOptions{
|
||||
Providers: handlerProviders,
|
||||
@@ -143,7 +143,7 @@ func (h *AppHandler) GenerateApplicationSteps(ctx monitorContext.Context,
|
||||
return instance, runners, nil
|
||||
}
|
||||
|
||||
// needRestart check if application workflow need restart and return the desired
|
||||
// CheckWorkflowRestart check if application workflow need restart and return the desired
|
||||
// rev to be set in status
|
||||
// 1. If workflow status is empty, it means no previous running record, the
|
||||
// workflow will restart (cold start)
|
||||
@@ -152,8 +152,8 @@ func (h *AppHandler) GenerateApplicationSteps(ctx monitorContext.Context,
|
||||
// 3. If workflow status is not empty, the desired rev will be the
|
||||
// ApplicationRevision name. For backward compatibility, the legacy style
|
||||
// <rev>:<hash> will be recognized and reduced into <rev>
|
||||
func needRestart(app *v1beta1.Application, revName string) (string, bool) {
|
||||
desiredRev, currentRev := revName, ""
|
||||
func (h *AppHandler) CheckWorkflowRestart(ctx monitorContext.Context, app *v1beta1.Application) {
|
||||
desiredRev, currentRev := h.currentAppRev.Name, ""
|
||||
if app.Status.Workflow != nil {
|
||||
currentRev = app.Status.Workflow.AppRevision
|
||||
}
|
||||
@@ -166,10 +166,40 @@ func needRestart(app *v1beta1.Application, revName string) (string, bool) {
|
||||
currentRev = currentRev[:idx]
|
||||
}
|
||||
}
|
||||
return desiredRev, currentRev == "" || desiredRev != currentRev
|
||||
if currentRev != "" && desiredRev == currentRev {
|
||||
return
|
||||
}
|
||||
// record in revision
|
||||
if h.latestAppRev != nil && h.latestAppRev.Status.Workflow == nil && app.Status.Workflow != nil {
|
||||
app.Status.Workflow.Terminated = true
|
||||
app.Status.Workflow.Finished = true
|
||||
if app.Status.Workflow.EndTime.IsZero() {
|
||||
app.Status.Workflow.EndTime = metav1.Now()
|
||||
}
|
||||
h.UpdateApplicationRevisionStatus(ctx, h.latestAppRev, app.Status.Workflow)
|
||||
}
|
||||
|
||||
// clean recorded resources info.
|
||||
app.Status.Services = nil
|
||||
app.Status.AppliedResources = nil
|
||||
|
||||
// clean conditions after render
|
||||
var reservedConditions []condition.Condition
|
||||
for i, cond := range app.Status.Conditions {
|
||||
condTpy, err := common.ParseApplicationConditionType(string(cond.Type))
|
||||
if err == nil {
|
||||
if condTpy <= common.RenderCondition {
|
||||
reservedConditions = append(reservedConditions, app.Status.Conditions[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
app.Status.Conditions = reservedConditions
|
||||
app.Status.Workflow = &common.WorkflowStatus{
|
||||
AppRevision: desiredRev,
|
||||
}
|
||||
}
|
||||
|
||||
func generateWorkflowInstance(af *appfile.Appfile, app *v1beta1.Application, appRev string) *wfTypes.WorkflowInstance {
|
||||
func generateWorkflowInstance(af *appfile.Appfile, app *v1beta1.Application) *wfTypes.WorkflowInstance {
|
||||
instance := &wfTypes.WorkflowInstance{
|
||||
WorkflowMeta: wfTypes.WorkflowMeta{
|
||||
Name: af.Name,
|
||||
@@ -191,27 +221,6 @@ func generateWorkflowInstance(af *appfile.Appfile, app *v1beta1.Application, app
|
||||
Steps: af.WorkflowSteps,
|
||||
Mode: af.WorkflowMode,
|
||||
}
|
||||
if desiredRev, nr := needRestart(app, appRev); nr {
|
||||
// clean recorded resources info.
|
||||
app.Status.Services = nil
|
||||
app.Status.AppliedResources = nil
|
||||
|
||||
// clean conditions after render
|
||||
var reservedConditions []condition.Condition
|
||||
for i, cond := range app.Status.Conditions {
|
||||
condTpy, err := common.ParseApplicationConditionType(string(cond.Type))
|
||||
if err == nil {
|
||||
if condTpy <= common.RenderCondition {
|
||||
reservedConditions = append(reservedConditions, app.Status.Conditions[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
app.Status.Conditions = reservedConditions
|
||||
app.Status.Workflow = &common.WorkflowStatus{
|
||||
AppRevision: desiredRev,
|
||||
}
|
||||
return instance
|
||||
}
|
||||
status := app.Status.Workflow
|
||||
instance.Status = workflowv1alpha1.WorkflowRunStatus{
|
||||
Mode: *af.WorkflowMode,
|
||||
|
||||
@@ -112,13 +112,15 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = af.GeneratePolicyManifests(context.Background())
|
||||
Expect(err).Should(BeNil())
|
||||
appRev := &oamcore.ApplicationRevision{}
|
||||
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
handler.currentAppRev = &oamcore.ApplicationRevision{}
|
||||
handler.CheckWorkflowRestart(logCtx, app)
|
||||
|
||||
_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(taskRunner)).Should(BeEquivalentTo(2))
|
||||
Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
|
||||
@@ -154,13 +156,14 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = af.GeneratePolicyManifests(context.Background())
|
||||
Expect(err).Should(BeNil())
|
||||
appRev := &oamcore.ApplicationRevision{}
|
||||
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
handler.currentAppRev = &oamcore.ApplicationRevision{}
|
||||
handler.CheckWorkflowRestart(logCtx, app)
|
||||
_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(taskRunner)).Should(BeEquivalentTo(2))
|
||||
Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
|
||||
@@ -276,13 +279,14 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
}
|
||||
af, err := appParser.GenerateAppFile(ctx, app)
|
||||
Expect(err).Should(BeNil())
|
||||
appRev := &oamcore.ApplicationRevision{}
|
||||
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
handler.currentAppRev = &oamcore.ApplicationRevision{}
|
||||
handler.CheckWorkflowRestart(logCtx, app)
|
||||
_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(taskRunner)).Should(BeEquivalentTo(2))
|
||||
Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
|
||||
@@ -317,13 +321,14 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
}
|
||||
af, err := appParser.GenerateAppFile(ctx, app)
|
||||
Expect(err).Should(BeNil())
|
||||
appRev := &oamcore.ApplicationRevision{}
|
||||
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
_, _, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
handler.currentAppRev = &oamcore.ApplicationRevision{}
|
||||
handler.CheckWorkflowRestart(logCtx, app)
|
||||
_, _, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af)
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
|
||||
@@ -355,13 +360,14 @@ var _ = Describe("Test Application workflow generator", func() {
|
||||
}
|
||||
af, err := appParser.GenerateAppFile(ctx, app)
|
||||
Expect(err).Should(BeNil())
|
||||
appRev := &oamcore.ApplicationRevision{}
|
||||
|
||||
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
logCtx := monitorContext.NewTraceContext(ctx, "")
|
||||
_, _, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af, appRev)
|
||||
handler.currentAppRev = &oamcore.ApplicationRevision{}
|
||||
handler.CheckWorkflowRestart(logCtx, app)
|
||||
_, _, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af)
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1019,11 +1019,11 @@ func (h historiesByComponentRevision) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
// UpdateApplicationRevisionStatus update application revision status
|
||||
func (h *AppHandler) UpdateApplicationRevisionStatus(ctx context.Context, appRev *v1beta1.ApplicationRevision, succeed bool, wfStatus *common.WorkflowStatus) {
|
||||
func (h *AppHandler) UpdateApplicationRevisionStatus(ctx context.Context, appRev *v1beta1.ApplicationRevision, wfStatus *common.WorkflowStatus) {
|
||||
if appRev == nil || DisableAllApplicationRevision {
|
||||
return
|
||||
}
|
||||
appRev.Status.Succeeded = succeed
|
||||
appRev.Status.Succeeded = wfStatus.Phase == workflowv1alpha1.WorkflowStateSucceeded
|
||||
appRev.Status.Workflow = wfStatus
|
||||
|
||||
// Versioned the context backend values.
|
||||
|
||||
@@ -231,7 +231,7 @@ func createOrGetExisting(ctx context.Context, act *applyAction, c client.Client,
|
||||
return nil, err
|
||||
}
|
||||
if act.readOnly {
|
||||
return nil, fmt.Errorf("cannot create %s (%s) in read-only mode", desired.GetObjectKind().GroupVersionKind().Kind, desired.GetName())
|
||||
return nil, fmt.Errorf("%s (%s) is marked as read-only but does not exist. You should check the existence of the resource or remove the read-only policy", desired.GetObjectKind().GroupVersionKind().Kind, desired.GetName())
|
||||
}
|
||||
if act.updateAnnotation {
|
||||
if err := addLastAppliedConfigAnnotation(desired); err != nil {
|
||||
|
||||
@@ -164,7 +164,7 @@ func getOriginalConfiguration(obj runtime.Object) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
original, ok := annots[oam.AnnotationLastAppliedConfig]
|
||||
if !ok {
|
||||
if !ok || original == "-" || original == "skip" {
|
||||
return nil, nil
|
||||
}
|
||||
return []byte(original), nil
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -32,6 +33,9 @@ import (
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
relutil "helm.sh/helm/v3/pkg/releaseutil"
|
||||
@@ -56,6 +60,12 @@ const (
|
||||
valuesPatten = "repoUrl: %s, chart: %s, version: %s"
|
||||
)
|
||||
|
||||
// ChartValues contain all values files in chart and default chart values
|
||||
type ChartValues struct {
|
||||
Data map[string]string
|
||||
Values map[string]interface{}
|
||||
}
|
||||
|
||||
// Helper provides helper functions for common Helm operations
|
||||
type Helper struct {
|
||||
cache *utils2.MemoryCacheStore
|
||||
@@ -309,12 +319,22 @@ func (h *Helper) ListChartsFromRepo(repoURL string, skipCache bool, opts *common
|
||||
}
|
||||
|
||||
// GetValuesFromChart will extract the parameter from a helm chart
|
||||
func (h *Helper) GetValuesFromChart(repoURL string, chartName string, version string, skipCache bool, opts *common.HTTPOption) (map[string]interface{}, error) {
|
||||
func (h *Helper) GetValuesFromChart(repoURL string, chartName string, version string, skipCache bool, repoType string, opts *common.HTTPOption) (*ChartValues, error) {
|
||||
if h.cache != nil && !skipCache {
|
||||
if v := h.cache.Get(fmt.Sprintf(valuesPatten, repoURL, chartName, version)); v != nil {
|
||||
return v.(map[string]interface{}), nil
|
||||
return v.(*ChartValues), nil
|
||||
}
|
||||
}
|
||||
if repoType == "oci" {
|
||||
v, err := fetchChartValuesFromOciRepo(repoURL, chartName, version, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if h.cache != nil {
|
||||
h.cache.Put(fmt.Sprintf(valuesPatten, repoURL, chartName, version), v, 20*time.Minute)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
i, err := h.GetIndexInfo(repoURL, skipCache, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -334,10 +354,17 @@ func (h *Helper) GetValuesFromChart(repoURL string, chartName string, version st
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if h.cache != nil {
|
||||
h.cache.Put(fmt.Sprintf(valuesPatten, repoURL, chartName, version), c.Values, calculateCacheTimeFromIndex(len(i.Entries)))
|
||||
v := &ChartValues{
|
||||
Data: loadValuesYamlFile(c),
|
||||
Values: c.Values,
|
||||
}
|
||||
return c.Values, nil
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if h.cache != nil {
|
||||
h.cache.Put(fmt.Sprintf(valuesPatten, repoURL, chartName, version), v, calculateCacheTimeFromIndex(len(i.Entries)))
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("cannot load chart from chart repo")
|
||||
}
|
||||
@@ -351,3 +378,49 @@ func calculateCacheTimeFromIndex(length int) time.Duration {
|
||||
}
|
||||
return cacheTime
|
||||
}
|
||||
|
||||
// nolint
|
||||
func fetchChartValuesFromOciRepo(repoURL string, chartName string, version string, opts *common.HTTPOption) (*ChartValues, error) {
|
||||
d := downloader.ChartDownloader{
|
||||
Verify: downloader.VerifyNever,
|
||||
Getters: getter.All(cli.New()),
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
d.Options = append(d.Options, getter.WithInsecureSkipVerifyTLS(opts.InsecureSkipTLS),
|
||||
getter.WithTLSClientConfig(opts.CertFile, opts.KeyFile, opts.CaFile),
|
||||
getter.WithBasicAuth(opts.Username, opts.Password))
|
||||
}
|
||||
|
||||
var err error
|
||||
dest, err := os.MkdirTemp("", "helm-")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to fetch values file")
|
||||
}
|
||||
defer os.RemoveAll(dest)
|
||||
|
||||
chartRef := fmt.Sprintf("%s/%s", repoURL, chartName)
|
||||
saved, _, err := d.DownloadTo(chartRef, version, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := loader.Load(saved)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to fetch values file")
|
||||
}
|
||||
return &ChartValues{
|
||||
Data: loadValuesYamlFile(c),
|
||||
Values: c.Values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func loadValuesYamlFile(chart *chart.Chart) map[string]string {
|
||||
result := map[string]string{}
|
||||
re := regexp.MustCompile(`.*yaml$`)
|
||||
for _, f := range chart.Raw {
|
||||
if re.MatchString(f.Name) && !strings.Contains(f.Name, "/") && f.Name != "Chart.yaml" {
|
||||
result[f.Name] = string(f.Data)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -114,9 +114,9 @@ var _ = Describe("Test helm helper", func() {
|
||||
|
||||
It("Test getValues from chart", func() {
|
||||
helper := NewHelper()
|
||||
values, err := helper.GetValuesFromChart("./testdata", "autoscalertrait", "0.2.0", true, nil)
|
||||
values, err := helper.GetValuesFromChart("./testdata", "autoscalertrait", "0.2.0", true, "helm", nil)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(values).ShouldNot(BeEmpty())
|
||||
Expect(values).ShouldNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -92,12 +92,26 @@ func MergeNoConflictLabels(labels map[string]string) MutateOption {
|
||||
// CreateOrUpdateNamespace will create a namespace if not exist, it will also update a namespace if exists
|
||||
// It will report an error if the labels conflict while it will override the annotations
|
||||
func CreateOrUpdateNamespace(ctx context.Context, kubeClient client.Client, name string, options ...MutateOption) error {
|
||||
err := CreateNamespace(ctx, kubeClient, name, options...)
|
||||
// only if namespace don't have the env label that we need to update it
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
return UpdateNamespace(ctx, kubeClient, name, options...)
|
||||
ns, err := GetNamespace(ctx, kubeClient, name)
|
||||
switch {
|
||||
case err == nil:
|
||||
return PatchNamespace(ctx, kubeClient, ns, options...)
|
||||
case apierrors.IsNotFound(err):
|
||||
return CreateNamespace(ctx, kubeClient, name, options...)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// PatchNamespace will patch a namespace
|
||||
func PatchNamespace(ctx context.Context, kubeClient client.Client, ns *corev1.Namespace, options ...MutateOption) error {
|
||||
original := ns.DeepCopy()
|
||||
for _, op := range options {
|
||||
if err := op(ns); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return kubeClient.Patch(ctx, ns, client.MergeFrom(original))
|
||||
}
|
||||
|
||||
// CreateNamespace will create a namespace with mutate option
|
||||
|
||||
@@ -108,6 +108,15 @@ func init() {
|
||||
}),
|
||||
GroupResourceType: GroupResourceType{Group: "apps", Kind: "DaemonSet"},
|
||||
},
|
||||
ChildrenResourcesRule{
|
||||
GroupResourceType: GroupResourceType{Group: "batch", Kind: "Job"},
|
||||
SubResources: buildSubResources([]*SubResourceSelector{
|
||||
{
|
||||
ResourceType: ResourceType{APIVersion: "v1", Kind: "Pod"},
|
||||
listOptions: defaultWorkloadLabelListOption,
|
||||
},
|
||||
}),
|
||||
},
|
||||
ChildrenResourcesRule{
|
||||
GroupResourceType: GroupResourceType{Group: "", Kind: "Service"},
|
||||
SubResources: buildSubResources([]*SubResourceSelector{
|
||||
|
||||
@@ -72,8 +72,8 @@ func NewAddAddonRegistryCommand(c common.Args, ioStreams cmdutil.IOStreams) *cob
|
||||
Short: "Add an addon registry.",
|
||||
Long: "Add an addon registry.",
|
||||
Example: `add a helm repo registry: vela addon registry add --type=helm my-repo --endpoint=<URL>
|
||||
add a github registry: vela addon registry add my-repo --type git --endpoint=<URL> --path=<ptah> --token=<git token>"
|
||||
add a gitlab registry: vela addon registry add my-repo --type gitlab --endpoint=<URL> --gitlabRepoName=<repoName> --path=<path> --token=<git token>`,
|
||||
add a github registry: vela addon registry add my-repo --type git --endpoint=<URL> --path=<ptah> --gitToken=<git token>"
|
||||
add a gitlab registry: vela addon registry add my-repo --type gitlab --endpoint=<URL> --gitlabRepoName=<repoName> --path=<path> --gitToken=<git token>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
registry, err := getRegistryFromArgs(cmd, args)
|
||||
if err != nil {
|
||||
|
||||
@@ -167,7 +167,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
addonArgs[pkgaddon.InstallerRuntimeOption] = map[string]interface{}{
|
||||
"upgrade": false,
|
||||
}
|
||||
|
||||
var addonName string
|
||||
if file, err := os.Stat(addonOrDir); err == nil {
|
||||
if !file.IsDir() {
|
||||
return fmt.Errorf("%s is not addon dir", addonOrDir)
|
||||
@@ -178,13 +178,13 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "directory %s is invalid", addonOrDir)
|
||||
}
|
||||
name = filepath.Base(abs)
|
||||
addonName = filepath.Base(abs)
|
||||
if !yes2all {
|
||||
if err := checkUninstallFromClusters(ctx, k8sClient, name, addonArgs); err != nil {
|
||||
if err := checkUninstallFromClusters(ctx, k8sClient, addonName, addonArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
additionalInfo, err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
|
||||
additionalInfo, err = enableAddonByLocal(ctx, addonName, addonOrDir, k8sClient, dc, config, addonArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -192,8 +192,12 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
if filepath.IsAbs(addonOrDir) || strings.HasPrefix(addonOrDir, ".") || strings.HasSuffix(addonOrDir, "/") {
|
||||
return fmt.Errorf("addon directory %s not found in local file system", addonOrDir)
|
||||
}
|
||||
_, addonName, err = splitSpecifyRegistry(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to split addonName and addonRegistry: %w", err)
|
||||
}
|
||||
if !yes2all {
|
||||
if err := checkUninstallFromClusters(ctx, k8sClient, name, addonArgs); err != nil {
|
||||
if err := checkUninstallFromClusters(ctx, k8sClient, addonName, addonArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -205,8 +209,8 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
if dryRun {
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("Addon %s enabled successfully.\n", name)
|
||||
AdditionalEndpointPrinter(ctx, c, k8sClient, name, additionalInfo, false)
|
||||
fmt.Printf("Addon %s enabled successfully.\n", addonName)
|
||||
AdditionalEndpointPrinter(ctx, c, k8sClient, addonName, additionalInfo, false)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -31,7 +32,9 @@ import (
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
apitypes "k8s.io/apimachinery/pkg/types"
|
||||
@@ -43,6 +46,8 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/kubevela/pkg/util/k8s"
|
||||
"github.com/kubevela/pkg/util/resourcetopology"
|
||||
velaslices "github.com/kubevela/pkg/util/slices"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
@@ -51,6 +56,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
velacmd "github.com/oam-dev/kubevela/pkg/cmd"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/env"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
@@ -70,6 +76,9 @@ const (
|
||||
//go:embed adopt-templates/default.cue
|
||||
var defaultAdoptTemplate string
|
||||
|
||||
//go:embed resource-topology/builtin-rule.cue
|
||||
var defaultResourceTopologyRule string
|
||||
|
||||
var (
|
||||
adoptTypes = []string{adoptTypeNative, adoptTypeHelm}
|
||||
adoptModes = []string{adoptModeReadOnly, adoptModeTakeOver}
|
||||
@@ -91,35 +100,49 @@ type AdoptOptions struct {
|
||||
HelmReleaseName string
|
||||
HelmReleaseNamespace string
|
||||
HelmDriver string
|
||||
HelmConfig *action.Configuration
|
||||
HelmStore *storage.Storage
|
||||
HelmRelease *release.Release
|
||||
HelmReleases []*release.Release
|
||||
HelmReleaseRevisions []*release.Release
|
||||
|
||||
NativeResourceRefs []*resourceRef
|
||||
|
||||
Apply bool
|
||||
Recycle bool
|
||||
Yes bool
|
||||
All bool
|
||||
|
||||
AdoptTemplateFile string
|
||||
AdoptTemplate string
|
||||
AdoptTemplateCUEValue cue.Value
|
||||
|
||||
ResourceTopologyRuleFile string
|
||||
ResourceTopologyRule string
|
||||
AllGVKs []schema.GroupVersionKind
|
||||
|
||||
Resources []*unstructured.Unstructured `json:"resources"`
|
||||
|
||||
util.IOStreams
|
||||
}
|
||||
|
||||
func (opt *AdoptOptions) parseResourceRef(f velacmd.Factory, cmd *cobra.Command, arg string) (*resourceRef, error) {
|
||||
parts := strings.Split(arg, "/")
|
||||
_, gr := schema.ParseResourceArg(parts[0])
|
||||
func (opt *AdoptOptions) parseResourceGVK(f velacmd.Factory, arg string) (schema.GroupVersionKind, error) {
|
||||
_, gr := schema.ParseResourceArg(arg)
|
||||
gvks, err := f.Client().RESTMapper().KindsFor(gr.WithVersion(""))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find types for resource %s: %w", arg, err)
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("failed to find types for resource %s: %w", arg, err)
|
||||
}
|
||||
if len(gvks) == 0 {
|
||||
return nil, fmt.Errorf("no schema found for resource %s: %w", arg, err)
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("no schema found for resource %s: %w", arg, err)
|
||||
}
|
||||
return gvks[0], nil
|
||||
}
|
||||
|
||||
func (opt *AdoptOptions) parseResourceRef(f velacmd.Factory, cmd *cobra.Command, arg string) (*resourceRef, error) {
|
||||
parts := strings.Split(arg, "/")
|
||||
gvk, err := opt.parseResourceGVK(f, parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
mappings, err := f.Client().RESTMapper().RESTMappings(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find mappings for resource %s: %w", arg, err)
|
||||
@@ -147,6 +170,186 @@ func (opt *AdoptOptions) parseResourceRef(f velacmd.Factory, cmd *cobra.Command,
|
||||
return or, nil
|
||||
}
|
||||
|
||||
// Init .
|
||||
func (opt *AdoptOptions) Init(f velacmd.Factory, cmd *cobra.Command, args []string) error {
|
||||
if opt.All {
|
||||
if len(args) > 0 {
|
||||
for _, arg := range args {
|
||||
gvk, err := opt.parseResourceGVK(f, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opt.AllGVKs = append(opt.AllGVKs, gvk)
|
||||
apiVersion, kind := gvk.ToAPIVersionAndKind()
|
||||
fmt.Fprintf(opt.Out, "Adopt all %s/%s resources\n", apiVersion, kind)
|
||||
}
|
||||
}
|
||||
if len(opt.AllGVKs) == 0 {
|
||||
opt.AllGVKs = []schema.GroupVersionKind{
|
||||
appsv1.SchemeGroupVersion.WithKind("Deployment"),
|
||||
appsv1.SchemeGroupVersion.WithKind("StatefulSet"),
|
||||
appsv1.SchemeGroupVersion.WithKind("DaemonSet"),
|
||||
}
|
||||
_, _ = opt.Out.Write([]byte("No arguments specified, adopt all Deployment/StatefulSet/DaemonSet resources by default\n"))
|
||||
}
|
||||
}
|
||||
if opt.AdoptTemplateFile != "" {
|
||||
bs, err := os.ReadFile(opt.AdoptTemplateFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load file %s", opt.AdoptTemplateFile)
|
||||
}
|
||||
opt.AdoptTemplate = string(bs)
|
||||
} else {
|
||||
opt.AdoptTemplate = defaultAdoptTemplate
|
||||
}
|
||||
if opt.ResourceTopologyRuleFile != "" {
|
||||
bs, err := os.ReadFile(opt.ResourceTopologyRuleFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load file %s", opt.ResourceTopologyRuleFile)
|
||||
}
|
||||
opt.ResourceTopologyRule = string(bs)
|
||||
} else {
|
||||
opt.ResourceTopologyRule = defaultResourceTopologyRule
|
||||
}
|
||||
opt.AppNamespace = velacmd.GetNamespace(f, cmd)
|
||||
opt.AdoptTemplateCUEValue = cuecontext.New().CompileString(fmt.Sprintf("%s\n\n%s: %s", opt.AdoptTemplate, adoptCUETempVal, adoptCUETempFunc))
|
||||
switch opt.Type {
|
||||
case adoptTypeNative:
|
||||
if opt.Recycle {
|
||||
return fmt.Errorf("native resource adoption does not support --recycle flag")
|
||||
}
|
||||
case adoptTypeHelm:
|
||||
if len(opt.HelmDriver) == 0 {
|
||||
opt.HelmDriver = os.Getenv(helmDriverEnvKey)
|
||||
}
|
||||
if len(opt.HelmDriver) == 0 {
|
||||
opt.HelmDriver = defaultHelmDriver
|
||||
}
|
||||
actionConfig := new(action.Configuration)
|
||||
opt.HelmReleaseNamespace = opt.AppNamespace
|
||||
if err := actionConfig.Init(
|
||||
util.NewRestConfigGetterByConfig(f.Config(), opt.HelmReleaseNamespace),
|
||||
opt.HelmReleaseNamespace,
|
||||
opt.HelmDriver,
|
||||
klog.Infof); err != nil {
|
||||
return err
|
||||
}
|
||||
opt.HelmConfig = actionConfig
|
||||
default:
|
||||
return fmt.Errorf("invalid adopt type: %s, available types: [%s]", opt.Type, strings.Join(adoptTypes, ", "))
|
||||
}
|
||||
if slices.Index(adoptModes, opt.Mode) < 0 {
|
||||
return fmt.Errorf("invalid adopt mode: %s, available modes: [%s]", opt.Mode, strings.Join(adoptModes, ", "))
|
||||
}
|
||||
if opt.Recycle && !opt.Apply {
|
||||
return fmt.Errorf("old data can only be recycled when the adoption application is applied")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultipleRun .
|
||||
func (opt *AdoptOptions) MultipleRun(f velacmd.Factory, cmd *cobra.Command) error {
|
||||
resources := make([][]*unstructured.Unstructured, 0)
|
||||
releases := make([]*release.Release, 0)
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
|
||||
matchLabels := metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: oam.LabelAppName,
|
||||
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
}
|
||||
selector, err := metav1.LabelSelectorAsSelector(&matchLabels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch opt.Type {
|
||||
case adoptTypeNative:
|
||||
for _, gvk := range opt.AllGVKs {
|
||||
list := &unstructured.UnstructuredList{}
|
||||
list.SetGroupVersionKind(gvk)
|
||||
if err := f.Client().List(ctx, list, &client.ListOptions{Namespace: opt.AppNamespace, LabelSelector: selector}); err != nil {
|
||||
apiVersion, kind := gvk.ToAPIVersionAndKind()
|
||||
_, _ = fmt.Fprintf(opt.Out, "Warning: failed to list resources from %s/%s: %s", apiVersion, kind, err.Error())
|
||||
continue
|
||||
}
|
||||
dedup := make([]k8s.ResourceIdentifier, 0)
|
||||
for _, item := range list.Items {
|
||||
engine := resourcetopology.New(opt.ResourceTopologyRule)
|
||||
itemIdentifier := k8s.ResourceIdentifier{
|
||||
Name: item.GetName(),
|
||||
Namespace: item.GetNamespace(),
|
||||
Kind: item.GetKind(),
|
||||
APIVersion: item.GetAPIVersion(),
|
||||
}
|
||||
if velaslices.Contains(dedup, itemIdentifier) {
|
||||
continue
|
||||
}
|
||||
firstElement := item
|
||||
r := []*unstructured.Unstructured{&firstElement}
|
||||
peers, err := engine.GetPeerResources(ctx, itemIdentifier)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(opt.Out, "Warning: failed to get peer resources for %s/%s: %s", itemIdentifier.APIVersion, itemIdentifier.Kind, err.Error())
|
||||
resources = append(resources, r)
|
||||
continue
|
||||
}
|
||||
dedup = append(dedup, peers...)
|
||||
for _, peer := range peers {
|
||||
gvk, err := k8s.GetGVKFromResource(peer)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(opt.Out, "Warning: failed to get gvk from resource %s/%s: %s", peer.APIVersion, peer.Kind, err.Error())
|
||||
continue
|
||||
}
|
||||
peerResource := &unstructured.Unstructured{}
|
||||
peerResource.SetGroupVersionKind(gvk)
|
||||
if err := f.Client().Get(ctx, apitypes.NamespacedName{Namespace: peer.Namespace, Name: peer.Name}, peerResource); err != nil {
|
||||
_, _ = fmt.Fprintf(opt.Out, "Warning: failed to get resource %s/%s: %s", peer.Namespace, peer.Name, err.Error())
|
||||
continue
|
||||
}
|
||||
r = append(r, peerResource)
|
||||
}
|
||||
resources = append(resources, r)
|
||||
}
|
||||
}
|
||||
case adoptTypeHelm:
|
||||
releases, err = opt.HelmConfig.Releases.List(func(release *release.Release) bool {
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, r := range resources {
|
||||
opt.Resources = r
|
||||
opt.AppName = r[0].GetName()
|
||||
opt.AppNamespace = r[0].GetNamespace()
|
||||
if err := opt.Run(f, cmd); err != nil {
|
||||
_, _ = fmt.Fprintf(opt.Out, "Error: failed to adopt %s/%s: %s", opt.AppNamespace, opt.AppName, err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, r := range releases {
|
||||
opt.AppName = r.Name
|
||||
opt.AppNamespace = r.Namespace
|
||||
opt.HelmReleaseName = r.Name
|
||||
opt.HelmReleaseNamespace = r.Namespace
|
||||
// TODO(fog): filter the helm that already adopted by vela
|
||||
if err := opt.loadHelm(); err != nil {
|
||||
_, _ = fmt.Fprintf(opt.Out, "Error: failed to load helm for %s/%s: %s", opt.AppNamespace, opt.AppName, err.Error())
|
||||
continue
|
||||
}
|
||||
if err := opt.Run(f, cmd); err != nil {
|
||||
_, _ = fmt.Fprintf(opt.Out, "Error: failed to adopt %s/%s: %s", opt.AppNamespace, opt.AppName, err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complete autofill fields in opts
|
||||
func (opt *AdoptOptions) Complete(f velacmd.Factory, cmd *cobra.Command, args []string) error {
|
||||
opt.AppNamespace = velacmd.GetNamespace(f, cmd)
|
||||
@@ -167,6 +370,9 @@ func (opt *AdoptOptions) Complete(f velacmd.Factory, cmd *cobra.Command, args []
|
||||
if opt.AppNamespace == "" {
|
||||
opt.AppNamespace = opt.NativeResourceRefs[0].Namespace
|
||||
}
|
||||
if err := opt.loadNative(f, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
case adoptTypeHelm:
|
||||
if len(args) > 0 {
|
||||
opt.HelmReleaseName = args[0]
|
||||
@@ -174,26 +380,27 @@ func (opt *AdoptOptions) Complete(f velacmd.Factory, cmd *cobra.Command, args []
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("helm type adoption only support one helm release by far")
|
||||
}
|
||||
if len(opt.HelmDriver) == 0 {
|
||||
opt.HelmDriver = os.Getenv(helmDriverEnvKey)
|
||||
}
|
||||
if len(opt.HelmDriver) == 0 {
|
||||
opt.HelmDriver = defaultHelmDriver
|
||||
}
|
||||
if opt.AppName == "" {
|
||||
opt.AppName = opt.HelmReleaseName
|
||||
}
|
||||
opt.HelmReleaseNamespace = opt.AppNamespace
|
||||
if err := opt.loadHelm(); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
}
|
||||
if opt.AdoptTemplateFile != "" {
|
||||
bs, err := os.ReadFile(opt.AdoptTemplateFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load file %s", opt.AdoptTemplateFile)
|
||||
if opt.AppName != "" {
|
||||
var ctx = context.Background()
|
||||
app := &v1beta1.Application{}
|
||||
err := f.Client().Get(ctx, apitypes.NamespacedName{Namespace: opt.AppNamespace, Name: opt.AppName}, app)
|
||||
if err == nil && app != nil {
|
||||
if !opt.Yes {
|
||||
userInput := NewUserInput()
|
||||
confirm := userInput.AskBool("Application '%s' already exists, apply will override the existing app with the adopted one, please confirm [Y/n]: "+opt.AppName, &UserInputOptions{AssumeYes: false})
|
||||
if !confirm {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
opt.AdoptTemplate = string(bs)
|
||||
} else {
|
||||
opt.AdoptTemplate = defaultAdoptTemplate
|
||||
}
|
||||
opt.AdoptTemplateCUEValue = cuecontext.New().CompileString(fmt.Sprintf("%s\n\n%s: %s", opt.AdoptTemplate, adoptCUETempVal, adoptCUETempFunc))
|
||||
return nil
|
||||
@@ -209,21 +416,10 @@ func (opt *AdoptOptions) Validate() error {
|
||||
if opt.AppName == "" {
|
||||
return fmt.Errorf("app-name flag must be set for native resource adoption when multiple resources have different names")
|
||||
}
|
||||
if opt.Recycle {
|
||||
return fmt.Errorf("native resource adoption does not support --recycle flag")
|
||||
}
|
||||
case adoptTypeHelm:
|
||||
if len(opt.HelmReleaseName) == 0 {
|
||||
return fmt.Errorf("helm release name must not be empty")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid adopt type: %s, available types: [%s]", opt.Type, strings.Join(adoptTypes, ", "))
|
||||
}
|
||||
if slices.Index(adoptModes, opt.Mode) < 0 {
|
||||
return fmt.Errorf("invalid adopt mode: %s, available modes: [%s]", opt.Mode, strings.Join(adoptModes, ", "))
|
||||
}
|
||||
if opt.Recycle && !opt.Apply {
|
||||
return fmt.Errorf("old data can only be recycled when the adoption application is applied")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -240,27 +436,18 @@ func (opt *AdoptOptions) loadNative(f velacmd.Factory, cmd *cobra.Command) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (opt *AdoptOptions) loadHelm(f velacmd.Factory) error {
|
||||
actionConfig := new(action.Configuration)
|
||||
err := actionConfig.Init(
|
||||
util.NewRestConfigGetterByConfig(f.Config(), opt.HelmReleaseNamespace),
|
||||
opt.HelmReleaseNamespace,
|
||||
opt.HelmDriver,
|
||||
klog.Infof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opt.HelmStore = actionConfig.Releases
|
||||
releases, err := opt.HelmStore.History(opt.HelmReleaseName)
|
||||
func (opt *AdoptOptions) loadHelm() error {
|
||||
opt.HelmStore = opt.HelmConfig.Releases
|
||||
revisions, err := opt.HelmStore.History(opt.HelmReleaseName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("helm release %s/%s not loaded: %w", opt.HelmReleaseNamespace, opt.HelmReleaseName, err)
|
||||
}
|
||||
if len(releases) == 0 {
|
||||
if len(revisions) == 0 {
|
||||
return fmt.Errorf("helm release %s/%s not found", opt.HelmReleaseNamespace, opt.HelmReleaseName)
|
||||
}
|
||||
releaseutil.SortByRevision(releases)
|
||||
opt.HelmRelease = releases[len(releases)-1]
|
||||
opt.HelmReleases = releases
|
||||
releaseutil.SortByRevision(revisions)
|
||||
opt.HelmRelease = revisions[len(revisions)-1]
|
||||
opt.HelmReleaseRevisions = revisions
|
||||
manifests := releaseutil.SplitManifests(opt.HelmRelease.Manifest)
|
||||
var objs []*unstructured.Unstructured
|
||||
for _, val := range manifests {
|
||||
@@ -285,22 +472,17 @@ func (opt *AdoptOptions) render() (*v1beta1.Application, error) {
|
||||
if err = json.Unmarshal(bs, app); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse template $returns into application: %w", err)
|
||||
}
|
||||
if app.Name == "" {
|
||||
app.Name = opt.AppName
|
||||
}
|
||||
if app.Namespace == "" {
|
||||
app.Namespace = opt.AppNamespace
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
// Run collect resources, assemble into application and print/apply
|
||||
func (opt *AdoptOptions) Run(f velacmd.Factory, cmd *cobra.Command) error {
|
||||
switch opt.Type {
|
||||
case adoptTypeNative:
|
||||
if err := opt.loadNative(f, cmd); err != nil {
|
||||
return fmt.Errorf("failed to load native resources: %w", err)
|
||||
}
|
||||
case adoptTypeHelm:
|
||||
if err := opt.loadHelm(f); err != nil {
|
||||
return fmt.Errorf("failed to load resources from helm release %s/%s: %w", opt.HelmReleaseNamespace, opt.HelmReleaseName, err)
|
||||
}
|
||||
default:
|
||||
}
|
||||
app, err := opt.render()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make adoption application for resources: %w", err)
|
||||
@@ -315,6 +497,9 @@ func (opt *AdoptOptions) Run(f velacmd.Factory, cmd *cobra.Command) error {
|
||||
if bs, err = yaml.Marshal(app); err != nil {
|
||||
return fmt.Errorf("failed to encode application into YAML format: %w", err)
|
||||
}
|
||||
if opt.All {
|
||||
_, _ = opt.Out.Write([]byte("\n---\n"))
|
||||
}
|
||||
_, _ = opt.Out.Write(bs)
|
||||
}
|
||||
if opt.Recycle && opt.Apply {
|
||||
@@ -335,7 +520,7 @@ func (opt *AdoptOptions) Run(f velacmd.Factory, cmd *cobra.Command) error {
|
||||
}
|
||||
switch opt.Type {
|
||||
case adoptTypeHelm:
|
||||
for _, r := range opt.HelmReleases {
|
||||
for _, r := range opt.HelmReleaseRevisions {
|
||||
if _, err = opt.HelmStore.Delete(r.Name, r.Version); err != nil {
|
||||
return fmt.Errorf("failed to clean up helm release: %w", err)
|
||||
}
|
||||
@@ -382,11 +567,21 @@ var (
|
||||
the command and make your own assemble rules for the adoption application. You can
|
||||
refer to https://github.com/kubevela/kubevela/blob/master/references/cli/adopt-templates/default.cue
|
||||
to see the default implementation of adoption rules.
|
||||
|
||||
If you want to adopt all resources with resource topology rule to Applications,
|
||||
you can use: 'vela adopt --all'. The resource topology rule can be customized by
|
||||
'--resource-topology-rule' flag.
|
||||
`))
|
||||
adoptExample = templates.Examples(i18n.T(`
|
||||
# Native Resources Adoption
|
||||
|
||||
## Adopt resources into new application
|
||||
|
||||
## Adopt all resources to Applications with resource topology rule
|
||||
## Use: vela adopt <resources-type> --all
|
||||
vela adopt --all
|
||||
vela adopt deployment --all --resource-topology-rule myrule.cue
|
||||
|
||||
## Use: vela adopt <resources-type>[/<resource-namespace>]/<resource-name> <resources-type>[/<resource-namespace>]/<resource-name> ...
|
||||
vela adopt deployment/my-app configmap/my-app
|
||||
|
||||
@@ -409,6 +604,11 @@ var (
|
||||
|
||||
# Helm Chart Adoption
|
||||
|
||||
## Adopt all helm releases to Applications with resource topology rule
|
||||
## Use: vela adopt <resources-type> --all
|
||||
vela adopt --all --type helm
|
||||
vela adopt my-chart --all --resource-topology-rule myrule.cue --type helm
|
||||
|
||||
## Adopt resources in a deployed helm chart
|
||||
vela adopt my-chart -n my-namespace --type helm
|
||||
|
||||
@@ -443,8 +643,12 @@ func NewAdoptCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandType: types.TypeCD,
|
||||
},
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Init(f, cmd, args))
|
||||
if o.All {
|
||||
cmdutil.CheckErr(o.MultipleRun(f, cmd))
|
||||
return
|
||||
}
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.Run(f, cmd))
|
||||
@@ -454,9 +658,12 @@ func NewAdoptCommand(f velacmd.Factory, streams util.IOStreams) *cobra.Command {
|
||||
cmd.Flags().StringVarP(&o.Mode, "mode", "m", o.Mode, fmt.Sprintf("The mode of adoption. Available values: [%s]", strings.Join(adoptModes, ", ")))
|
||||
cmd.Flags().StringVarP(&o.AppName, "app-name", "", o.AppName, "The name of application for adoption. If empty for helm type adoption, it will inherit the helm chart's name.")
|
||||
cmd.Flags().StringVarP(&o.AdoptTemplateFile, "adopt-template", "", o.AdoptTemplate, "The CUE template for adoption. If not provided, the default template will be used when --auto is switched on.")
|
||||
cmd.Flags().StringVarP(&o.ResourceTopologyRuleFile, "resource-topology-rule", "", o.ResourceTopologyRule, "The CUE template for specify the rule of the resource topology. If not provided, the default rule will be used.")
|
||||
cmd.Flags().StringVarP(&o.HelmDriver, "driver", "d", o.HelmDriver, "The storage backend of helm adoption. Only take effect when --type=helm.")
|
||||
cmd.Flags().BoolVarP(&o.Apply, "apply", "", o.Apply, "If true, the application for adoption will be applied. Otherwise, it will only be printed.")
|
||||
cmd.Flags().BoolVarP(&o.Recycle, "recycle", "", o.Recycle, "If true, when the adoption application is successfully applied, the old storage (like Helm secret) will be recycled.")
|
||||
cmd.Flags().BoolVarP(&o.Yes, "yes", "y", o.Yes, "Skip confirmation prompt")
|
||||
cmd.Flags().BoolVarP(&o.All, "all", "", o.All, "Adopt all resources in the namespace")
|
||||
return velacmd.NewCommandBuilder(f, cmd).
|
||||
WithNamespaceFlag().
|
||||
WithResponsiveWriter().
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -27,6 +28,11 @@ import (
|
||||
)
|
||||
|
||||
func TestDefaultNamespace(t *testing.T) {
|
||||
f := velacmd.NewDeferredFactory(config.GetConfig)
|
||||
ioStream := util.IOStreams{}
|
||||
ctx := context.Background()
|
||||
cmd := NewAdoptCommand(f, ioStream)
|
||||
cmd.SetContext(ctx)
|
||||
testcase := []struct {
|
||||
namespace string
|
||||
args []string
|
||||
@@ -46,10 +52,8 @@ func TestDefaultNamespace(t *testing.T) {
|
||||
Type: adoptTypeNative,
|
||||
Mode: adoptModeReadOnly,
|
||||
}
|
||||
f := velacmd.NewDeferredFactory(config.GetConfig)
|
||||
ioStream := util.IOStreams{}
|
||||
cmd := NewAdoptCommand(f, ioStream)
|
||||
err := opt.Complete(f, cmd, c.args)
|
||||
_ = cmd.Flags().Set(FlagNamespace, c.namespace)
|
||||
err := opt.Init(f, cmd, c.args)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse resourceRef: %v", err)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package cli
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
@@ -34,6 +35,8 @@ import (
|
||||
"github.com/oam-dev/kubevela/references/appfile"
|
||||
)
|
||||
|
||||
var re = regexp.MustCompile(`"((?:[^"\\]|\\.)*)"`)
|
||||
|
||||
// NewLogsCommand creates `logs` command to tail logs of application
|
||||
func NewLogsCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command {
|
||||
largs := &Args{Args: c}
|
||||
@@ -122,6 +125,10 @@ func (l *Args) printPodLogs(ctx context.Context, ioStreams util.IOStreams, selec
|
||||
}
|
||||
}
|
||||
if show {
|
||||
match := re.FindStringSubmatch(str)
|
||||
if len(match) > 1 {
|
||||
str = strings.ReplaceAll(match[1], "\\n", "\n")
|
||||
}
|
||||
ioStreams.Infonln(str)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
|
||||
92
references/cli/resource-topology/builtin-rule.cue
Normal file
92
references/cli/resource-topology/builtin-rule.cue
Normal file
@@ -0,0 +1,92 @@
|
||||
rules: [
|
||||
{
|
||||
group: "apps"
|
||||
resource: "deployment"
|
||||
subResources: [
|
||||
{
|
||||
group: "apps"
|
||||
resource: "replicaSet"
|
||||
selectors: {
|
||||
ownerReference: true
|
||||
}
|
||||
},
|
||||
]
|
||||
peerResources: commonPeerResources
|
||||
}, {
|
||||
group: "apps"
|
||||
resource: "replicaSet"
|
||||
subResources: [
|
||||
{
|
||||
group: ""
|
||||
resource: "pod"
|
||||
selectors: {
|
||||
ownerReference: true
|
||||
}
|
||||
},
|
||||
]
|
||||
}, {
|
||||
group: "apps"
|
||||
resource: "statefulSet"
|
||||
subResources: [
|
||||
{
|
||||
group: ""
|
||||
resource: "pod"
|
||||
selectors: {
|
||||
ownerReference: true
|
||||
}
|
||||
},
|
||||
]
|
||||
peerResources: commonPeerResources
|
||||
}, {
|
||||
group: "apps"
|
||||
resource: "daemonSet"
|
||||
subResources: [
|
||||
{
|
||||
group: ""
|
||||
resource: "pod"
|
||||
selectors: {
|
||||
ownerReference: true
|
||||
}
|
||||
},
|
||||
]
|
||||
peerResources: commonPeerResources
|
||||
},
|
||||
]
|
||||
|
||||
commonPeerResources: [{
|
||||
group: ""
|
||||
resource: "configMap"
|
||||
selectors: {
|
||||
name: [
|
||||
if context.data.spec.template.spec.volumes != _|_ {
|
||||
for v in context.data.spec.template.spec.volumes if v.configMap != _|_ if v.configMap.name != _|_ {
|
||||
v.configMap.name
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}, {
|
||||
group: ""
|
||||
resource: "secret"
|
||||
selectors: {
|
||||
name: [
|
||||
if context.data.spec.template.spec.volumes != _|_ {
|
||||
for v in context.data.spec.template.spec.volumes if v.secret != _|_ if v.secret.name != _|_ {
|
||||
v.secret.name
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}, {
|
||||
group: ""
|
||||
resource: "service"
|
||||
selectors: {
|
||||
builtin: "service"
|
||||
}
|
||||
}, {
|
||||
group: "networking.k8s.io"
|
||||
resource: "ingress"
|
||||
selectors: {
|
||||
builtin: "ingress"
|
||||
}
|
||||
}]
|
||||
@@ -19,7 +19,10 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
@@ -50,6 +53,7 @@ func NewTopCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *co
|
||||
if AllNamespace {
|
||||
namespace = ""
|
||||
}
|
||||
klog.SetLogger(logr.New(log.NullLogSink{}))
|
||||
return launchUI(c, namespace)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
|
||||
@@ -291,7 +291,7 @@ func makeThemeConfigFileIfNotExist() bool {
|
||||
if _, err := os.Open(filepath.Clean(themeConfigFilePath)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// make file if not exist
|
||||
_ = os.MkdirAll(filepath.Clean(velaThemeHome), 0600)
|
||||
_ = os.MkdirAll(filepath.Clean(velaThemeHome), 0700)
|
||||
_ = os.WriteFile(filepath.Clean(themeConfigFilePath), []byte("name : "+DefaultTheme), 0600)
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
@@ -35,3 +37,11 @@ func TestColor(t *testing.T) {
|
||||
c2 := Color("red")
|
||||
assert.Equal(t, c2.Color(), tcell.GetColor("red").TrueColor())
|
||||
}
|
||||
|
||||
func TestPersistentThemeConfig(t *testing.T) {
|
||||
defer PersistentThemeConfig(DefaultTheme)
|
||||
PersistentThemeConfig("foo")
|
||||
bytes, err := os.ReadFile(themeConfigFilePath)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, strings.Contains(string(bytes), "foo"))
|
||||
}
|
||||
|
||||
@@ -243,6 +243,7 @@ type WorkflowArgs struct {
|
||||
Writer io.Writer
|
||||
Args common.Args
|
||||
StepName string
|
||||
StepID string
|
||||
ErrMap map[string]string
|
||||
App *v1beta1.Application
|
||||
WorkflowRun *workflowv1alpha1.WorkflowRun
|
||||
@@ -399,7 +400,7 @@ func (w *WorkflowArgs) printStepLogs(ctx context.Context, cli client.Client, ioS
|
||||
return w.printResourceLogs(ctx, cli, ioStreams, []wfTypes.Resource{{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
LabelSelector: w.ControllerLabels,
|
||||
}}, []string{fmt.Sprintf(`step_name="%s"`, w.StepName), fmt.Sprintf("%s/%s", w.WorkflowInstance.Namespace, w.WorkflowInstance.Name), "cue logs"})
|
||||
}}, []string{fmt.Sprintf(`stepSessionID="%s"`, w.StepID), fmt.Sprintf("%s/%s", w.WorkflowInstance.Namespace, w.WorkflowInstance.Name), "cue logs"})
|
||||
case logConfig.Source != nil:
|
||||
if len(logConfig.Source.Resources) > 0 {
|
||||
return w.printResourceLogs(ctx, cli, ioStreams, logConfig.Source.Resources, nil)
|
||||
@@ -454,7 +455,8 @@ func (w *WorkflowArgs) selectWorkflowStep(msg string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to select step %s: %w", unwrapStepName(w.StepName), err)
|
||||
}
|
||||
w.StepName = unwrapStepID(stepName, w.WorkflowInstance)
|
||||
w.StepName = unwrapStepName(stepName)
|
||||
w.StepID = unwrapStepID(stepName, w.WorkflowInstance)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ metadata:
|
||||
name: replica-webservice
|
||||
namespace: vela-system
|
||||
spec:
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
|
||||
@@ -153,7 +153,7 @@ var _ = Describe("Test the rest api about the config", func() {
|
||||
Expect(config.Secret).Should(BeNil())
|
||||
Expect(config.Properties["registry"]).Should(Equal("kubevela.test.com"))
|
||||
|
||||
By("the config name is exist")
|
||||
By("the config name does exist")
|
||||
req = v1.CreateConfigRequest{
|
||||
Name: "test-registry",
|
||||
Alias: "Test Registry",
|
||||
@@ -164,7 +164,7 @@ var _ = Describe("Test the rest api about the config", func() {
|
||||
res = post("/configs", req)
|
||||
Expect(res.StatusCode).Should(Equal(400))
|
||||
|
||||
By("the template is not exist")
|
||||
By("the template does not exist")
|
||||
req = v1.CreateConfigRequest{
|
||||
Name: "test-registry2",
|
||||
Alias: "Test Registry",
|
||||
|
||||
42
test/e2e-apiserver-test/repository_test.go
Normal file
42
test/e2e-apiserver-test/repository_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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 e2e_apiserver_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Helm rest api test", func() {
|
||||
|
||||
Describe("helm repo api test", func() {
|
||||
It("test fetching chart values in OCI registry", func() {
|
||||
resp := getWithQuery("/repository/chart/values", map[string]string{
|
||||
"repoUrl": "oci://ghcr.io",
|
||||
"chart": "stefanprodan/charts/podinfo",
|
||||
"repoType": "oci",
|
||||
"version": "6.1.0",
|
||||
})
|
||||
defer resp.Body.Close()
|
||||
values, err := io.ReadAll(resp.Body)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(values)).ShouldNot(BeEquivalentTo(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -49,7 +49,7 @@ var token string
|
||||
const (
|
||||
baseDomain = "http://127.0.0.1:8001"
|
||||
baseURL = "http://127.0.0.1:8001/api/v1"
|
||||
testNSprefix = "api-e2e-test-"
|
||||
testNSprefix = "api-test-"
|
||||
)
|
||||
|
||||
func TestE2eApiserverTest(t *testing.T) {
|
||||
@@ -191,6 +191,26 @@ func get(path string) *http.Response {
|
||||
return response
|
||||
}
|
||||
|
||||
func getWithQuery(path string, params map[string]string) *http.Response {
|
||||
client := &http.Client{}
|
||||
if !strings.HasPrefix(path, "/v1") {
|
||||
path = baseURL + path
|
||||
} else {
|
||||
path = baseDomain + path
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodGet, path, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
req.Header.Add("Authorization", token)
|
||||
query := req.URL.Query()
|
||||
for k, v := range params {
|
||||
query.Set(k, v)
|
||||
}
|
||||
req.URL.RawQuery = query.Encode()
|
||||
response, err := client.Do(req)
|
||||
Expect(err).Should(BeNil())
|
||||
return response
|
||||
}
|
||||
|
||||
func delete(path string) *http.Response {
|
||||
client := &http.Client{}
|
||||
if !strings.HasPrefix(path, "/v1") {
|
||||
|
||||
@@ -7,6 +7,8 @@ metadata:
|
||||
name: replica-webservice
|
||||
namespace: vela-system
|
||||
spec:
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
template: {
|
||||
#PolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector: [...#RuleSelector]
|
||||
selector: #RuleSelector
|
||||
}
|
||||
|
||||
#RuleSelector: {
|
||||
|
||||
@@ -64,7 +64,7 @@ template: {
|
||||
}
|
||||
}
|
||||
|
||||
parameter: *#PatchParams | close({
|
||||
parameter: #PatchParams | close({
|
||||
// +usage=Specify the container image for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -18,7 +18,10 @@ expose: {
|
||||
}
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
isHealth: *false | bool
|
||||
if status != _|_ if status.loadBalancer != _|_ if status.loadBalancer.ingress != _|_ if len(status.loadBalancer.ingress) > 0 if status.loadBalancer.ingress[0].ip != _|_ {
|
||||
isHealth: true
|
||||
}
|
||||
if !isHealth {
|
||||
message: "ExternalIP: Pending"
|
||||
}
|
||||
@@ -28,11 +31,16 @@ expose: {
|
||||
}
|
||||
"""#
|
||||
healthPolicy: #"""
|
||||
isHealth: *true | bool
|
||||
service: context.outputs.service
|
||||
if service.spec.type == "LoadBalancer" {
|
||||
status: service.status
|
||||
isHealth: status != _|_ && status.loadBalancer != _|_ && status.loadBalancer.ingress != _|_ && len(status.loadBalancer.ingress) > 0
|
||||
isHealth: *false | bool
|
||||
if status != _|_ if status.loadBalancer != _|_ if status.loadBalancer.ingress != _|_ if len(status.loadBalancer.ingress) > 0 if status.loadBalancer.ingress[0].ip != _|_ {
|
||||
isHealth: true
|
||||
}
|
||||
}
|
||||
if service.spec.type != "LoadBalancer" {
|
||||
isHealth: true
|
||||
}
|
||||
"""#
|
||||
}
|
||||
|
||||
@@ -14,19 +14,20 @@ gateway: {
|
||||
}
|
||||
if context.outputs.ingress.status.loadBalancer.ingress != _|_ {
|
||||
let igs = context.outputs.ingress.status.loadBalancer.ingress
|
||||
let host = context.outputs.ingress.spec.rules[0].host
|
||||
if igs[0].ip != _|_ {
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
|
||||
}
|
||||
if igs[0].host == _|_ {
|
||||
if host == _|_ {
|
||||
message: "Host not specified, visit the cluster or load balancer in front of the cluster with IP: " + igs[0].ip
|
||||
}
|
||||
}
|
||||
if igs[0].ip == _|_ {
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
|
||||
}
|
||||
if igs[0].host != _|_ {
|
||||
if host != _|_ {
|
||||
message: "Host not specified, visit the cluster or load balancer in front of the cluster"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ template: {
|
||||
envName: string
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
|
||||
@@ -113,7 +113,7 @@ template: {
|
||||
#ECProvider: {
|
||||
type: "ec"
|
||||
apiKey: *"" | string
|
||||
name: "ec-provider" | string
|
||||
name: *"ec-provider" | string
|
||||
}
|
||||
#GCPProvider: {
|
||||
credentials: string
|
||||
|
||||
@@ -51,44 +51,41 @@ template: {
|
||||
// +usage=Specify the message that you want to sent, refer to [dingtalk messaging](https://developers.dingtalk.com/document/robots/custom-robot-access/title-72m-8ag-pqw)
|
||||
message: {
|
||||
// +usage=Specify the message content of dingtalk notification
|
||||
text?: *null | close({
|
||||
text?: close({
|
||||
content: string
|
||||
})
|
||||
// +usage=msgType can be text, link, mardown, actionCard, feedCard
|
||||
msgtype: *"text" | "link" | "markdown" | "actionCard" | "feedCard"
|
||||
link?: *null | close({
|
||||
#link: {
|
||||
text?: string
|
||||
title?: string
|
||||
messageUrl?: string
|
||||
picUrl?: string
|
||||
})
|
||||
markdown?: *null | close({
|
||||
}
|
||||
|
||||
link?: #link
|
||||
markdown?: close({
|
||||
text: string
|
||||
title: string
|
||||
})
|
||||
at?: *null | close({
|
||||
atMobiles?: *null | [...string]
|
||||
isAtAll?: bool
|
||||
at?: close({
|
||||
atMobiles?: [...string]
|
||||
isAtAll?: bool
|
||||
})
|
||||
actionCard?: *null | close({
|
||||
actionCard?: close({
|
||||
text: string
|
||||
title: string
|
||||
hideAvatar: string
|
||||
btnOrientation: string
|
||||
singleTitle: string
|
||||
singleURL: string
|
||||
btns: *null | close([...*null | close({
|
||||
btns?: [...close({
|
||||
title: string
|
||||
actionURL: string
|
||||
})])
|
||||
})]
|
||||
})
|
||||
feedCard?: *null | close({
|
||||
links: *null | close([...*null | close({
|
||||
text?: string
|
||||
title?: string
|
||||
messageUrl?: string
|
||||
picUrl?: string
|
||||
})])
|
||||
feedCard?: close({
|
||||
links: [...#link]
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -109,11 +106,11 @@ template: {
|
||||
// +usage=Specify the message that you want to sent, refer to [slack messaging](https://api.slack.com/reference/messaging/payload)
|
||||
message: {
|
||||
// +usage=Specify the message text for slack notification
|
||||
text: string
|
||||
blocks?: *null | close([...block])
|
||||
attachments?: *null | close({
|
||||
blocks?: *null | close([...block])
|
||||
color?: string
|
||||
text: string
|
||||
blocks?: [...block]
|
||||
attachments?: close({
|
||||
blocks?: [...block]
|
||||
color?: string
|
||||
})
|
||||
thread_ts?: string
|
||||
// +usage=Specify the message text format in markdown for slack notification
|
||||
|
||||
Reference in New Issue
Block a user