mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-01 01:00:38 +00:00
Compare commits
15 Commits
v1.8.0-bet
...
v1.8.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e528902bea | ||
|
|
c3b736f753 | ||
|
|
f343111f87 | ||
|
|
007901f9f1 | ||
|
|
fcd721ffed | ||
|
|
37718a095d | ||
|
|
2ca81e037d | ||
|
|
33940c621a | ||
|
|
705ea38158 | ||
|
|
8fabbbf2de | ||
|
|
c010d1c7f3 | ||
|
|
856a3a636e | ||
|
|
24c33b8313 | ||
|
|
c3ae3fedf3 | ||
|
|
83962d44d9 |
@@ -176,9 +176,8 @@ type Capability struct {
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
||||
// Plugin Source
|
||||
Source *Source `json:"source,omitempty"`
|
||||
Install *Installation `json:"install,omitempty"`
|
||||
CrdInfo *CRDInfo `json:"crdInfo,omitempty"`
|
||||
Source *Source `json:"source,omitempty"`
|
||||
CrdInfo *CRDInfo `json:"crdInfo,omitempty"`
|
||||
|
||||
// Terraform
|
||||
TerraformConfiguration string `json:"terraformConfiguration,omitempty"`
|
||||
|
||||
@@ -49,7 +49,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `disableCaps` | Disable capability | `rollout` |
|
||||
| `dependCheckWait` | dependCheckWait is the time to wait for ApplicationConfiguration's dependent-resource ready | `30s` |
|
||||
|
||||
|
||||
### KubeVela workflow parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -59,7 +58,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `workflow.backoff.maxTime.failedState` | The max backoff time of workflow in a failed condition | `300` |
|
||||
| `workflow.step.errorRetryTimes` | The max retry times of a failed workflow step | `10` |
|
||||
|
||||
|
||||
### KubeVela controller parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -77,7 +75,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `webhookService.port` | KubeVela webhook service port | `9443` |
|
||||
| `healthCheck.port` | KubeVela health check port | `9440` |
|
||||
|
||||
|
||||
### KubeVela controller optimization parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -104,7 +101,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `featureGates.sharedDefinitionStorageForApplicationRevision` | use definition cache to reduce duplicated definition storage for application revision, must be used with InformerCacheFilterUnnecessaryFields | `true` |
|
||||
| `featureGates.disableWorkflowContextConfigMapCache` | disable the workflow context's configmap informer cache | `true` |
|
||||
|
||||
|
||||
### MultiCluster parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -125,7 +121,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `multicluster.clusterGateway.secureTLS.certPath` | Path to the certificate file | `/etc/k8s-cluster-gateway-certs` |
|
||||
| `multicluster.clusterGateway.secureTLS.certManager.enabled` | Whether to enable cert-manager | `false` |
|
||||
|
||||
|
||||
### Test parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -135,7 +130,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `test.k8s.repository` | Test k8s repository | `oamdev/alpine-k8s` |
|
||||
| `test.k8s.tag` | Test k8s tag | `1.18.2` |
|
||||
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
|
||||
@@ -2306,6 +2306,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it
|
||||
defines the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow
|
||||
step.
|
||||
@@ -4168,6 +4173,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it defines
|
||||
the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
|
||||
@@ -1068,6 +1068,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it defines
|
||||
the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
|
||||
@@ -26,7 +26,6 @@ spec:
|
||||
duration: parameter.duration
|
||||
failDuration: parameter.failDuration
|
||||
}
|
||||
|
||||
fail: op.#Steps & {
|
||||
if check.failed != _|_ {
|
||||
if check.failed == true {
|
||||
@@ -36,14 +35,12 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait: op.#ConditionalWait & {
|
||||
continue: check.result
|
||||
if check.message != _|_ {
|
||||
message: check.message
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Query is a raw prometheus query to perform
|
||||
query: string
|
||||
|
||||
@@ -318,8 +318,5 @@ spec:
|
||||
failureThreshold: *3 | int
|
||||
}
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
type: cronjobs.batch
|
||||
type: autodetects.core.oam.dev
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ spec:
|
||||
template: |
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
outputs: service: {
|
||||
@@ -25,22 +26,66 @@ spec:
|
||||
metadata: name: context.name
|
||||
metadata: annotations: parameter.annotations
|
||||
spec: {
|
||||
selector: "app.oam.dev/component": context.name
|
||||
ports: [
|
||||
for p in parameter.port {
|
||||
name: "port-" + strconv.FormatInt(p, 10)
|
||||
port: p
|
||||
targetPort: p
|
||||
if parameter["matchLabels"] == _|_ {
|
||||
selector: "app.oam.dev/component": context.name
|
||||
}
|
||||
if parameter["matchLabels"] != _|_ {
|
||||
selector: parameter["matchLabels"]
|
||||
}
|
||||
|
||||
// compatible with the old way
|
||||
if parameter["port"] != _|_ if parameter["ports"] == _|_ {
|
||||
ports: [
|
||||
for p in parameter.port {
|
||||
name: "port-" + strconv.FormatInt(p, 10)
|
||||
port: p
|
||||
targetPort: p
|
||||
},
|
||||
]
|
||||
}
|
||||
if parameter["ports"] != _|_ {
|
||||
ports: [ for v in parameter.ports {
|
||||
port: v.port
|
||||
targetPort: v.port
|
||||
if v.name != _|_ {
|
||||
name: v.name
|
||||
}
|
||||
if v.name == _|_ {
|
||||
_name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
name: *_name | string
|
||||
if v.protocol != "TCP" {
|
||||
name: _name + "-" + strings.ToLower(v.protocol)
|
||||
}
|
||||
}
|
||||
if v.nodePort != _|_ if parameter.type == "NodePort" {
|
||||
nodePort: v.nodePort
|
||||
}
|
||||
if v.protocol != _|_ {
|
||||
protocol: v.protocol
|
||||
}
|
||||
},
|
||||
]
|
||||
]
|
||||
}
|
||||
type: parameter.type
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the exposion ports
|
||||
port: [...int]
|
||||
// +usage=Deprecated, the old way to specify the exposion ports
|
||||
port?: [...int]
|
||||
// +usage=Specify portsyou want customer traffic sent to
|
||||
ports?: [...{
|
||||
// +usage=Number of port to expose on the pod's IP address
|
||||
port: int
|
||||
// +usage=Name of the port
|
||||
name?: string
|
||||
// +usage=Protocol for port. Must be UDP, TCP, or SCTP
|
||||
protocol: *"TCP" | "UDP" | "SCTP"
|
||||
// +usage=exposed node port. Only Valid when exposeType is NodePort
|
||||
nodePort?: int
|
||||
}]
|
||||
// +usage=Specify the annotaions of the exposed service
|
||||
annotations: [string]: string
|
||||
annotations: [string]: string
|
||||
matchLabels?: [string]: string
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP","NodePort","LoadBalancer","ExternalName"
|
||||
type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ spec:
|
||||
- mountPath: {{ .Values.admissionWebhooks.certificate.mountPath }}
|
||||
name: tls-cert-vol
|
||||
readOnly: true
|
||||
{{ if and .Values.multicluster.clusterGateway.secureTLS.enabled .Values.multicluster.clusterGateway.direct }}
|
||||
{{ if and .Values.multicluster.enabled .Values.multicluster.clusterGateway.secureTLS.enabled .Values.multicluster.clusterGateway.direct }}
|
||||
- mountPath: /cluster-gateway-tls-cert
|
||||
name: tls-cert-vol-cg
|
||||
readOnly: true
|
||||
@@ -362,7 +362,7 @@ spec:
|
||||
secret:
|
||||
defaultMode: 420
|
||||
secretName: {{ template "kubevela.fullname" . }}-admission
|
||||
{{ if and .Values.multicluster.clusterGateway.secureTLS.enabled .Values.multicluster.clusterGateway.direct }}
|
||||
{{ if and .Values.multicluster.enabled .Values.multicluster.clusterGateway.secureTLS.enabled .Values.multicluster.clusterGateway.direct }}
|
||||
- name: tls-cert-vol-cg
|
||||
secret:
|
||||
defaultMode: 420
|
||||
|
||||
@@ -67,7 +67,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
||||
| `disableCaps` | Disable capability | `envbinding,rollout` |
|
||||
| `dependCheckWait` | dependCheckWait is the time to wait for ApplicationConfiguration's dependent-resource ready | `30s` |
|
||||
|
||||
|
||||
### KubeVela workflow parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -77,7 +76,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
||||
| `workflow.backoff.maxTime.failedState` | The max backoff time of workflow in a failed condition | `300` |
|
||||
| `workflow.step.errorRetryTimes` | The max retry times of a failed workflow step | `10` |
|
||||
|
||||
|
||||
### KubeVela controller parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -95,14 +93,12 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
||||
| `webhookService.port` | KubeVela webhook service port | `9443` |
|
||||
| `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 |
|
||||
@@ -120,7 +116,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
||||
| `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
|
||||
|
||||
| Name | Description | Value |
|
||||
@@ -130,7 +125,6 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
||||
| `test.k8s.repository` | Test k8s repository | `oamdev/alpine-k8s` |
|
||||
| `test.k8s.tag` | Test k8s tag | `1.18.2` |
|
||||
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
|
||||
@@ -2306,6 +2306,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it
|
||||
defines the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow
|
||||
step.
|
||||
@@ -4168,6 +4173,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it defines
|
||||
the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
|
||||
@@ -1068,6 +1068,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it defines
|
||||
the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
|
||||
@@ -26,7 +26,6 @@ spec:
|
||||
duration: parameter.duration
|
||||
failDuration: parameter.failDuration
|
||||
}
|
||||
|
||||
fail: op.#Steps & {
|
||||
if check.failed != _|_ {
|
||||
if check.failed == true {
|
||||
@@ -36,14 +35,12 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait: op.#ConditionalWait & {
|
||||
continue: check.result
|
||||
if check.message != _|_ {
|
||||
message: check.message
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Query is a raw prometheus query to perform
|
||||
query: string
|
||||
|
||||
@@ -318,8 +318,5 @@ spec:
|
||||
failureThreshold: *3 | int
|
||||
}
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
type: cronjobs.batch
|
||||
type: autodetects.core.oam.dev
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ spec:
|
||||
template: |
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
outputs: service: {
|
||||
@@ -25,22 +26,66 @@ spec:
|
||||
metadata: name: context.name
|
||||
metadata: annotations: parameter.annotations
|
||||
spec: {
|
||||
selector: "app.oam.dev/component": context.name
|
||||
ports: [
|
||||
for p in parameter.port {
|
||||
name: "port-" + strconv.FormatInt(p, 10)
|
||||
port: p
|
||||
targetPort: p
|
||||
if parameter["matchLabels"] == _|_ {
|
||||
selector: "app.oam.dev/component": context.name
|
||||
}
|
||||
if parameter["matchLabels"] != _|_ {
|
||||
selector: parameter["matchLabels"]
|
||||
}
|
||||
|
||||
// compatible with the old way
|
||||
if parameter["port"] != _|_ if parameter["ports"] == _|_ {
|
||||
ports: [
|
||||
for p in parameter.port {
|
||||
name: "port-" + strconv.FormatInt(p, 10)
|
||||
port: p
|
||||
targetPort: p
|
||||
},
|
||||
]
|
||||
}
|
||||
if parameter["ports"] != _|_ {
|
||||
ports: [ for v in parameter.ports {
|
||||
port: v.port
|
||||
targetPort: v.port
|
||||
if v.name != _|_ {
|
||||
name: v.name
|
||||
}
|
||||
if v.name == _|_ {
|
||||
_name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
name: *_name | string
|
||||
if v.protocol != "TCP" {
|
||||
name: _name + "-" + strings.ToLower(v.protocol)
|
||||
}
|
||||
}
|
||||
if v.nodePort != _|_ if parameter.type == "NodePort" {
|
||||
nodePort: v.nodePort
|
||||
}
|
||||
if v.protocol != _|_ {
|
||||
protocol: v.protocol
|
||||
}
|
||||
},
|
||||
]
|
||||
]
|
||||
}
|
||||
type: parameter.type
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the exposion ports
|
||||
port: [...int]
|
||||
// +usage=Deprecated, the old way to specify the exposion ports
|
||||
port?: [...int]
|
||||
// +usage=Specify portsyou want customer traffic sent to
|
||||
ports?: [...{
|
||||
// +usage=Number of port to expose on the pod's IP address
|
||||
port: int
|
||||
// +usage=Name of the port
|
||||
name?: string
|
||||
// +usage=Protocol for port. Must be UDP, TCP, or SCTP
|
||||
protocol: *"TCP" | "UDP" | "SCTP"
|
||||
// +usage=exposed node port. Only Valid when exposeType is NodePort
|
||||
nodePort?: int
|
||||
}]
|
||||
// +usage=Specify the annotaions of the exposed service
|
||||
annotations: [string]: string
|
||||
annotations: [string]: string
|
||||
matchLabels?: [string]: string
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP","NodePort","LoadBalancer","ExternalName"
|
||||
type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -31,6 +32,7 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/e2e"
|
||||
"github.com/oam-dev/kubevela/pkg/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
@@ -176,4 +178,160 @@ var _ = Describe("Addon Test", func() {
|
||||
Expect(output).To(ContainSubstring("Successfully delete an addon registry my-repo"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Enable dependency addon test", func() {
|
||||
It(" enable mock-dependence-rely without specified clusters when mock-dependence addon is not enabled", func() {
|
||||
output, err := e2e.Exec("vela addon enable mock-dependence-rely")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).To(ContainSubstring("enabled successfully."))
|
||||
Eventually(func(g Gomega) {
|
||||
app := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-mock-dependence", Namespace: "vela-system"}, app)).Should(Succeed())
|
||||
topologyPolicyValue := map[string]interface{}{}
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if policy.Type == "topology" {
|
||||
Expect(json.Unmarshal(policy.Properties.Raw, &topologyPolicyValue)).Should(Succeed())
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(topologyPolicyValue["clusterLabelSelector"]).Should(Equal(map[string]interface{}{}))
|
||||
}, 30*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("enable mock-dependence-rely with specified clusters when mock-dependence addon is not enabled ", func() {
|
||||
output, err := e2e.Exec("vela addon enable mock-dependence-rely2 --clusters local")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).To(ContainSubstring("enabled successfully."))
|
||||
Eventually(func(g Gomega) {
|
||||
app := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-mock-dependence2", Namespace: "vela-system"}, app)).Should(Succeed())
|
||||
topologyPolicyValue := map[string]interface{}{}
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if policy.Type == "topology" {
|
||||
Expect(json.Unmarshal(policy.Properties.Raw, &topologyPolicyValue)).Should(Succeed())
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(topologyPolicyValue["clusters"]).Should(Equal([]interface{}{"local"}))
|
||||
}, 30*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("enable mock-dependence-rely without specified clusters when mock-dependence addon was enabled with specified clusters", func() {
|
||||
// 1. enable mock-dependence addon with local clusters
|
||||
output, err := e2e.InteractiveExec("vela addon enable mock-dependence --clusters local myparam=test", func(c *expect.Console) {
|
||||
_, err = c.SendLine("y")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).To(ContainSubstring("enabled successfully."))
|
||||
Eventually(func(g Gomega) {
|
||||
// check application render cluster
|
||||
app := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-mock-dependence", Namespace: "vela-system"}, app)).Should(Succeed())
|
||||
topologyPolicyValue := map[string]interface{}{}
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if policy.Type == "topology" {
|
||||
Expect(json.Unmarshal(policy.Properties.Raw, &topologyPolicyValue)).Should(Succeed())
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(topologyPolicyValue["clusters"]).Should(Equal([]interface{}{"local"}))
|
||||
Expect(topologyPolicyValue["clusterLabelSelector"]).Should(BeNil())
|
||||
}, 600*time.Second).Should(Succeed())
|
||||
// 2. enable mock-dependence-rely addon without clusters
|
||||
output1, err := e2e.InteractiveExec("vela addon enable mock-dependence-rely", func(c *expect.Console) {
|
||||
_, err = c.SendLine("y")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output1).To(ContainSubstring("enabled successfully."))
|
||||
// 3. enable mock-dependence-rely addon changes the mock-dependence topology policy
|
||||
Eventually(func(g Gomega) {
|
||||
app := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-mock-dependence", Namespace: "vela-system"}, app)).Should(Succeed())
|
||||
topologyPolicyValue := map[string]interface{}{}
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if policy.Type == "topology" {
|
||||
Expect(json.Unmarshal(policy.Properties.Raw, &topologyPolicyValue)).Should(Succeed())
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(topologyPolicyValue["clusterLabelSelector"]).Should(Equal(map[string]interface{}{}))
|
||||
Expect(topologyPolicyValue["clusters"]).Should(BeNil())
|
||||
}, 30*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test addon dependency with specified clusters", func() {
|
||||
const clusterName = "k3s-default"
|
||||
// enable addon
|
||||
output, err := e2e.InteractiveExec("vela addon enable mock-dependence --clusters local myparam=test", func(c *expect.Console) {
|
||||
_, err = c.SendLine("y")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).To(ContainSubstring("enabled successfully."))
|
||||
output1, err := e2e.Exec("vela ls -A")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output1).To(ContainSubstring("mock-dependence"))
|
||||
output2, err := e2e.Exec("vela addon list")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output2).To(ContainSubstring("mock-dependence"))
|
||||
// check dependence application parameter
|
||||
Eventually(func(g Gomega) {
|
||||
// check parameter
|
||||
sec := &v1.Secret{}
|
||||
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-secret-mock-dependence", Namespace: "vela-system"}, sec)).Should(Succeed())
|
||||
parameters := map[string]interface{}{}
|
||||
json.Unmarshal(sec.Data[addon.AddonParameterDataKey], ¶meters)
|
||||
g.Expect(parameters).Should(BeEquivalentTo(map[string]interface{}{
|
||||
"clusters": []interface{}{"local"},
|
||||
"myparam": "test",
|
||||
}))
|
||||
// check application render cluster
|
||||
app := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-mock-dependence", Namespace: "vela-system"}, app)).Should(Succeed())
|
||||
topologyPolicyValue := map[string]interface{}{}
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if policy.Type == "topology" {
|
||||
Expect(json.Unmarshal(policy.Properties.Raw, &topologyPolicyValue)).Should(Succeed())
|
||||
break
|
||||
}
|
||||
}
|
||||
fluxcdYaml, err1 := e2e.Exec("vela status addon-mock-dependence -n vela-system -oyaml")
|
||||
Expect(err1).NotTo(HaveOccurred())
|
||||
Expect(fluxcdYaml).To(ContainSubstring("mock-dependence"))
|
||||
fluxcdStatus, err2 := e2e.Exec("vela addon status mock-dependence -v")
|
||||
Expect(err2).NotTo(HaveOccurred())
|
||||
Expect(fluxcdStatus).To(ContainSubstring("mock-dependence"))
|
||||
Expect(topologyPolicyValue["clusters"]).Should(Equal([]interface{}{"local"}))
|
||||
}, 600*time.Second).Should(Succeed())
|
||||
// enable addon which rely on mock-dependence addon
|
||||
e2e.InteractiveExec("vela addon enable mock-dependence-rely --clusters local,"+clusterName, func(c *expect.Console) {
|
||||
_, err = c.SendLine("y")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
// check mock-dependence application parameter
|
||||
Eventually(func(g Gomega) {
|
||||
sec := &v1.Secret{}
|
||||
g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-secret-mock-dependence", Namespace: "vela-system"}, sec)).Should(Succeed())
|
||||
parameters := map[string]interface{}{}
|
||||
json.Unmarshal(sec.Data[addon.AddonParameterDataKey], ¶meters)
|
||||
g.Expect(parameters).Should(BeEquivalentTo(map[string]interface{}{
|
||||
"clusters": []interface{}{"local", clusterName},
|
||||
"myparam": "test",
|
||||
}))
|
||||
app := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "addon-mock-dependence", Namespace: "vela-system"}, app)).Should(Succeed())
|
||||
topologyPolicyValue := map[string]interface{}{}
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if policy.Type == "topology" {
|
||||
Expect(json.Unmarshal(policy.Properties.Raw, &topologyPolicyValue)).Should(Succeed())
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(topologyPolicyValue["clusters"]).Should(Equal([]interface{}{"local", clusterName}))
|
||||
}, 30*time.Second).Should(Succeed())
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
3
e2e/addon/mock/testdata/mock-dependence-rely/README.md
vendored
Normal file
3
e2e/addon/mock/testdata/mock-dependence-rely/README.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# mock-dependence-rely
|
||||
|
||||
This is an addon template. Check how to build your own addon: https://kubevela.net/docs/platform-engineers/addon/intro
|
||||
25
e2e/addon/mock/testdata/mock-dependence-rely/definitions/mytraitb.cue
vendored
Normal file
25
e2e/addon/mock/testdata/mock-dependence-rely/definitions/mytraitb.cue
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// We put Definitions in definitions directory.
|
||||
// References:
|
||||
// - https://kubevela.net/docs/platform-engineers/cue/definition-edit
|
||||
// - https://kubevela.net/docs/platform-engineers/addon/intro#definitions-directoryoptional
|
||||
"mytraitb": {
|
||||
alias: "mtb"
|
||||
annotations: {}
|
||||
attributes: {
|
||||
appliesToWorkloads: [
|
||||
"deployments.apps",
|
||||
"replicasets.apps",
|
||||
"statefulsets.apps",
|
||||
]
|
||||
conflictsWith: []
|
||||
podDisruptive: false
|
||||
workloadRefPath: ""
|
||||
}
|
||||
description: "My trait description."
|
||||
labels: {}
|
||||
type: "trait"
|
||||
}
|
||||
template: {
|
||||
parameter: {param: ""}
|
||||
outputs: {sample: {}}
|
||||
}
|
||||
10
e2e/addon/mock/testdata/mock-dependence-rely/metadata.yaml
vendored
Normal file
10
e2e/addon/mock/testdata/mock-dependence-rely/metadata.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
description: An addon for testing addon dependency with specified clusters.
|
||||
icon: ""
|
||||
invisible: false
|
||||
name: mock-dependence-rely
|
||||
tags:
|
||||
- my-tag
|
||||
version: 1.0.0
|
||||
dependencies:
|
||||
# install controller by helm.
|
||||
- name: mock-dependence
|
||||
10
e2e/addon/mock/testdata/mock-dependence-rely/parameter.cue
vendored
Normal file
10
e2e/addon/mock/testdata/mock-dependence-rely/parameter.cue
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// parameter.cue is used to store addon parameters.
|
||||
//
|
||||
// You can use these parameters in template.cue or in resources/ by 'parameter.myparam'
|
||||
//
|
||||
// For example, you can use parameters to allow the user to customize
|
||||
// container images, ports, and etc.
|
||||
parameter: {
|
||||
// +usage=Custom parameter description
|
||||
myparam: *"mynsrely" | string
|
||||
}
|
||||
18
e2e/addon/mock/testdata/mock-dependence-rely/resources/myresourceb.cue
vendored
Normal file
18
e2e/addon/mock/testdata/mock-dependence-rely/resources/myresourceb.cue
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// We put Components in resources directory.
|
||||
// References:
|
||||
// - https://kubevela.net/docs/end-user/components/references
|
||||
// - https://kubevela.net/docs/platform-engineers/addon/intro#resources-directoryoptional
|
||||
output: {
|
||||
type: "k8s-objects"
|
||||
properties: {
|
||||
objects: [
|
||||
{
|
||||
// This creates a plain old Kubernetes namespace
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
// We can use the parameter defined in parameter.cue like this.
|
||||
metadata: name: parameter.myparam
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
9
e2e/addon/mock/testdata/mock-dependence-rely/template.cue
vendored
Normal file
9
e2e/addon/mock/testdata/mock-dependence-rely/template.cue
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
spec: {
|
||||
components: []
|
||||
policies: []
|
||||
}
|
||||
}
|
||||
3
e2e/addon/mock/testdata/mock-dependence-rely2/README.md
vendored
Normal file
3
e2e/addon/mock/testdata/mock-dependence-rely2/README.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# mock-dependence-rely2
|
||||
|
||||
This is an addon template. Check how to build your own addon: https://kubevela.net/docs/platform-engineers/addon/intro
|
||||
10
e2e/addon/mock/testdata/mock-dependence-rely2/metadata.yaml
vendored
Normal file
10
e2e/addon/mock/testdata/mock-dependence-rely2/metadata.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
description: An addon for testing addon dependency with specified clusters.
|
||||
icon: ""
|
||||
invisible: false
|
||||
name: mock-dependence-rely2
|
||||
tags:
|
||||
- my-tag
|
||||
version: 1.0.0
|
||||
dependencies:
|
||||
# install controller by helm.
|
||||
- name: mock-dependence2
|
||||
4
e2e/addon/mock/testdata/mock-dependence-rely2/parameter.cue
vendored
Normal file
4
e2e/addon/mock/testdata/mock-dependence-rely2/parameter.cue
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
parameter: {
|
||||
// +usage=Custom parameter description
|
||||
myparam: *"mynsrely" | string
|
||||
}
|
||||
9
e2e/addon/mock/testdata/mock-dependence-rely2/template.cue
vendored
Normal file
9
e2e/addon/mock/testdata/mock-dependence-rely2/template.cue
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
spec: {
|
||||
components: []
|
||||
policies: []
|
||||
}
|
||||
}
|
||||
3
e2e/addon/mock/testdata/mock-dependence/README.md
vendored
Normal file
3
e2e/addon/mock/testdata/mock-dependence/README.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# mock-dependence
|
||||
|
||||
This is an addon template. Check how to build your own addon: https://kubevela.net/docs/platform-engineers/addon/intro
|
||||
25
e2e/addon/mock/testdata/mock-dependence/definitions/mytrait.cue
vendored
Normal file
25
e2e/addon/mock/testdata/mock-dependence/definitions/mytrait.cue
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// We put Definitions in definitions directory.
|
||||
// References:
|
||||
// - https://kubevela.net/docs/platform-engineers/cue/definition-edit
|
||||
// - https://kubevela.net/docs/platform-engineers/addon/intro#definitions-directoryoptional
|
||||
"mytrait-depend": {
|
||||
alias: "mt-depend"
|
||||
annotations: {}
|
||||
attributes: {
|
||||
appliesToWorkloads: [
|
||||
"deployments.apps",
|
||||
"replicasets.apps",
|
||||
"statefulsets.apps",
|
||||
]
|
||||
conflictsWith: []
|
||||
podDisruptive: false
|
||||
workloadRefPath: ""
|
||||
}
|
||||
description: "My trait description."
|
||||
labels: {}
|
||||
type: "trait"
|
||||
}
|
||||
template: {
|
||||
parameter: {param: ""}
|
||||
outputs: {sample: {}}
|
||||
}
|
||||
7
e2e/addon/mock/testdata/mock-dependence/metadata.yaml
vendored
Normal file
7
e2e/addon/mock/testdata/mock-dependence/metadata.yaml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
description: An addon for testing addon dependency with specified clusters.
|
||||
icon: ""
|
||||
invisible: false
|
||||
name: mock-dependence
|
||||
tags:
|
||||
- my-tag
|
||||
version: 1.0.0
|
||||
12
e2e/addon/mock/testdata/mock-dependence/parameter.cue
vendored
Normal file
12
e2e/addon/mock/testdata/mock-dependence/parameter.cue
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// parameter.cue is used to store addon parameters.
|
||||
//
|
||||
// You can use these parameters in template.cue or in resources/ by 'parameter.myparam'
|
||||
//
|
||||
// For example, you can use parameters to allow the user to customize
|
||||
// container images, ports, and etc.
|
||||
parameter: {
|
||||
// +usage=Custom parameter description
|
||||
myparam: *"myns" | string
|
||||
//+usage=Deploy to specified clusters. Leave empty to deploy to all clusters.
|
||||
clusters?: [...string]
|
||||
}
|
||||
18
e2e/addon/mock/testdata/mock-dependence/resources/myresource-depend.cue
vendored
Normal file
18
e2e/addon/mock/testdata/mock-dependence/resources/myresource-depend.cue
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// We put Components in resources directory.
|
||||
// References:
|
||||
// - https://kubevela.net/docs/end-user/components/references
|
||||
// - https://kubevela.net/docs/platform-engineers/addon/intro#resources-directoryoptional
|
||||
output: {
|
||||
type: "k8s-objects"
|
||||
properties: {
|
||||
objects: [
|
||||
{
|
||||
// This creates a plain old Kubernetes namespace
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
// We can use the parameter defined in parameter.cue like this.
|
||||
metadata: name: parameter.myparam
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
24
e2e/addon/mock/testdata/mock-dependence/template.cue
vendored
Normal file
24
e2e/addon/mock/testdata/mock-dependence/template.cue
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
_targetNamespace: parameter.myparam
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
spec: {
|
||||
components: [],
|
||||
policies: [
|
||||
{
|
||||
type: "topology"
|
||||
name: "deploy-mock-dependency-ns"
|
||||
properties: {
|
||||
namespace: _targetNamespace
|
||||
if parameter.clusters != _|_ {
|
||||
clusters: parameter.clusters
|
||||
}
|
||||
if parameter.clusters == _|_ {
|
||||
clusterLabelSelector: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
3
e2e/addon/mock/testdata/mock-dependence2/README.md
vendored
Normal file
3
e2e/addon/mock/testdata/mock-dependence2/README.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# mock-dependence2
|
||||
|
||||
This is an addon template. Check how to build your own addon: https://kubevela.net/docs/platform-engineers/addon/intro
|
||||
7
e2e/addon/mock/testdata/mock-dependence2/metadata.yaml
vendored
Normal file
7
e2e/addon/mock/testdata/mock-dependence2/metadata.yaml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
description: An addon for testing addon dependency with specified clusters.
|
||||
icon: ""
|
||||
invisible: false
|
||||
name: mock-dependence2
|
||||
tags:
|
||||
- my-tag
|
||||
version: 1.0.0
|
||||
6
e2e/addon/mock/testdata/mock-dependence2/parameter.cue
vendored
Normal file
6
e2e/addon/mock/testdata/mock-dependence2/parameter.cue
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
parameter: {
|
||||
// +usage=Custom parameter description
|
||||
myparam: *"myns" | string
|
||||
//+usage=Deploy to specified clusters. Leave empty to deploy to all clusters.
|
||||
clusters?: [...string]
|
||||
}
|
||||
24
e2e/addon/mock/testdata/mock-dependence2/template.cue
vendored
Normal file
24
e2e/addon/mock/testdata/mock-dependence2/template.cue
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
_targetNamespace: parameter.myparam
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
spec: {
|
||||
components: [],
|
||||
policies: [
|
||||
{
|
||||
type: "topology"
|
||||
name: "deploy-mock-dependency-ns"
|
||||
properties: {
|
||||
namespace: _targetNamespace
|
||||
if parameter.clusters != _|_ {
|
||||
clusters: parameter.clusters
|
||||
}
|
||||
if parameter.clusters == _|_ {
|
||||
clusterLabelSelector: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -52,7 +52,7 @@ require (
|
||||
github.com/imdario/mergo v0.3.13
|
||||
github.com/kubevela/pkg v0.0.0-20230316114047-e2b41b377bac
|
||||
github.com/kubevela/prism v1.7.0-alpha.1
|
||||
github.com/kubevela/workflow v0.5.0
|
||||
github.com/kubevela/workflow v0.5.1-0.20230412142923-1f15ba091699
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -954,8 +954,8 @@ github.com/kubevela/pkg v0.0.0-20230316114047-e2b41b377bac h1:TLQchMx+BRTnHyebDp
|
||||
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.5.0 h1:1C3v8q7xuNQRww/pD/uu2ywTX0xOG0jb+rQsM1sjhwU=
|
||||
github.com/kubevela/workflow v0.5.0/go.mod h1:l1zZpJEJmI/ieI3vM3TTGOfZSPFDl5Ax7MWfbR6It98=
|
||||
github.com/kubevela/workflow v0.5.1-0.20230412142923-1f15ba091699 h1:XvHs/8a10AvHnetlGSpylFnx8PFvONTzmR9CAzFAHbY=
|
||||
github.com/kubevela/workflow v0.5.1-0.20230412142923-1f15ba091699/go.mod h1:+Ah40fwzX9fi/xeWdphew9J4kqfJiGwXw5MDeGPq3IU=
|
||||
github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
|
||||
@@ -2306,6 +2306,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it
|
||||
defines the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow
|
||||
step.
|
||||
@@ -4168,6 +4173,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it defines
|
||||
the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
|
||||
@@ -1068,6 +1068,11 @@ spec:
|
||||
alias:
|
||||
type: string
|
||||
type: object
|
||||
mode:
|
||||
description: Mode is only valid for sub steps, it defines
|
||||
the mode of the sub steps
|
||||
nullable: true
|
||||
type: string
|
||||
name:
|
||||
description: Name is the unique name of the workflow step.
|
||||
type: string
|
||||
|
||||
@@ -54,6 +54,7 @@ import (
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/klog/v2"
|
||||
stringslices "k8s.io/utils/strings/slices"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
@@ -999,20 +1000,29 @@ func (h *Installer) getAddonMeta() (map[string]SourceMeta, error) {
|
||||
// installDependency checks if addon's dependency and install it
|
||||
func (h *Installer) installDependency(addon *InstallPackage) error {
|
||||
var dependencies []string
|
||||
var addonClusters = getClusters(h.args)
|
||||
for _, dep := range addon.Dependencies {
|
||||
_, err := FetchAddonRelatedApp(h.ctx, h.cli, dep.Name)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if !apierrors.IsNotFound(err) {
|
||||
needInstallAddonDep, depClusters, err := checkDependencyNeedInstall(h.ctx, h.cli, dep.Name, addonClusters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !needInstallAddonDep {
|
||||
continue
|
||||
}
|
||||
|
||||
dependencies = append(dependencies, dep.Name)
|
||||
if h.dryRun {
|
||||
continue
|
||||
}
|
||||
depHandler := *h
|
||||
depHandler.args = nil
|
||||
// reset dependency addon clusters parameter
|
||||
depArgs, depArgsErr := getDependencyArgs(h.ctx, h.cli, dep.Name, depClusters)
|
||||
if depArgsErr != nil {
|
||||
return depArgsErr
|
||||
}
|
||||
|
||||
depHandler.args = depArgs
|
||||
|
||||
var depAddon *InstallPackage
|
||||
// try to install the dependent addon from the same registry with the current addon
|
||||
depAddon, err = h.loadInstallPackage(dep.Name, dep.Version)
|
||||
@@ -1062,6 +1072,72 @@ func (h *Installer) installDependency(addon *InstallPackage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkDependencyNeedInstall checks whether dependency addon needs to be installed on other clusters
|
||||
func checkDependencyNeedInstall(ctx context.Context, k8sClient client.Client, depName string, addonClusters []string) (bool, []string, error) {
|
||||
depApp, err := FetchAddonRelatedApp(ctx, k8sClient, depName)
|
||||
if err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return false, nil, err
|
||||
}
|
||||
// dependent addon is not exist
|
||||
return true, addonClusters, nil
|
||||
}
|
||||
topologyPolicyValue := map[string]interface{}{}
|
||||
for _, policy := range depApp.Spec.Policies {
|
||||
if policy.Type == "topology" {
|
||||
unmarshalErr := json.Unmarshal(policy.Properties.Raw, &topologyPolicyValue)
|
||||
if unmarshalErr != nil {
|
||||
return false, nil, unmarshalErr
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// nil clusters indicates that the dependent addon is installed on all clusters
|
||||
if topologyPolicyValue["clusters"] == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
// nil addonClusters indicates the addon will be installed,
|
||||
// thus we should set the dependent addon's clusters arg to be nil so that it is installed on all clusters
|
||||
if addonClusters == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
// Determine whether the dependent addon's existing clusters can cover the new addon's clusters
|
||||
var needInstallAddonDep = false
|
||||
var depClusters []string
|
||||
originClusters := topologyPolicyValue["clusters"].([]interface{})
|
||||
for _, r := range originClusters {
|
||||
depClusters = append(depClusters, r.(string))
|
||||
}
|
||||
for _, addonCluster := range addonClusters {
|
||||
if !stringslices.Contains(depClusters, addonCluster) {
|
||||
depClusters = append(depClusters, addonCluster)
|
||||
needInstallAddonDep = true
|
||||
}
|
||||
}
|
||||
return needInstallAddonDep, depClusters, nil
|
||||
}
|
||||
|
||||
// getDependencyArgs resets the dependency clusters arg according needed install depClusters
|
||||
func getDependencyArgs(ctx context.Context, k8sClient client.Client, depName string, depClusters []string) (map[string]interface{}, error) {
|
||||
depArgs, depArgsErr := GetAddonLegacyParameters(ctx, k8sClient, depName)
|
||||
if depArgsErr != nil && !apierrors.IsNotFound(depArgsErr) {
|
||||
return nil, depArgsErr
|
||||
}
|
||||
// reset the cluster arg
|
||||
if depClusters == nil {
|
||||
// delete clusters args, when render addon, it will use clusterLabelSelector then render addon to all clusters
|
||||
if depArgs != nil && depArgs[types.ClustersArg] != nil {
|
||||
delete(depArgs, types.ClustersArg)
|
||||
}
|
||||
} else {
|
||||
if depArgs == nil {
|
||||
depArgs = map[string]interface{}{}
|
||||
}
|
||||
depArgs[types.ClustersArg] = depClusters
|
||||
}
|
||||
return depArgs, nil
|
||||
}
|
||||
|
||||
// checkDependency checks if addon's dependency
|
||||
func (h *Installer) checkDependency(addon *InstallPackage) ([]string, error) {
|
||||
var app v1beta1.Application
|
||||
|
||||
@@ -174,6 +174,107 @@ var _ = Describe("Addon test", func() {
|
||||
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
})
|
||||
|
||||
It("checkDependencyNeedInstall func test", func() {
|
||||
// case1: dependency addon not exist, adonClusters is not nil
|
||||
depAddonName := "legacy-addon"
|
||||
addonClusters := []string{"cluster1", "cluster2"}
|
||||
needInstallAddonDep, depClusters, err := checkDependencyNeedInstall(ctx, k8sClient, depAddonName, addonClusters)
|
||||
Expect(needInstallAddonDep).Should(BeTrue())
|
||||
Expect(depClusters).Should(Equal(addonClusters))
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
// case1.1: dependency addon not exist, adonClusters is nil
|
||||
needInstallAddonDep1, depClusters1, err := checkDependencyNeedInstall(ctx, k8sClient, depAddonName, nil)
|
||||
Expect(needInstallAddonDep1).Should(BeTrue())
|
||||
Expect(depClusters1).Should(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
// case2: dependency addon exist, no topology policy, addonClusters is not nil
|
||||
app = v1beta1.Application{}
|
||||
Expect(yaml.Unmarshal([]byte(legacyAppYaml), &app)).Should(BeNil())
|
||||
app.SetNamespace(testns)
|
||||
Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
|
||||
Eventually(func(g Gomega) {
|
||||
needInstallAddonDep, depClusters, err := checkDependencyNeedInstall(ctx, k8sClient, depAddonName, addonClusters)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(needInstallAddonDep).Should(BeFalse())
|
||||
Expect(depClusters).Should(BeNil())
|
||||
}, 30*time.Second).Should(Succeed())
|
||||
|
||||
// case3: clusters is nil (no topology policy), addonClusters is nil
|
||||
needInstallAddonDep2, depClusters2, err := checkDependencyNeedInstall(ctx, k8sClient, depAddonName, nil)
|
||||
Expect(needInstallAddonDep2).Should(BeFalse())
|
||||
Expect(depClusters2).Should(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
// case4: clusters is nil, addonClusters is nil
|
||||
app = v1beta1.Application{}
|
||||
Expect(yaml.Unmarshal([]byte(legacy3AppYaml), &app)).Should(BeNil())
|
||||
app.SetNamespace(testns)
|
||||
Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
|
||||
Eventually(func(g Gomega) {
|
||||
needInstallAddonDep, depClusters, err := checkDependencyNeedInstall(ctx, k8sClient, "legacy-addon3", nil)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(needInstallAddonDep).Should(BeFalse())
|
||||
Expect(depClusters).Should(BeNil())
|
||||
}, 60*time.Second).Should(Succeed())
|
||||
|
||||
// case5: clusters is not nil, addonClusters is nil,
|
||||
app = v1beta1.Application{}
|
||||
Expect(yaml.Unmarshal([]byte(legacy2AppYaml), &app)).Should(BeNil())
|
||||
app.SetNamespace(testns)
|
||||
Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
|
||||
Eventually(func(g Gomega) {
|
||||
needInstallAddonDep, depClusters, err := checkDependencyNeedInstall(ctx, k8sClient, "legacy-addon2", nil)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(needInstallAddonDep).Should(BeTrue())
|
||||
Expect(depClusters).Should(BeNil())
|
||||
}, 60*time.Second).Should(Succeed())
|
||||
|
||||
// case6: clusters is [local], addonClusters is ["cluster1", "cluster2"]
|
||||
Eventually(func(g Gomega) {
|
||||
needInstallAddonDep, depClusters, err := checkDependencyNeedInstall(ctx, k8sClient, "legacy-addon2", addonClusters)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(needInstallAddonDep).Should(BeTrue())
|
||||
Expect(depClusters).Should(Equal(append([]string{"local"}, addonClusters...)))
|
||||
}, 60*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("getDependencyArgs func test", func() {
|
||||
// case1: depClusters is nil
|
||||
depAddonName := "legacy-addon"
|
||||
depArgs, err := getDependencyArgs(ctx, k8sClient, depAddonName, nil)
|
||||
Expect(depArgs).Should(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
// case2: depClusters is not nil
|
||||
app = v1beta1.Application{}
|
||||
Expect(yaml.Unmarshal([]byte(legacyAppYaml), &app)).Should(BeNil())
|
||||
app.SetNamespace(testns)
|
||||
//Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
|
||||
depClusters := []string{"cluster1", "cluster2"}
|
||||
depArgs2, err := getDependencyArgs(ctx, k8sClient, depAddonName, depClusters)
|
||||
Expect(depArgs2["clusters"]).Should(Equal(depClusters))
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
// clusters exist, depClusters is nil
|
||||
sec := v1.Secret{}
|
||||
Expect(yaml.Unmarshal([]byte(secretYaml), &sec)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &sec)).Should(BeNil())
|
||||
depArgs3, err := getDependencyArgs(ctx, k8sClient, "fluxcd", nil)
|
||||
Expect(depArgs3).ToNot(BeNil())
|
||||
Expect(depArgs3["clusters"]).Should(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
// getArgs throw exception
|
||||
sec1 := v1.Secret{}
|
||||
Expect(yaml.Unmarshal([]byte(secretErrorYaml), &sec1)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &sec1)).Should(BeNil())
|
||||
depArgs4, err := getDependencyArgs(ctx, k8sClient, "fluxcd1", nil)
|
||||
Expect(depArgs4).Should(BeNil())
|
||||
Expect(err).ToNot(BeNil())
|
||||
})
|
||||
|
||||
It(" determineAddonAppName func test", func() {
|
||||
app = v1beta1.Application{}
|
||||
Expect(yaml.Unmarshal([]byte(legacyAppYaml), &app)).Should(BeNil())
|
||||
@@ -577,6 +678,60 @@ spec:
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
`
|
||||
legacy2AppYaml = `apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: legacy-addon2
|
||||
spec:
|
||||
components:
|
||||
- name: express-server
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
policies:
|
||||
- name: target-default
|
||||
type: topology
|
||||
properties:
|
||||
clusters: ["local"]
|
||||
namespace: "default"
|
||||
`
|
||||
legacy3AppYaml = `apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: legacy-addon3
|
||||
spec:
|
||||
components:
|
||||
- name: express-server
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
policies:
|
||||
- name: target-default
|
||||
type: topology
|
||||
properties:
|
||||
clusterLabelSelector: {}
|
||||
namespace: "default"
|
||||
`
|
||||
secretYaml = `apiVersion: v1
|
||||
data:
|
||||
addonParameterDataKey: eyJjbHVzdGVycyI6WyJsb2NhbCIsInZlbGEtbTEiXX0K
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: addon-secret-fluxcd
|
||||
namespace: vela-system
|
||||
type: Opaque
|
||||
`
|
||||
secretErrorYaml = `apiVersion: v1
|
||||
data:
|
||||
addonParameterDataKey: eyJjbHVzdGVycyI6WyJsb2NhbCIsInZlbGEtbTEiXQo=
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: addon-secret-fluxcd1
|
||||
namespace: vela-system
|
||||
type: Opaque
|
||||
`
|
||||
deployYaml = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
||||
@@ -83,17 +83,21 @@ type WholeAddonPackage struct {
|
||||
|
||||
// Meta defines the format for a single addon
|
||||
type Meta struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
// UXPlugins used for velaux plugins download/install with the use of addon registry.
|
||||
UXPlugins map[string]string `json:"uxPlugins,omitempty"`
|
||||
DeployTo *DeployTo `json:"deployTo,omitempty"`
|
||||
Dependencies []*Dependency `json:"dependencies,omitempty"`
|
||||
NeedNamespace []string `json:"needNamespace,omitempty"`
|
||||
Invisible bool `json:"invisible"`
|
||||
SystemRequirements *SystemRequirements `json:"system,omitempty"`
|
||||
// Annotations used for addon maintainers to add their own description or extensions to metadata.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// DeployTo defines where the addon to deploy to
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kubevela/pkg/multicluster"
|
||||
"github.com/pkg/errors"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -70,6 +71,7 @@ type Template struct {
|
||||
// It returns a helper struct, Template, which will be used for further
|
||||
// processing.
|
||||
func LoadTemplate(ctx context.Context, dm discoverymapper.DiscoveryMapper, cli client.Reader, capName string, capType types.CapType) (*Template, error) {
|
||||
ctx = multicluster.WithCluster(ctx, multicluster.Local)
|
||||
// Application Controller only load template from ComponentDefinition and TraitDefinition
|
||||
switch capType {
|
||||
case types.TypeComponentDefinition, types.TypeWorkload:
|
||||
|
||||
@@ -1434,6 +1434,111 @@ var _ = Describe("Test Application Controller", func() {
|
||||
Expect(checkApp.Status.Workflow.Mode).Should(BeEquivalentTo(fmt.Sprintf("%s-%s", workflowv1alpha1.WorkflowModeDAG, workflowv1alpha1.WorkflowModeStep)))
|
||||
})
|
||||
|
||||
It("application with mode in workflow step group", func() {
|
||||
ns := corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-with-group-mode",
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(BeNil())
|
||||
healthComponentDef := &v1beta1.ComponentDefinition{}
|
||||
hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml))
|
||||
Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil())
|
||||
healthComponentDef.Name = "worker-with-health"
|
||||
healthComponentDef.Namespace = "app-with-group-mode"
|
||||
Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil())
|
||||
app := &v1beta1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-with-group-mode",
|
||||
Namespace: "app-with-group-mode",
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: "myweb1",
|
||||
Type: "worker-with-health",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
|
||||
},
|
||||
{
|
||||
Name: "myweb3",
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
|
||||
},
|
||||
{
|
||||
Name: "myweb2",
|
||||
Type: "worker-with-health",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
|
||||
},
|
||||
},
|
||||
Workflow: &v1beta1.Workflow{
|
||||
Mode: &workflowv1alpha1.WorkflowExecuteMode{
|
||||
Steps: workflowv1alpha1.WorkflowModeDAG,
|
||||
},
|
||||
Steps: []workflowv1alpha1.WorkflowStep{
|
||||
{
|
||||
WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
|
||||
Name: "myweb1",
|
||||
Type: "apply-component",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)},
|
||||
},
|
||||
},
|
||||
{
|
||||
WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
|
||||
Name: "myweb2",
|
||||
Type: "step-group",
|
||||
},
|
||||
Mode: workflowv1alpha1.WorkflowModeStep,
|
||||
SubSteps: []workflowv1alpha1.WorkflowStepBase{
|
||||
{
|
||||
Name: "myweb2-sub1",
|
||||
Type: "apply-component",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)},
|
||||
},
|
||||
{
|
||||
Name: "myweb2-sub2",
|
||||
Type: "apply-component",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Create(context.Background(), app)).Should(BeNil())
|
||||
appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name}
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
|
||||
expDeployment := &v1.Deployment{}
|
||||
web3Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb3"}
|
||||
Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{})
|
||||
|
||||
web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"}
|
||||
Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil())
|
||||
expDeployment.Status.Replicas = 1
|
||||
expDeployment.Status.ReadyReplicas = 1
|
||||
Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil())
|
||||
web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"}
|
||||
Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil())
|
||||
expDeployment.Status.Replicas = 1
|
||||
expDeployment.Status.ReadyReplicas = 1
|
||||
Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil())
|
||||
|
||||
testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
|
||||
Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(BeNil())
|
||||
|
||||
checkApp := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning))
|
||||
Expect(checkApp.Status.Workflow.Mode).Should(BeEquivalentTo(fmt.Sprintf("%s-%s", workflowv1alpha1.WorkflowModeDAG, workflowv1alpha1.WorkflowModeDAG)))
|
||||
})
|
||||
|
||||
It("application with sub steps", func() {
|
||||
ns := corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
@@ -235,6 +235,9 @@ const (
|
||||
|
||||
// AnnotationAddonDefinitionBondCompKey indicates the definition in addon bond component.
|
||||
AnnotationAddonDefinitionBondCompKey = "addon.oam.dev/bind-component"
|
||||
|
||||
// AnnotationSkipResume annotation indicates that the resource does not need to be resumed.
|
||||
AnnotationSkipResume = "controller.core.oam.dev/skip-resume"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -51,13 +51,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
)
|
||||
|
||||
var (
|
||||
// KindDeployment is the k8s Deployment kind.
|
||||
KindDeployment = reflect.TypeOf(appsv1.Deployment{}).Name()
|
||||
// KindService is the k8s Service kind.
|
||||
KindService = reflect.TypeOf(corev1.Service{}).Name()
|
||||
)
|
||||
|
||||
const (
|
||||
// TraitPrefixKey is prefix of trait name
|
||||
TraitPrefixKey = "trait"
|
||||
@@ -870,28 +863,6 @@ func MergeMapOverrideWithDst(src, dst map[string]string) map[string]string {
|
||||
return r
|
||||
}
|
||||
|
||||
// ConvertComponentDef2WorkloadDef help convert a ComponentDefinition to WorkloadDefinition
|
||||
func ConvertComponentDef2WorkloadDef(dm discoverymapper.DiscoveryMapper, componentDef *v1beta1.ComponentDefinition,
|
||||
workloadDef *v1beta1.WorkloadDefinition) error {
|
||||
var reference common.DefinitionReference
|
||||
reference, err := ConvertWorkloadGVK2Definition(dm, componentDef.Spec.Workload.Definition)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create DefinitionReference fail %w", err)
|
||||
}
|
||||
|
||||
workloadDef.SetName(componentDef.Name)
|
||||
workloadDef.SetNamespace(componentDef.Namespace)
|
||||
workloadDef.SetLabels(componentDef.Labels)
|
||||
workloadDef.SetAnnotations(componentDef.Annotations)
|
||||
workloadDef.Spec.Reference = reference
|
||||
workloadDef.Spec.ChildResourceKinds = componentDef.Spec.ChildResourceKinds
|
||||
workloadDef.Spec.Extension = componentDef.Spec.Extension
|
||||
workloadDef.Spec.RevisionLabel = componentDef.Spec.RevisionLabel
|
||||
workloadDef.Spec.Status = componentDef.Spec.Status
|
||||
workloadDef.Spec.Schematic = componentDef.Spec.Schematic
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractComponentName will extract the componentName from a revisionName
|
||||
func ExtractComponentName(revisionName string) string {
|
||||
splits := strings.Split(revisionName, "-")
|
||||
|
||||
@@ -37,12 +37,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/condition"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/mock"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
@@ -1560,128 +1558,6 @@ func TestGetScopeDefinition(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertComponentDef2WorkloadDef(t *testing.T) {
|
||||
var cd = v1beta1.ComponentDefinition{}
|
||||
mapper := mock.NewMockDiscoveryMapper()
|
||||
|
||||
var componentDefWithWrongDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
spec:
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: /apps/v1/
|
||||
kind: Deployment
|
||||
`
|
||||
cd = v1beta1.ComponentDefinition{}
|
||||
err := yaml.Unmarshal([]byte(componentDefWithWrongDefinition), &cd)
|
||||
assert.Equal(t, nil, err)
|
||||
err = util.ConvertComponentDef2WorkloadDef(mapper, &cd, &v1beta1.WorkloadDefinition{})
|
||||
assert.Error(t, err)
|
||||
|
||||
mapper.MockRESTMapping = mock.NewMockRESTMapping("deployments")
|
||||
var Template = `
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: {
|
||||
selector: matchLabels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
template: {
|
||||
metadata: labels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
spec: {
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
// +usage=Commands to run in the container
|
||||
cmd?: [...string]
|
||||
}
|
||||
`
|
||||
var componentDefWithDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: vela-system
|
||||
labels:
|
||||
env: test
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend."
|
||||
spec:
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
status:
|
||||
healthPolicy: |
|
||||
isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == context.output.status.replicas)` + Template
|
||||
|
||||
var expectWorkloadDef = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
namespace: vela-system
|
||||
labels:
|
||||
env: test
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend."
|
||||
spec:
|
||||
definitionRef:
|
||||
name: deployments.apps
|
||||
version: v1
|
||||
childResourceKinds:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
status:
|
||||
healthPolicy: |
|
||||
isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == context.output.status.replicas)` + Template
|
||||
cd = v1beta1.ComponentDefinition{}
|
||||
wd := &v1beta1.WorkloadDefinition{}
|
||||
err = yaml.Unmarshal([]byte(componentDefWithDefinition), &cd)
|
||||
assert.NoError(t, err)
|
||||
err = util.ConvertComponentDef2WorkloadDef(mapper, &cd, wd)
|
||||
assert.NoError(t, err)
|
||||
expectWd := v1beta1.WorkloadDefinition{}
|
||||
err = yaml.Unmarshal([]byte(expectWorkloadDef), &expectWd)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectWd.Namespace, wd.Namespace)
|
||||
assert.Equal(t, expectWd.Name, wd.Name)
|
||||
assert.Equal(t, expectWd.Labels, wd.Labels)
|
||||
assert.Equal(t, expectWd.Annotations, wd.Annotations)
|
||||
assert.Equal(t, expectWd.Spec.Reference, wd.Spec.Reference)
|
||||
assert.Equal(t, expectWd.Spec.ChildResourceKinds, wd.Spec.ChildResourceKinds)
|
||||
assert.Equal(t, expectWd.Spec.Status, wd.Spec.Status)
|
||||
assert.Equal(t, expectWd.Spec.Schematic, wd.Spec.Schematic)
|
||||
}
|
||||
|
||||
func TestExtractRevisionNum(t *testing.T) {
|
||||
testcases := []struct {
|
||||
revName string
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"k8s.io/client-go/util/retry"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
|
||||
kruisev1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
@@ -62,6 +64,9 @@ func getAssociatedRollouts(ctx context.Context, cli client.Client, app *v1beta1.
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed to get kruise rollout %s/%s in cluster %s", mr.Namespace, mr.Name, mr.Cluster)
|
||||
}
|
||||
if value, ok := rollout.Annotations[oam.AnnotationSkipResume]; ok && value == "true" {
|
||||
continue
|
||||
}
|
||||
rollouts = append(rollouts, &ClusterRollout{Rollout: rollout, Cluster: mr.Cluster})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
@@ -37,11 +38,13 @@ var _ = Describe("Kruise rollout test", func() {
|
||||
Expect(k8sClient.Create(ctx, rollout.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, rt.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, app.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(ctx, rollingReleaseRollout.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
})
|
||||
|
||||
It("test get associated rollout func", func() {
|
||||
rollouts, err := getAssociatedRollouts(ctx, k8sClient, &app, false)
|
||||
Expect(err).Should(BeNil())
|
||||
// test will only fetch one rollout in result
|
||||
Expect(len(rollouts)).Should(BeEquivalentTo(1))
|
||||
})
|
||||
|
||||
@@ -120,6 +123,19 @@ var rt = v1beta1.ResourceTracker{
|
||||
Component: "my-rollout",
|
||||
},
|
||||
},
|
||||
{
|
||||
ClusterObjectReference: common.ClusterObjectReference{
|
||||
ObjectReference: v1.ObjectReference{
|
||||
APIVersion: "rollouts.kruise.io/v1alpha1",
|
||||
Kind: "Rollout",
|
||||
Name: "rolling-release-rollout",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
OAMObjectReference: common.OAMObjectReference{
|
||||
Component: "my-rollout",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -153,3 +169,36 @@ var rollout = kruisev1alpha1.Rollout{
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var rollingReleaseRollout = kruisev1alpha1.Rollout{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "rollouts.kruise.io/v1alpha1",
|
||||
Kind: "Rollout",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "rolling-release-rollout",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
oam.AnnotationSkipResume: "true",
|
||||
},
|
||||
},
|
||||
Spec: kruisev1alpha1.RolloutSpec{
|
||||
ObjectRef: kruisev1alpha1.ObjectRef{
|
||||
WorkloadRef: &kruisev1alpha1.WorkloadRef{
|
||||
APIVersion: "appsv1",
|
||||
Kind: "Deployment",
|
||||
Name: "canary-demo",
|
||||
},
|
||||
},
|
||||
Strategy: kruisev1alpha1.RolloutStrategy{
|
||||
Canary: &kruisev1alpha1.CanaryStrategy{
|
||||
Steps: []kruisev1alpha1.CanaryStep{
|
||||
{
|
||||
Weight: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
Paused: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -126,12 +126,12 @@ func init() {
|
||||
|
||||
// HTTPOption define the https options
|
||||
type HTTPOption struct {
|
||||
Username string
|
||||
Password string
|
||||
CaFile string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
InsecureSkipTLS bool
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
CaFile string `json:"caFile,omitempty"`
|
||||
CertFile string `json:"certFile,omitempty"`
|
||||
KeyFile string `json:"keyFile,omitempty"`
|
||||
InsecureSkipTLS bool `json:"insecureSkipTLS,omitempty"`
|
||||
}
|
||||
|
||||
// InitBaseRestConfig will return reset config for create controller runtime client
|
||||
|
||||
@@ -115,16 +115,6 @@ func SuspendWorkflow(ctx context.Context, kubecli client.Client, app *v1beta1.Ap
|
||||
found := stepName == ""
|
||||
|
||||
for i, step := range steps {
|
||||
if step.Phase != workflowv1alpha1.WorkflowStepPhaseRunning {
|
||||
continue
|
||||
}
|
||||
if stepName == "" {
|
||||
wfUtils.OperateSteps(steps, i, -1, workflowv1alpha1.WorkflowStepPhaseSuspending)
|
||||
} else if stepName == step.Name {
|
||||
wfUtils.OperateSteps(steps, i, -1, workflowv1alpha1.WorkflowStepPhaseSuspending)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
for j, sub := range step.SubStepsStatus {
|
||||
if sub.Phase != workflowv1alpha1.WorkflowStepPhaseRunning {
|
||||
continue
|
||||
@@ -137,6 +127,16 @@ func SuspendWorkflow(ctx context.Context, kubecli client.Client, app *v1beta1.Ap
|
||||
break
|
||||
}
|
||||
}
|
||||
if step.Phase != workflowv1alpha1.WorkflowStepPhaseRunning {
|
||||
continue
|
||||
}
|
||||
if stepName == "" {
|
||||
wfUtils.OperateSteps(steps, i, -1, workflowv1alpha1.WorkflowStepPhaseSuspending)
|
||||
} else if stepName == step.Name {
|
||||
wfUtils.OperateSteps(steps, i, -1, workflowv1alpha1.WorkflowStepPhaseSuspending)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("can not find step %s", stepName)
|
||||
@@ -209,16 +209,6 @@ func ResumeWorkflow(ctx context.Context, kubecli client.Client, app *v1beta1.App
|
||||
found := stepName == ""
|
||||
|
||||
for i, step := range steps {
|
||||
if step.Phase != workflowv1alpha1.WorkflowStepPhaseSuspending {
|
||||
continue
|
||||
}
|
||||
if stepName == "" {
|
||||
wfUtils.OperateSteps(steps, i, -1, workflowv1alpha1.WorkflowStepPhaseRunning)
|
||||
} else if stepName == step.Name {
|
||||
wfUtils.OperateSteps(steps, i, -1, workflowv1alpha1.WorkflowStepPhaseRunning)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
for j, sub := range step.SubStepsStatus {
|
||||
if sub.Phase != workflowv1alpha1.WorkflowStepPhaseSuspending {
|
||||
continue
|
||||
@@ -231,6 +221,16 @@ func ResumeWorkflow(ctx context.Context, kubecli client.Client, app *v1beta1.App
|
||||
break
|
||||
}
|
||||
}
|
||||
if step.Phase != workflowv1alpha1.WorkflowStepPhaseSuspending {
|
||||
continue
|
||||
}
|
||||
if stepName == "" {
|
||||
wfUtils.OperateSteps(steps, i, -1, workflowv1alpha1.WorkflowStepPhaseRunning)
|
||||
} else if stepName == step.Name {
|
||||
wfUtils.OperateSteps(steps, i, -1, workflowv1alpha1.WorkflowStepPhaseRunning)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
@@ -489,7 +489,7 @@ func TerminateWorkflow(ctx context.Context, kubecli client.Client, app *v1beta1.
|
||||
if step.Reason != wfTypes.StatusReasonFailedAfterRetries && step.Reason != wfTypes.StatusReasonTimeout {
|
||||
steps[i].Reason = wfTypes.StatusReasonTerminate
|
||||
}
|
||||
case workflowv1alpha1.WorkflowStepPhaseRunning:
|
||||
case workflowv1alpha1.WorkflowStepPhaseRunning, workflowv1alpha1.WorkflowStepPhaseSuspending:
|
||||
steps[i].Phase = workflowv1alpha1.WorkflowStepPhaseFailed
|
||||
steps[i].Reason = wfTypes.StatusReasonTerminate
|
||||
default:
|
||||
@@ -500,7 +500,7 @@ func TerminateWorkflow(ctx context.Context, kubecli client.Client, app *v1beta1.
|
||||
if sub.Reason != wfTypes.StatusReasonFailedAfterRetries && sub.Reason != wfTypes.StatusReasonTimeout {
|
||||
steps[i].SubStepsStatus[j].Reason = wfTypes.StatusReasonTerminate
|
||||
}
|
||||
case workflowv1alpha1.WorkflowStepPhaseRunning:
|
||||
case workflowv1alpha1.WorkflowStepPhaseRunning, workflowv1alpha1.WorkflowStepPhaseSuspending:
|
||||
steps[i].SubStepsStatus[j].Phase = workflowv1alpha1.WorkflowStepPhaseFailed
|
||||
steps[i].SubStepsStatus[j].Reason = wfTypes.StatusReasonTerminate
|
||||
default:
|
||||
|
||||
@@ -72,6 +72,17 @@ var _ = Describe("Kruise rollout test", func() {
|
||||
It("Terminate workflow", func() {
|
||||
checkApp := v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: "default", Name: "opt-app"}, &checkApp)).Should(BeNil())
|
||||
checkApp.Status.Workflow = &common.WorkflowStatus{
|
||||
Steps: []workflowv1alpha1.WorkflowStepStatus{
|
||||
{
|
||||
StepStatus: workflowv1alpha1.StepStatus{
|
||||
Name: "step1",
|
||||
Type: "suspend",
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseSuspending,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
operator := NewApplicationWorkflowOperator(k8sClient, nil, checkApp.DeepCopy())
|
||||
Expect(operator.Terminate(ctx)).Should(BeNil())
|
||||
checkApp = v1beta1.Application{}
|
||||
@@ -98,7 +109,7 @@ var _ = Describe("Kruise rollout test", func() {
|
||||
StepStatus: workflowv1alpha1.StepStatus{
|
||||
Name: "step1",
|
||||
Type: "suspend",
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseRunning,
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseSuspending,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -87,9 +87,11 @@ import "list"
|
||||
for key, kinds in resourceCategoryMap if list.Contains(kinds, r.kind) {
|
||||
_category: key
|
||||
},
|
||||
_cluster: r.metadata.annotations["app.oam.dev/cluster"]
|
||||
if r.metadata.annotations != _|_ if r.metadata.annotations["app.oam.dev/cluster"] != _|_ {
|
||||
_cluster: r.metadata.annotations["app.oam.dev/cluster"]
|
||||
}
|
||||
}]
|
||||
_clusters: [ for r in _resources {r._cluster} ]
|
||||
_clusters: [ for r in _resources if r._cluster != _|_ {r._cluster} ]
|
||||
resourceMap: {
|
||||
for key, val in resourceCategoryMap {
|
||||
"\(key)": [ for r in _resources if r._category == key {r}]
|
||||
@@ -111,7 +113,9 @@ import "list"
|
||||
apiVersion: r.apiVersion
|
||||
kind: r.kind
|
||||
metadata: name: r.metadata.name
|
||||
metadata: annotations: "app.oam.dev/cluster": "\(r._cluster)"
|
||||
if r._cluster != _|_ {
|
||||
metadata: annotations: "app.oam.dev/cluster": (r._cluster)
|
||||
}
|
||||
}]
|
||||
},
|
||||
if len(resourceMap.ns) > 0 {
|
||||
@@ -121,7 +125,9 @@ import "list"
|
||||
apiVersion: r.apiVersion
|
||||
kind: r.kind
|
||||
metadata: name: r.metadata.name
|
||||
metadata: annotations: "app.oam.dev/cluster": "\(r._cluster)"
|
||||
if r._cluster != _|_ {
|
||||
metadata: annotations: "app.oam.dev/cluster": (r._cluster)
|
||||
}
|
||||
}]
|
||||
},
|
||||
for r in resourceMap.workload + resourceMap.service {
|
||||
@@ -147,7 +153,9 @@ import "list"
|
||||
if r.metadata.namespace != _|_ {
|
||||
metadata: namespace: r.metadata.namespace
|
||||
}
|
||||
metadata: annotations: "app.oam.dev/cluster": "\(r._cluster)"
|
||||
if r._cluster != _|_ {
|
||||
metadata: annotations: "app.oam.dev/cluster": (r._cluster)
|
||||
}
|
||||
}]
|
||||
},
|
||||
for kind, rs in unknownByKinds {
|
||||
@@ -157,19 +165,21 @@ import "list"
|
||||
apiVersion: r.apiVersion
|
||||
kind: r.kind
|
||||
metadata: name: r.metadata.name
|
||||
metadata: annotations: "app.oam.dev/cluster": "\(r._cluster)"
|
||||
if r._cluster != _|_ {
|
||||
metadata: annotations: "app.oam.dev/cluster": (r._cluster)
|
||||
}
|
||||
}]
|
||||
},
|
||||
]
|
||||
|
||||
clusterCompMap: {
|
||||
for cluster in _clusters {
|
||||
"\(cluster)": [ for comp in comps if comp.properties.objects[0].metadata.annotations["app.oam.dev/cluster"] == cluster {comp.name} ]
|
||||
"\(cluster)": [ for comp in comps if comp.properties.objects[0].metadata.annotations != _|_ if comp.properties.objects[0].metadata.annotations["app.oam.dev/cluster"] == cluster {comp.name} ]
|
||||
}
|
||||
}
|
||||
|
||||
compClusterMap: {
|
||||
for comp in comps {
|
||||
for comp in comps if comp.properties.objects[0].metadata.annotations != _|_ {
|
||||
"\(comp.name)": comp.properties.objects[0].metadata.annotations["app.oam.dev/cluster"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,9 +288,9 @@ func (opt *AdoptOptions) MultipleRun(f velacmd.Factory, cmd *cobra.Command) erro
|
||||
_, _ = fmt.Fprintf(opt.Out, "Warning: failed to list resources from %s/%s: %s", apiVersion, kind, err.Error())
|
||||
continue
|
||||
}
|
||||
engine := resourcetopology.New(opt.ResourceTopologyRule)
|
||||
dedup := make([]k8s.ResourceIdentifier, 0)
|
||||
for _, item := range list.Items {
|
||||
engine := resourcetopology.New(opt.ResourceTopologyRule)
|
||||
itemIdentifier := k8s.ResourceIdentifier{
|
||||
Name: item.GetName(),
|
||||
Namespace: item.GetNamespace(),
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -58,8 +58,10 @@ commonPeerResources: [{
|
||||
resource: "configMap"
|
||||
selectors: {
|
||||
name: [
|
||||
for v in context.data.spec.template.spec.volumes if v.configMap != _|_ if v.configMap.name != _|_ {
|
||||
v.configMap.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
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -68,8 +70,10 @@ commonPeerResources: [{
|
||||
resource: "secret"
|
||||
selectors: {
|
||||
name: [
|
||||
for v in context.data.spec.template.spec.volumes if v.secret != _|_ if v.secret.name != _|_ {
|
||||
v.secret.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
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -254,6 +254,7 @@ type WorkflowArgs struct {
|
||||
Writer io.Writer
|
||||
Args common.Args
|
||||
StepName string
|
||||
StepID string
|
||||
ErrMap map[string]string
|
||||
App *v1beta1.Application
|
||||
WorkflowRun *workflowv1alpha1.WorkflowRun
|
||||
@@ -412,7 +413,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)
|
||||
@@ -467,7 +468,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
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/helm"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
@@ -48,16 +47,6 @@ func InstallComponentDefinition(client client.Client, componentData []byte, ioSt
|
||||
}
|
||||
cd.Namespace = types.DefaultKubeVelaNS
|
||||
ioStreams.Info("Installing component: " + cd.Name)
|
||||
if tp.Install != nil {
|
||||
tp.Source.ChartName = tp.Install.Helm.Name
|
||||
if err = helm.InstallHelmChart(ioStreams, tp.Install.Helm); err != nil {
|
||||
return err
|
||||
}
|
||||
err = addSourceIntoExtension(cd.Spec.Extension, tp.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if cd.Spec.Workload.Type == "" {
|
||||
tp.CrdInfo = &types.CRDInfo{
|
||||
APIVersion: cd.Spec.Workload.Definition.APIVersion,
|
||||
@@ -79,19 +68,6 @@ func InstallTraitDefinition(client client.Client, mapper discoverymapper.Discove
|
||||
}
|
||||
td.Namespace = types.DefaultKubeVelaNS
|
||||
ioStreams.Info("Installing trait " + td.Name)
|
||||
if cap.Install != nil {
|
||||
cap.Source.ChartName = cap.Install.Helm.Name
|
||||
if err = helm.InstallHelmChart(ioStreams, cap.Install.Helm); err != nil {
|
||||
return err
|
||||
}
|
||||
err = addSourceIntoExtension(td.Spec.Extension, cap.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = HackForStandardTrait(*cap, client); err != nil {
|
||||
return err
|
||||
}
|
||||
gvk, err := util.GetGVKFromDefinition(mapper, td.Spec.Reference)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -109,25 +85,6 @@ func InstallTraitDefinition(client client.Client, mapper discoverymapper.Discove
|
||||
return nil
|
||||
}
|
||||
|
||||
// HackForStandardTrait will do some hack install for standard registry
|
||||
func HackForStandardTrait(tp types.Capability, client client.Client) error {
|
||||
switch tp.Name {
|
||||
case "metrics":
|
||||
// metrics trait will rely on a Prometheus instance to be installed
|
||||
// make sure the chart is a prometheus operator
|
||||
if tp.Install == nil {
|
||||
break
|
||||
}
|
||||
if tp.Install.Helm.Namespace == "monitoring" && tp.Install.Helm.Name == "kube-prometheus-stack" {
|
||||
if err := InstallPrometheusInstance(client); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addSourceIntoExtension(in *runtime.RawExtension, source *types.Source) error {
|
||||
var extension map[string]interface{}
|
||||
err := json.Unmarshal(in.Raw, &extension)
|
||||
|
||||
@@ -19,7 +19,6 @@ package docgen
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -42,8 +41,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/helm"
|
||||
util2 "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
"github.com/oam-dev/kubevela/references/docgen/fix"
|
||||
)
|
||||
|
||||
@@ -300,16 +297,9 @@ func GetPolicies(ctx context.Context, namespace string, c common.Args) ([]types.
|
||||
return templates, templateErrors, nil
|
||||
}
|
||||
|
||||
// validateCapabilities validates whether helm charts are successful installed, GVK are successfully retrieved.
|
||||
// validateCapabilities validates whether GVK are successfully retrieved.
|
||||
func validateCapabilities(tmp *types.Capability, dm discoverymapper.DiscoveryMapper, definitionName string, reference commontypes.DefinitionReference) error {
|
||||
var err error
|
||||
if tmp.Install != nil {
|
||||
tmp.Source = &types.Source{ChartName: tmp.Install.Helm.Name}
|
||||
ioStream := util2.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
|
||||
if err = helm.InstallHelmChart(ioStream, tmp.Install.Helm); err != nil {
|
||||
return fmt.Errorf("unable to install helm chart dependency %s(%s from %s) for this trait '%s': %w ", tmp.Install.Helm.Name, tmp.Install.Helm.Version, tmp.Install.Helm.URL, definitionName, err)
|
||||
}
|
||||
}
|
||||
gvk, err := util.GetGVKFromDefinition(dm, reference)
|
||||
if err != nil {
|
||||
errMsg := err.Error()
|
||||
|
||||
@@ -991,5 +991,32 @@ var _ = Describe("Test multicluster scenario", func() {
|
||||
g.Expect(app.Status.Services[0].Traits[0].Healthy).Should(BeTrue())
|
||||
}).WithTimeout(20 * time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test application carrying deploy step with inline policy", func() {
|
||||
ctx := context.Background()
|
||||
wsDef := &v1beta1.WorkflowStepDefinition{}
|
||||
bs, err := os.ReadFile("./testdata/def/inline-deploy.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(yaml.Unmarshal(bs, wsDef)).Should(Succeed())
|
||||
wsDef.SetNamespace(namespace)
|
||||
Expect(k8sClient.Create(ctx, wsDef)).Should(Succeed())
|
||||
app := &v1beta1.Application{}
|
||||
bs, err = os.ReadFile("./testdata/app/app-carrying-deploy-step-with-inline-policy.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
|
||||
app.SetNamespace(namespace)
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
|
||||
}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
Eventually(func(g Gomega) {
|
||||
_app := &v1beta1.Application{}
|
||||
g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
|
||||
g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
|
||||
_deploy := &appsv1.Deployment{}
|
||||
Expect(k8sClient.Get(ctx, appKey, _deploy)).Should(Succeed())
|
||||
Expect(int(*_deploy.Spec.Replicas)).Should(Equal(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
19
test/e2e-multicluster-test/testdata/app/app-carrying-deploy-step-with-inline-policy.yaml
vendored
Normal file
19
test/e2e-multicluster-test/testdata/app/app-carrying-deploy-step-with-inline-policy.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: test
|
||||
spec:
|
||||
components:
|
||||
- name: test
|
||||
type: webservice
|
||||
properties:
|
||||
image: nginx:1.20
|
||||
policies:
|
||||
- type: topology
|
||||
name: topo
|
||||
properties:
|
||||
clusters: ["cluster-worker"]
|
||||
workflow:
|
||||
steps:
|
||||
- type: inline-deploy
|
||||
name: deploy
|
||||
25
test/e2e-multicluster-test/testdata/def/inline-deploy.yaml
vendored
Normal file
25
test/e2e-multicluster-test/testdata/def/inline-deploy.yaml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
name: inline-deploy
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import "vela/op"
|
||||
deploy: op.#Deploy & {
|
||||
policies: []
|
||||
parallelism: 5
|
||||
ignoreTerraformComponent: true
|
||||
inlinePolicies: [{
|
||||
type: "override"
|
||||
name: "set-replica"
|
||||
properties: components: [{
|
||||
traits: [{
|
||||
type: "scaler"
|
||||
properties: replicas: 0
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
parameter: {}
|
||||
@@ -76,7 +76,7 @@ var _ = Describe("HealthScope", func() {
|
||||
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test an application with health policy", func() {
|
||||
PIt("Test an application with health policy", func() {
|
||||
By("Apply a healthy application")
|
||||
var newApp v1beta1.Application
|
||||
var healthyAppName, unhealthyAppName string
|
||||
|
||||
@@ -3,13 +3,7 @@
|
||||
annotations: {}
|
||||
labels: {}
|
||||
description: "Describes cron jobs that run code or a script to completion."
|
||||
attributes: workload: {
|
||||
definition: {
|
||||
apiVersion: "batch/v1beta1"
|
||||
kind: "CronJob"
|
||||
}
|
||||
type: "cronjobs.batch"
|
||||
}
|
||||
attributes: workload: type: "autodetects.core.oam.dev"
|
||||
}
|
||||
template: {
|
||||
output: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
expose: {
|
||||
@@ -54,22 +55,66 @@ template: {
|
||||
metadata: name: context.name
|
||||
metadata: annotations: parameter.annotations
|
||||
spec: {
|
||||
selector: "app.oam.dev/component": context.name
|
||||
ports: [
|
||||
for p in parameter.port {
|
||||
name: "port-" + strconv.FormatInt(p, 10)
|
||||
port: p
|
||||
targetPort: p
|
||||
if parameter["matchLabels"] == _|_ {
|
||||
selector: "app.oam.dev/component": context.name
|
||||
}
|
||||
if parameter["matchLabels"] != _|_ {
|
||||
selector: parameter["matchLabels"]
|
||||
}
|
||||
|
||||
// compatible with the old way
|
||||
if parameter["port"] != _|_ if parameter["ports"] == _|_ {
|
||||
ports: [
|
||||
for p in parameter.port {
|
||||
name: "port-" + strconv.FormatInt(p, 10)
|
||||
port: p
|
||||
targetPort: p
|
||||
},
|
||||
]
|
||||
}
|
||||
if parameter["ports"] != _|_ {
|
||||
ports: [ for v in parameter.ports {
|
||||
port: v.port
|
||||
targetPort: v.port
|
||||
if v.name != _|_ {
|
||||
name: v.name
|
||||
}
|
||||
if v.name == _|_ {
|
||||
_name: "port-" + strconv.FormatInt(v.port, 10)
|
||||
name: *_name | string
|
||||
if v.protocol != "TCP" {
|
||||
name: _name + "-" + strings.ToLower(v.protocol)
|
||||
}
|
||||
}
|
||||
if v.nodePort != _|_ if parameter.type == "NodePort" {
|
||||
nodePort: v.nodePort
|
||||
}
|
||||
if v.protocol != _|_ {
|
||||
protocol: v.protocol
|
||||
}
|
||||
},
|
||||
]
|
||||
]
|
||||
}
|
||||
type: parameter.type
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the exposion ports
|
||||
port: [...int]
|
||||
// +usage=Deprecated, the old way to specify the exposion ports
|
||||
port?: [...int]
|
||||
// +usage=Specify portsyou want customer traffic sent to
|
||||
ports?: [...{
|
||||
// +usage=Number of port to expose on the pod's IP address
|
||||
port: int
|
||||
// +usage=Name of the port
|
||||
name?: string
|
||||
// +usage=Protocol for port. Must be UDP, TCP, or SCTP
|
||||
protocol: *"TCP" | "UDP" | "SCTP"
|
||||
// +usage=exposed node port. Only Valid when exposeType is NodePort
|
||||
nodePort?: int
|
||||
}]
|
||||
// +usage=Specify the annotaions of the exposed service
|
||||
annotations: [string]: string
|
||||
annotations: [string]: string
|
||||
matchLabels?: [string]: string
|
||||
// +usage=Specify what kind of Service you want. options: "ClusterIP","NodePort","LoadBalancer","ExternalName"
|
||||
type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user