mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-02 17:50:58 +00:00
Compare commits
36 Commits
v1.7.0-alp
...
v1.7.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ee5915546 | ||
|
|
3f6b38cc7f | ||
|
|
c2c7ab91f9 | ||
|
|
866ffb9689 | ||
|
|
61afb366ee | ||
|
|
a2a8d73a58 | ||
|
|
fc3b428788 | ||
|
|
500dc52b34 | ||
|
|
789aa38476 | ||
|
|
17adf35717 | ||
|
|
586f266798 | ||
|
|
b8dcbe4964 | ||
|
|
778579c79b | ||
|
|
69293f4094 | ||
|
|
3a917cb6af | ||
|
|
1c43c6d1c5 | ||
|
|
9bbf7bf01b | ||
|
|
5a845104fb | ||
|
|
693eb3cb1d | ||
|
|
78f5827fa6 | ||
|
|
af8a7eb695 | ||
|
|
97ce8ba500 | ||
|
|
03d892bcf1 | ||
|
|
360c9e24b2 | ||
|
|
ba0a726cfc | ||
|
|
b2f4e237c2 | ||
|
|
5f71d05db1 | ||
|
|
340059989b | ||
|
|
5b636e451a | ||
|
|
ecc77f8cae | ||
|
|
8a5239575a | ||
|
|
6461625832 | ||
|
|
28f6f42ed4 | ||
|
|
7bc2f4e8d1 | ||
|
|
d588def0af | ||
|
|
6c12b968a7 |
2
.github/bot.md
vendored
2
.github/bot.md
vendored
@@ -1,6 +1,6 @@
|
||||
### GitHub & kubevela automation
|
||||
|
||||
The bot is configured via [issue-commands.json](https://github.com/kubevela/kubevela/blob/master/.github/workflows/issue-commands.json)
|
||||
The bot is configured via [issue-commands.json](https://github.com/kubevela/kubevela/blob/master/.github/issue-commands.json)
|
||||
and some other GitHub [workflows](https://github.com/kubevela/kubevela/blob/master/.github/workflows).
|
||||
By default, users with write access to the repo is allowed to use the comments,
|
||||
the [userlist](https://github.com/kubevela/kubevela/blob/master/.github/comment.userlist)
|
||||
|
||||
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
@@ -18,6 +18,15 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read
|
||||
checks: write
|
||||
issues: read
|
||||
packages: write
|
||||
pull-requests: read
|
||||
repository-projects: read
|
||||
statuses: read
|
||||
runs-on: ubuntu-latest
|
||||
name: build
|
||||
strategy:
|
||||
@@ -130,6 +139,15 @@ jobs:
|
||||
./ossutil --config-file .ossutilconfig cp -u ./latest_version oss://$BUCKET/binary/vela/latest_version
|
||||
|
||||
upload-plugin-homebrew:
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read
|
||||
checks: write
|
||||
issues: read
|
||||
packages: write
|
||||
pull-requests: read
|
||||
repository-projects: read
|
||||
statuses: read
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
name: upload-sha256sums
|
||||
|
||||
@@ -336,7 +336,8 @@ type WorkflowStatus struct {
|
||||
Steps []workflowv1alpha1.WorkflowStepStatus `json:"steps,omitempty"`
|
||||
|
||||
StartTime metav1.Time `json:"startTime,omitempty"`
|
||||
EndTime metav1.Time `json:"endTime,omitempty"`
|
||||
// +nullable
|
||||
EndTime metav1.Time `json:"endTime,omitempty"`
|
||||
}
|
||||
|
||||
// DefinitionType describes the type of DefinitionRevision.
|
||||
|
||||
@@ -135,6 +135,8 @@ type ApplicationRevisionStatus struct {
|
||||
Succeeded bool `json:"succeeded"`
|
||||
// Workflow the running status of the workflow
|
||||
Workflow *common.WorkflowStatus `json:"workflow,omitempty"`
|
||||
// Record the context values to the revision.
|
||||
WorkflowContext map[string]string `json:"workflowContext,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
@@ -293,6 +293,13 @@ func (in *ApplicationRevisionStatus) DeepCopyInto(out *ApplicationRevisionStatus
|
||||
*out = new(common.WorkflowStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.WorkflowContext != nil {
|
||||
in, out := &in.WorkflowContext, &out.WorkflowContext
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationRevisionStatus.
|
||||
|
||||
@@ -99,6 +99,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `featureGates.multiStageComponentApply` | if enabled, the multiStageComponentApply feature will be combined with the stage field in TraitDefinition to complete the multi-stage apply. | `false` |
|
||||
| `featureGates.gzipApplicationRevision` | compress apprev using gzip (good) before being stored. This is reduces network throughput when dealing with huge apprevs. | `false` |
|
||||
| `featureGates.zstdApplicationRevision` | compress apprev using zstd (fast and good) before being stored. This is reduces network throughput when dealing with huge apprevs. Note that zstd will be prioritized if you enable other compression options. | `true` |
|
||||
| `featureGates.preDispatchDryRun` | enable dryrun before dispatching resources. Enable this flag can help prevent unsuccessful dispatch resources entering resourcetracker and improve the user experiences of gc but at the cost of increasing network requests. | `true` |
|
||||
|
||||
|
||||
### MultiCluster parameters
|
||||
@@ -110,7 +111,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `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.4.0` |
|
||||
| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.7.0-alpha.3` |
|
||||
| `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` |
|
||||
@@ -149,7 +150,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `kubeClient.qps` | The qps for reconcile clients, default is 100 | `100` |
|
||||
| `kubeClient.burst` | The burst for reconcile clients, default is 200 | `200` |
|
||||
| `authentication.enabled` | Enable authentication for application | `false` |
|
||||
| `authentication.withUser` | Application authentication will impersonate as the request User | `false` |
|
||||
| `authentication.withUser` | Application authentication will impersonate as the request User | `true` |
|
||||
| `authentication.defaultUser` | Application authentication will impersonate as the User if no user provided in Application | `kubevela:vela-core` |
|
||||
| `authentication.groupPattern` | Application authentication will impersonate as the request Group that matches the pattern | `kubevela:*` |
|
||||
|
||||
|
||||
@@ -848,6 +848,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -2806,6 +2807,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -4942,6 +4944,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -5042,6 +5045,11 @@ spec:
|
||||
- suspend
|
||||
- terminated
|
||||
type: object
|
||||
workflowContext:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Record the context values to the revision.
|
||||
type: object
|
||||
required:
|
||||
- succeeded
|
||||
type: object
|
||||
|
||||
@@ -774,6 +774,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -1535,6 +1536,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
@@ -32,6 +32,7 @@ spec:
|
||||
- "--secure-port={{ .Values.multicluster.clusterGateway.port }}"
|
||||
- "--secret-namespace={{ .Release.Namespace }}"
|
||||
- "--feature-gates=APIPriorityAndFairness=false,ClientIdentityPenetration={{ .Values.authentication.enabled }}"
|
||||
- "--cluster-gateway-proxy-config=/etc/proxy-config/config.yaml"
|
||||
{{- if .Values.multicluster.clusterGateway.secureTLS.enabled }}
|
||||
- "--tls-cert-file={{ .Values.multicluster.clusterGateway.secureTLS.certPath }}/tls.crt"
|
||||
- "--tls-private-key-file={{ .Values.multicluster.clusterGateway.secureTLS.certPath }}/tls.key"
|
||||
@@ -42,14 +43,20 @@ spec:
|
||||
{{- toYaml .Values.multicluster.clusterGateway.resources | nindent 12 }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.multicluster.clusterGateway.port }}
|
||||
{{ if .Values.multicluster.clusterGateway.secureTLS.enabled }}
|
||||
volumeMounts:
|
||||
- mountPath: /etc/proxy-config
|
||||
name: proxy-config
|
||||
{{ if .Values.multicluster.clusterGateway.secureTLS.enabled }}
|
||||
- mountPath: {{ .Values.multicluster.clusterGateway.secureTLS.certPath }}
|
||||
name: tls-cert-vol
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{ if .Values.multicluster.clusterGateway.secureTLS.enabled }}
|
||||
volumes:
|
||||
- configMap:
|
||||
defaultMode: 420
|
||||
name: {{ .Release.Name }}-cluster-gateway-proxy-config
|
||||
name: proxy-config
|
||||
{{ if .Values.multicluster.clusterGateway.secureTLS.enabled }}
|
||||
- name: tls-cert-vol
|
||||
secret:
|
||||
defaultMode: 420
|
||||
@@ -74,6 +81,23 @@ spec:
|
||||
maxUnavailable: 1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-cluster-gateway-proxy-config
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
config.yaml: |
|
||||
apiVersion: cluster.core.oam.dev/v1alpha1
|
||||
kind: ClusterGatewayProxyConfiguration
|
||||
spec:
|
||||
clientIdentityExchanger:
|
||||
rules:
|
||||
- name: super-user
|
||||
source:
|
||||
group: kubevela:ux
|
||||
type: PrivilegedIdentityExchanger
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-cluster-gateway-service
|
||||
|
||||
@@ -7,7 +7,6 @@ metadata:
|
||||
definition.oam.dev/description: Apply a specific component and its corresponding traits in application
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-component
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Wait for the specified Application to complete.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: depends-on-app
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Export data to specified Kubernetes ConfigMap in your workflow.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: export2config
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Export data to Kubernetes Secret in your workflow.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: export2secret
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Generate a JDBC connection based on Component of alibaba-rds
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: generate-jdbc-connection
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
110
charts/vela-core/templates/defwithtemplate/hpa.yaml
Normal file
110
charts/vela-core/templates/defwithtemplate/hpa.yaml
Normal file
@@ -0,0 +1,110 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/hpa.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Configure k8s HPA for Deployment or Statefulsets
|
||||
name: hpa
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
- statefulsets.apps
|
||||
podDisruptive: false
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: hpa: {
|
||||
if context.clusterVersion.minor < 23 {
|
||||
apiVersion: "autoscaling/v2beta2"
|
||||
}
|
||||
if context.clusterVersion.minor >= 23 {
|
||||
apiVersion: "autoscaling/v2"
|
||||
}
|
||||
kind: "HorizontalPodAutoscaler"
|
||||
metadata: name: context.name
|
||||
spec: {
|
||||
scaleTargetRef: {
|
||||
apiVersion: parameter.targetAPIVersion
|
||||
kind: parameter.targetKind
|
||||
name: context.name
|
||||
}
|
||||
minReplicas: parameter.min
|
||||
maxReplicas: parameter.max
|
||||
metrics: [
|
||||
{
|
||||
type: "Resource"
|
||||
resource: {
|
||||
name: "cpu"
|
||||
target: {
|
||||
type: parameter.cpu.type
|
||||
if parameter.cpu.type == "Utilization" {
|
||||
averageUtilization: parameter.cpu.value
|
||||
}
|
||||
if parameter.cpu.type == "AverageValue" {
|
||||
averageValue: parameter.cpu.value
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
if parameter.mem != _|_ {
|
||||
{
|
||||
type: "Resource"
|
||||
resource: {
|
||||
name: "memory"
|
||||
target: {
|
||||
type: parameter.mem.type
|
||||
if parameter.cpu.type == "Utilization" {
|
||||
averageUtilization: parameter.cpu.value
|
||||
}
|
||||
if parameter.cpu.type == "AverageValue" {
|
||||
averageValue: parameter.cpu.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
if parameter.podCustomMetrics != _|_ for m in parameter.podCustomMetrics {
|
||||
type: "Pods"
|
||||
pods: {
|
||||
metric: name: m.name
|
||||
target: {
|
||||
type: "AverageValue"
|
||||
averageValue: m.value
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the minimal number of replicas to which the autoscaler can scale down
|
||||
min: *1 | int
|
||||
// +usage=Specify the maximum number of of replicas to which the autoscaler can scale up
|
||||
max: *10 | int
|
||||
// +usage=Specify the apiVersion of scale target
|
||||
targetAPIVersion: *"apps/v1" | string
|
||||
// +usage=Specify the kind of scale target
|
||||
targetKind: *"Deployment" | string
|
||||
cpu: {
|
||||
// +usage=Specify resource metrics in terms of percentage("Utilization") or direct value("AverageValue")
|
||||
type: *"Utilization" | "AverageValue"
|
||||
// +usage=Specify the value of CPU utilization or averageValue
|
||||
value: *50 | int
|
||||
}
|
||||
mem?: {
|
||||
// +usage=Specify resource metrics in terms of percentage("Utilization") or direct value("AverageValue")
|
||||
type: *"Utilization" | "AverageValue"
|
||||
// +usage=Specify the value of MEM utilization or averageValue
|
||||
value: *50 | int
|
||||
}
|
||||
// +usage=Specify custom metrics of pod type
|
||||
podCustomMetrics?: [...{
|
||||
// +usage=Specify name of custom metrics
|
||||
name: string
|
||||
// +usage=Specify target value of custom metrics
|
||||
value: string
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: print message in workflow step status
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: print-message-in-status
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Read Kubernetes objects from cluster for your workflow steps
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: read-object
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: A special step that you can declare 'subSteps' in it, 'subSteps' is an array containing any step type whose valid parameters do not include the `step-group` step type itself. The sub steps were executed in parallel.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: step-group
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -54,6 +54,12 @@ spec:
|
||||
emptyDir: medium: v.medium
|
||||
}
|
||||
},
|
||||
if parameter.hostPath != _|_ for v in parameter.hostPath {
|
||||
{
|
||||
name: "hostpath-" + v.name
|
||||
path: v.path
|
||||
}
|
||||
},
|
||||
]
|
||||
volumeMountsList: [
|
||||
if parameter.pvc != _|_ for v in parameter.pvc {
|
||||
@@ -94,6 +100,12 @@ spec:
|
||||
}
|
||||
}
|
||||
},
|
||||
if parameter.hostPath != _|_ for v in parameter.hostPath {
|
||||
{
|
||||
name: "hostpath-" + v.name
|
||||
mountPath: v.mountPath
|
||||
}
|
||||
},
|
||||
]
|
||||
envList: [
|
||||
if parameter.configMap != _|_ for v in parameter.configMap if v.mountToEnv != _|_ {
|
||||
@@ -331,5 +343,13 @@ spec:
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
|
||||
// +usage=Declare host path type storage
|
||||
hostPath?: [...{
|
||||
name: string
|
||||
path: string
|
||||
mountPath: string
|
||||
type: *"Directory" | "DirectoryOrCreate" | "FileOrCreate" | "File" | "Socket" | "CharDevice" | "BlockDevice"
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@@ -19,18 +19,24 @@ spec:
|
||||
mountsArray: [
|
||||
if parameter.storage != _|_ && parameter.storage.secret != _|_ for v in parameter.storage.secret {
|
||||
{
|
||||
name: "secret-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
if parameter.storage != _|_ && parameter.storage.hostPath != _|_ for v in parameter.storage.hostPath {
|
||||
{
|
||||
name: "hostpath-" + v.name
|
||||
mountPath: v.mountPath
|
||||
}
|
||||
},
|
||||
]
|
||||
volumesList: [
|
||||
if parameter.storage != _|_ && parameter.storage.secret != _|_ for v in parameter.storage.secret {
|
||||
{
|
||||
name: v.name
|
||||
name: "secret-" + v.name
|
||||
secret: {
|
||||
defaultMode: v.defaultMode
|
||||
secretName: v.secretName
|
||||
@@ -39,6 +45,12 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
if parameter.storage != _|_ && parameter.storage.hostPath != _|_ for v in parameter.storage.hostPath {
|
||||
{
|
||||
name: "hostpath-" + v.name
|
||||
path: v.path
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
deDupVolumesArray: [
|
||||
@@ -69,7 +81,7 @@ spec:
|
||||
spec: {
|
||||
backoffLimit: 3
|
||||
template: {
|
||||
labels: "workflow.oam.dev/step-name": "\(context.name)-\(context.stepName)"
|
||||
metadata: labels: "workflow.oam.dev/step-name": "\(context.name)-\(context.stepName)"
|
||||
spec: {
|
||||
containers: [
|
||||
{
|
||||
@@ -125,6 +137,13 @@ spec:
|
||||
mode: *511 | int
|
||||
}]
|
||||
}]
|
||||
// +usage=Declare host path type storage
|
||||
hostPath?: [...{
|
||||
name: string
|
||||
path: string
|
||||
mountPath: string
|
||||
type: *"Directory" | "DirectoryOrCreate" | "FileOrCreate" | "File" | "Socket" | "CharDevice" | "BlockDevice"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,21 +11,6 @@ metadata:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:manager-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ if .Values.authentication.enabled }} {{ include "kubevela.fullname" . }}:manager {{ else }} "cluster-admin" {{ end }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
{{ if .Values.authentication.enabled }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
@@ -66,9 +51,41 @@ rules:
|
||||
- apiGroups: ["authorization.k8s.io"]
|
||||
resources: ["subjectaccessreviews"]
|
||||
verbs: ["*"]
|
||||
{{ end }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:manager-authentication-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "kubevela.fullname" . }}:manager
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
{{ else }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubevela.fullname" . }}:manager-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: "cluster-admin"
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubevela.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
{{ end }}
|
||||
---
|
||||
# permissions to do leader election.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
@@ -257,6 +274,7 @@ spec:
|
||||
- "--feature-gates=MultiStageComponentApply= {{- .Values.featureGates.multiStageComponentApply | toString -}}"
|
||||
- "--feature-gates=GzipApplicationRevision={{- .Values.featureGates.gzipResourceTracker | toString -}}"
|
||||
- "--feature-gates=ZstdApplicationRevision={{- .Values.featureGates.zstdResourceTracker | toString -}}"
|
||||
- "--feature-gates=PreDispatchDryRun={{- .Values.featureGates.preDispatchDryRun | toString -}}"
|
||||
{{ if .Values.authentication.enabled }}
|
||||
{{ if .Values.authentication.withUser }}
|
||||
- "--authentication-with-user"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{{- if not (lookup "v1" "ConfigMap" (include "systemDefinitionNamespace" .) "component-pod-view") }}
|
||||
apiVersion: v1
|
||||
data:
|
||||
template: |
|
||||
@@ -74,4 +73,3 @@ kind: ConfigMap
|
||||
metadata:
|
||||
name: component-pod-view
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{{- if not (lookup "v1" "ConfigMap" (include "systemDefinitionNamespace" .) "component-service-view") }}
|
||||
apiVersion: v1
|
||||
data:
|
||||
template: |
|
||||
@@ -47,4 +46,3 @@ kind: ConfigMap
|
||||
metadata:
|
||||
name: component-service-view
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
{{- end}}
|
||||
@@ -113,6 +113,7 @@ optimize:
|
||||
##@param featureGates.multiStageComponentApply if enabled, the multiStageComponentApply feature will be combined with the stage field in TraitDefinition to complete the multi-stage apply.
|
||||
##@param featureGates.gzipApplicationRevision compress apprev using gzip (good) before being stored. This is reduces network throughput when dealing with huge apprevs.
|
||||
##@param featureGates.zstdApplicationRevision compress apprev using zstd (fast and good) before being stored. This is reduces network throughput when dealing with huge apprevs. Note that zstd will be prioritized if you enable other compression options.
|
||||
##@param featureGates.preDispatchDryRun enable dryrun before dispatching resources. Enable this flag can help prevent unsuccessful dispatch resources entering resourcetracker and improve the user experiences of gc but at the cost of increasing network requests.
|
||||
##@param
|
||||
featureGates:
|
||||
enableLegacyComponentRevision: false
|
||||
@@ -122,6 +123,7 @@ featureGates:
|
||||
multiStageComponentApply: false
|
||||
gzipApplicationRevision: false
|
||||
zstdApplicationRevision: true
|
||||
preDispatchDryRun: true
|
||||
|
||||
## @section MultiCluster parameters
|
||||
|
||||
@@ -146,7 +148,7 @@ multicluster:
|
||||
port: 9443
|
||||
image:
|
||||
repository: oamdev/cluster-gateway
|
||||
tag: v1.4.0
|
||||
tag: v1.7.0-alpha.3
|
||||
pullPolicy: IfNotPresent
|
||||
resources:
|
||||
limits:
|
||||
@@ -263,6 +265,6 @@ kubeClient:
|
||||
## @param authentication.groupPattern Application authentication will impersonate as the request Group that matches the pattern
|
||||
authentication:
|
||||
enabled: false
|
||||
withUser: false
|
||||
withUser: true
|
||||
defaultUser: kubevela:vela-core
|
||||
groupPattern: kubevela:*
|
||||
|
||||
@@ -848,6 +848,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -2806,6 +2807,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -4942,6 +4944,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -5042,6 +5045,11 @@ spec:
|
||||
- suspend
|
||||
- terminated
|
||||
type: object
|
||||
workflowContext:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Record the context values to the revision.
|
||||
type: object
|
||||
required:
|
||||
- succeeded
|
||||
type: object
|
||||
|
||||
@@ -774,6 +774,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -1535,6 +1536,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
@@ -7,7 +7,6 @@ metadata:
|
||||
definition.oam.dev/description: Apply a specific component and its corresponding traits in application
|
||||
labels:
|
||||
custom.definition.oam.dev/scope: Application
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: apply-component
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Wait for the specified Application to complete.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: depends-on-app
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Export data to specified Kubernetes ConfigMap in your workflow.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: export2config
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Export data to Kubernetes Secret in your workflow.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: export2secret
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Generate a JDBC connection based on Component of alibaba-rds
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: generate-jdbc-connection
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
110
charts/vela-minimal/templates/defwithtemplate/hpa.yaml
Normal file
110
charts/vela-minimal/templates/defwithtemplate/hpa.yaml
Normal file
@@ -0,0 +1,110 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/hpa.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Configure k8s HPA for Deployment or Statefulsets
|
||||
name: hpa
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
- statefulsets.apps
|
||||
podDisruptive: false
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: hpa: {
|
||||
if context.clusterVersion.minor < 23 {
|
||||
apiVersion: "autoscaling/v2beta2"
|
||||
}
|
||||
if context.clusterVersion.minor >= 23 {
|
||||
apiVersion: "autoscaling/v2"
|
||||
}
|
||||
kind: "HorizontalPodAutoscaler"
|
||||
metadata: name: context.name
|
||||
spec: {
|
||||
scaleTargetRef: {
|
||||
apiVersion: parameter.targetAPIVersion
|
||||
kind: parameter.targetKind
|
||||
name: context.name
|
||||
}
|
||||
minReplicas: parameter.min
|
||||
maxReplicas: parameter.max
|
||||
metrics: [
|
||||
{
|
||||
type: "Resource"
|
||||
resource: {
|
||||
name: "cpu"
|
||||
target: {
|
||||
type: parameter.cpu.type
|
||||
if parameter.cpu.type == "Utilization" {
|
||||
averageUtilization: parameter.cpu.value
|
||||
}
|
||||
if parameter.cpu.type == "AverageValue" {
|
||||
averageValue: parameter.cpu.value
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
if parameter.mem != _|_ {
|
||||
{
|
||||
type: "Resource"
|
||||
resource: {
|
||||
name: "memory"
|
||||
target: {
|
||||
type: parameter.mem.type
|
||||
if parameter.cpu.type == "Utilization" {
|
||||
averageUtilization: parameter.cpu.value
|
||||
}
|
||||
if parameter.cpu.type == "AverageValue" {
|
||||
averageValue: parameter.cpu.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
if parameter.podCustomMetrics != _|_ for m in parameter.podCustomMetrics {
|
||||
type: "Pods"
|
||||
pods: {
|
||||
metric: name: m.name
|
||||
target: {
|
||||
type: "AverageValue"
|
||||
averageValue: m.value
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the minimal number of replicas to which the autoscaler can scale down
|
||||
min: *1 | int
|
||||
// +usage=Specify the maximum number of of replicas to which the autoscaler can scale up
|
||||
max: *10 | int
|
||||
// +usage=Specify the apiVersion of scale target
|
||||
targetAPIVersion: *"apps/v1" | string
|
||||
// +usage=Specify the kind of scale target
|
||||
targetKind: *"Deployment" | string
|
||||
cpu: {
|
||||
// +usage=Specify resource metrics in terms of percentage("Utilization") or direct value("AverageValue")
|
||||
type: *"Utilization" | "AverageValue"
|
||||
// +usage=Specify the value of CPU utilization or averageValue
|
||||
value: *50 | int
|
||||
}
|
||||
mem?: {
|
||||
// +usage=Specify resource metrics in terms of percentage("Utilization") or direct value("AverageValue")
|
||||
type: *"Utilization" | "AverageValue"
|
||||
// +usage=Specify the value of MEM utilization or averageValue
|
||||
value: *50 | int
|
||||
}
|
||||
// +usage=Specify custom metrics of pod type
|
||||
podCustomMetrics?: [...{
|
||||
// +usage=Specify name of custom metrics
|
||||
name: string
|
||||
// +usage=Specify target value of custom metrics
|
||||
value: string
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: print message in workflow step status
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: print-message-in-status
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Read Kubernetes objects from cluster for your workflow steps
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: read-object
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: A special step that you can declare 'subSteps' in it, 'subSteps' is an array containing any step type whose valid parameters do not include the `step-group` step type itself. The sub steps were executed in parallel.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: step-group
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -54,6 +54,12 @@ spec:
|
||||
emptyDir: medium: v.medium
|
||||
}
|
||||
},
|
||||
if parameter.hostPath != _|_ for v in parameter.hostPath {
|
||||
{
|
||||
name: "hostpath-" + v.name
|
||||
path: v.path
|
||||
}
|
||||
},
|
||||
]
|
||||
volumeMountsList: [
|
||||
if parameter.pvc != _|_ for v in parameter.pvc {
|
||||
@@ -94,6 +100,12 @@ spec:
|
||||
}
|
||||
}
|
||||
},
|
||||
if parameter.hostPath != _|_ for v in parameter.hostPath {
|
||||
{
|
||||
name: "hostpath-" + v.name
|
||||
mountPath: v.mountPath
|
||||
}
|
||||
},
|
||||
]
|
||||
envList: [
|
||||
if parameter.configMap != _|_ for v in parameter.configMap if v.mountToEnv != _|_ {
|
||||
@@ -331,5 +343,13 @@ spec:
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
|
||||
// +usage=Declare host path type storage
|
||||
hostPath?: [...{
|
||||
name: string
|
||||
path: string
|
||||
mountPath: string
|
||||
type: *"Directory" | "DirectoryOrCreate" | "FileOrCreate" | "File" | "Socket" | "CharDevice" | "BlockDevice"
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@@ -19,18 +19,24 @@ spec:
|
||||
mountsArray: [
|
||||
if parameter.storage != _|_ && parameter.storage.secret != _|_ for v in parameter.storage.secret {
|
||||
{
|
||||
name: "secret-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
if parameter.storage != _|_ && parameter.storage.hostPath != _|_ for v in parameter.storage.hostPath {
|
||||
{
|
||||
name: "hostpath-" + v.name
|
||||
mountPath: v.mountPath
|
||||
}
|
||||
},
|
||||
]
|
||||
volumesList: [
|
||||
if parameter.storage != _|_ && parameter.storage.secret != _|_ for v in parameter.storage.secret {
|
||||
{
|
||||
name: v.name
|
||||
name: "secret-" + v.name
|
||||
secret: {
|
||||
defaultMode: v.defaultMode
|
||||
secretName: v.secretName
|
||||
@@ -39,6 +45,12 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
if parameter.storage != _|_ && parameter.storage.hostPath != _|_ for v in parameter.storage.hostPath {
|
||||
{
|
||||
name: "hostpath-" + v.name
|
||||
path: v.path
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
deDupVolumesArray: [
|
||||
@@ -69,7 +81,7 @@ spec:
|
||||
spec: {
|
||||
backoffLimit: 3
|
||||
template: {
|
||||
labels: "workflow.oam.dev/step-name": "\(context.name)-\(context.stepName)"
|
||||
metadata: labels: "workflow.oam.dev/step-name": "\(context.name)-\(context.stepName)"
|
||||
spec: {
|
||||
containers: [
|
||||
{
|
||||
@@ -125,6 +137,13 @@ spec:
|
||||
mode: *511 | int
|
||||
}]
|
||||
}]
|
||||
// +usage=Declare host path type storage
|
||||
hostPath?: [...{
|
||||
name: string
|
||||
path: string
|
||||
mountPath: string
|
||||
type: *"Directory" | "DirectoryOrCreate" | "FileOrCreate" | "File" | "Socket" | "CharDevice" | "BlockDevice"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,13 @@ limitations under the License.
|
||||
package options
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
"flag"
|
||||
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/config"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
)
|
||||
|
||||
// ServerRunOptions contains everything necessary to create and run api server
|
||||
@@ -40,6 +43,9 @@ func NewServerRunOptions() *ServerRunOptions {
|
||||
func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
||||
fs := fss.FlagSet("generic")
|
||||
s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions)
|
||||
feature.DefaultMutableFeatureGate.AddFlag(fss.FlagSet("featuregate"))
|
||||
features.APIServerMutableFeatureGate.AddFlag(fss.FlagSet("featuregate"))
|
||||
local := flag.NewFlagSet("klog", flag.ExitOnError)
|
||||
klog.InitFlags(local)
|
||||
fs.AddGoFlagSet(local)
|
||||
return fss
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -199,7 +199,7 @@ var ApplicationDeleteWithWaitOptions = func(context string, appName string) bool
|
||||
cli := fmt.Sprintf("vela delete %s --wait -y", appName)
|
||||
output, err := e2e.ExecAndTerminate(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("deleted"))
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("succeeded"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ var (
|
||||
cli := fmt.Sprintf("vela delete %s -y", applicationName)
|
||||
output, err := Exec(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("deleted from namespace"))
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("succeeded"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
5
go.mod
5
go.mod
@@ -49,16 +49,15 @@ require (
|
||||
github.com/google/go-github/v32 v32.1.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/gosuri/uilive v0.0.4
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
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-20221220022408-126a9c58aa3a
|
||||
github.com/kubevela/pkg v0.0.0-20230105054759-263dc191bf51
|
||||
github.com/kubevela/prism v1.7.0-alpha.1
|
||||
github.com/kubevela/workflow v0.3.6-0.20221228071359-3da7f1a4df6b
|
||||
github.com/kubevela/workflow v0.3.6-0.20221230102636-6ae0c5cbc40f
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.1
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
|
||||
|
||||
10
go.sum
10
go.sum
@@ -1060,8 +1060,6 @@ github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/g
|
||||
github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A=
|
||||
github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
|
||||
github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU=
|
||||
github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY=
|
||||
github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI=
|
||||
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
|
||||
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
|
||||
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
@@ -1287,12 +1285,12 @@ 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-20221220022408-126a9c58aa3a h1:kWBTjpxcA6ZQTGyk1Mm+1/MT2+3Fv3ffQnK/1GYdXo4=
|
||||
github.com/kubevela/pkg v0.0.0-20221220022408-126a9c58aa3a/go.mod h1:IQ0/t6H7+580nwFlkt08gbPyH9S4zQNxnKTM2eiK0TI=
|
||||
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/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.3.6-0.20221228071359-3da7f1a4df6b h1:zbBG/fTXIyhwyS3XfoYxEWJ3F1xGGQN7bCZQhlI0KYI=
|
||||
github.com/kubevela/workflow v0.3.6-0.20221228071359-3da7f1a4df6b/go.mod h1:AX/WL3G/YBkpmNpA/SKKm9M3Y0T9y95gZA8mFWylkyM=
|
||||
github.com/kubevela/workflow v0.3.6-0.20221230102636-6ae0c5cbc40f h1:7EZWIfcTOgMlLgHkdDlf++hSjBTulfr4DYhZjeQbiJI=
|
||||
github.com/kubevela/workflow v0.3.6-0.20221230102636-6ae0c5cbc40f/go.mod h1:AX/WL3G/YBkpmNpA/SKKm9M3Y0T9y95gZA8mFWylkyM=
|
||||
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=
|
||||
|
||||
@@ -848,6 +848,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -2806,6 +2807,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -4942,6 +4944,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -5042,6 +5045,11 @@ spec:
|
||||
- suspend
|
||||
- terminated
|
||||
type: object
|
||||
workflowContext:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Record the context values to the revision.
|
||||
type: object
|
||||
required:
|
||||
- succeeded
|
||||
type: object
|
||||
|
||||
@@ -774,6 +774,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -1535,6 +1536,7 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
@@ -60,6 +60,7 @@ import (
|
||||
common2 "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/apis/types"
|
||||
apiutils "github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/config"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/script"
|
||||
"github.com/oam-dev/kubevela/pkg/definition"
|
||||
@@ -910,7 +911,6 @@ func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *
|
||||
func (h *Installer) enableAddon(addon *InstallPackage) (string, error) {
|
||||
var err error
|
||||
h.addon = addon
|
||||
|
||||
if !h.skipVersionValidate {
|
||||
err = checkAddonVersionMeetRequired(h.ctx, addon.SystemRequirements, h.cli, h.dc)
|
||||
if err != nil {
|
||||
@@ -1075,23 +1075,25 @@ func (h *Installer) checkDependency(addon *InstallPackage) ([]string, error) {
|
||||
|
||||
// createOrUpdate will return true if updated
|
||||
func (h *Installer) createOrUpdate(app *v1beta1.Application) (bool, error) {
|
||||
var getapp v1beta1.Application
|
||||
err := h.cli.Get(h.ctx, client.ObjectKey{Name: app.Name, Namespace: app.Namespace}, &getapp)
|
||||
// Set the publish version for the addon application
|
||||
oam.SetPublishVersion(app, apiutils.GenerateVersion("addon"))
|
||||
var existApp v1beta1.Application
|
||||
err := h.cli.Get(h.ctx, client.ObjectKey{Name: app.Name, Namespace: app.Namespace}, &existApp)
|
||||
if apierrors.IsNotFound(err) {
|
||||
return false, h.cli.Create(h.ctx, app)
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
getapp.Spec = app.Spec
|
||||
getapp.Labels = app.Labels
|
||||
getapp.Annotations = app.Annotations
|
||||
err = h.cli.Update(h.ctx, &getapp)
|
||||
existApp.Spec = app.Spec
|
||||
existApp.Labels = app.Labels
|
||||
existApp.Annotations = app.Annotations
|
||||
err = h.cli.Update(h.ctx, &existApp)
|
||||
if err != nil {
|
||||
klog.Errorf("fail to create application: %v", err)
|
||||
return false, errors.Wrap(err, "fail to create application")
|
||||
}
|
||||
getapp.DeepCopyInto(app)
|
||||
existApp.DeepCopyInto(app)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -1208,6 +1210,9 @@ func (h *Installer) dispatchAddonResource(addon *InstallPackage) error {
|
||||
}
|
||||
|
||||
func (h *Installer) renderNotes(addon *InstallPackage) (string, error) {
|
||||
if len(addon.Notes.Data) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
r := addonCueTemplateRender{
|
||||
addon: addon,
|
||||
inputArgs: h.args,
|
||||
@@ -1219,7 +1224,7 @@ func (h *Installer) renderNotes(addon *InstallPackage) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
notesFile := addon.Notes.Data + "\n" + contextFile
|
||||
notesFile := contextFile + "\n" + addon.Notes.Data
|
||||
val, err := value.NewValue(notesFile, nil, "")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "build values for NOTES.cue")
|
||||
|
||||
@@ -67,13 +67,18 @@ func NewCache(ds RegistryDataStore) *Cache {
|
||||
}
|
||||
|
||||
// DiscoverAndRefreshLoop will run a loop to automatically discovery and refresh addons from registry
|
||||
func (u *Cache) DiscoverAndRefreshLoop(cacheTime time.Duration) {
|
||||
func (u *Cache) DiscoverAndRefreshLoop(ctx context.Context, cacheTime time.Duration) {
|
||||
ticker := time.NewTicker(cacheTime)
|
||||
defer ticker.Stop()
|
||||
|
||||
// This is infinite loop, we can receive a channel for close
|
||||
for ; true; <-ticker.C {
|
||||
u.discoverAndRefreshRegistry()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
u.discoverAndRefreshRegistry()
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@ func EnableAddon(ctx context.Context, name string, version string, cli client.Cl
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := validateAddonPackage(pkg); err != nil {
|
||||
return "", errors.Wrap(err, fmt.Sprintf("failed to enable addon: %s", name))
|
||||
}
|
||||
return h.enableAddon(pkg)
|
||||
}
|
||||
|
||||
@@ -106,6 +109,9 @@ func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli cli
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := validateAddonPackage(pkg); err != nil {
|
||||
return "", errors.Wrap(err, fmt.Sprintf("failed to enable addon by local dir: %s", dir))
|
||||
}
|
||||
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil, nil, opts...)
|
||||
needEnableAddonNames, err := h.checkDependency(pkg)
|
||||
if err != nil {
|
||||
|
||||
@@ -17,9 +17,12 @@ limitations under the License.
|
||||
package addon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -29,6 +32,38 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func setupMockServer() *httptest.Server {
|
||||
var listenURL string
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
fileList := []string{
|
||||
"index.yaml",
|
||||
"fluxcd-test-version-1.0.0.tgz",
|
||||
"fluxcd-test-version-2.0.0.tgz",
|
||||
"vela-workflow-v0.3.5.tgz",
|
||||
"foo-v1.0.0.tgz",
|
||||
"bar-v1.0.0.tgz",
|
||||
"bar-v2.0.0.tgz",
|
||||
"mock-be-dep-addon-v1.0.0.tgz",
|
||||
}
|
||||
for _, f := range fileList {
|
||||
if strings.Contains(req.URL.Path, f) {
|
||||
file, err := os.ReadFile("../../e2e/addon/mock/testrepo/helm-repo/" + f)
|
||||
if err != nil {
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
}
|
||||
if f == "index.yaml" {
|
||||
// in index.yaml, url is hardcoded to 127.0.0.1:9098,
|
||||
// so we need to replace it with the real random listen url
|
||||
file = bytes.ReplaceAll(file, []byte("http://127.0.0.1:9098"), []byte(listenURL))
|
||||
}
|
||||
_, _ = w.Write(file)
|
||||
}
|
||||
}
|
||||
}))
|
||||
listenURL = s.URL
|
||||
return s
|
||||
}
|
||||
|
||||
var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
|
||||
Describe("when no registry is added, no matter what you do, it will just return error", func() {
|
||||
Context("when empty addonNames and registryNames is supplied", func() {
|
||||
@@ -50,12 +85,15 @@ var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
|
||||
})
|
||||
|
||||
Describe("one versioned registry is added", func() {
|
||||
var s *httptest.Server
|
||||
|
||||
BeforeEach(func() {
|
||||
// Prepare KubeVela registry
|
||||
s = setupMockServer()
|
||||
// Prepare registry
|
||||
reg := &Registry{
|
||||
Name: "KubeVela",
|
||||
Name: "addon_helper_test",
|
||||
Helm: &HelmSource{
|
||||
URL: "https://addons.kubevela.net",
|
||||
URL: s.URL,
|
||||
},
|
||||
}
|
||||
ds := NewRegistryDataStore(k8sClient)
|
||||
@@ -63,38 +101,36 @@ var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// Clean up KubeVela registry
|
||||
// Clean up registry
|
||||
ds := NewRegistryDataStore(k8sClient)
|
||||
Expect(ds.DeleteRegistry(context.Background(), "KubeVela")).To(Succeed())
|
||||
Expect(ds.DeleteRegistry(context.Background(), "addon_helper_test")).To(Succeed())
|
||||
s.Close()
|
||||
})
|
||||
|
||||
Context("when empty addonNames and registryNames is supplied", func() {
|
||||
It("should return error, empty addonNames are not allowed", func() {
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{"KubeVela"})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{"addon_helper_test"})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("should return error, empty addonNames are not allowed", func() {
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, []string{"KubeVela"})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, []string{"addon_helper_test"})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("one existing addon name provided", func() {
|
||||
It("should return one valid result, matching all registries", func() {
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"foo"}, nil)
|
||||
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
Expect(res[0].Name).To(Equal("foo"))
|
||||
})
|
||||
It("should return one valid result, matching one registry", func() {
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux"}, []string{"KubeVela"})
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"foo"}, []string{"addon_helper_test"})
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
Expect(res[0].Name).To(Equal("foo"))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -108,26 +144,20 @@ var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
|
||||
|
||||
Context("two existing addon names provided", func() {
|
||||
It("should return two valid result", func() {
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux", "traefik"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"foo", "bar"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(2))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
Expect(res[1].Name).To(Equal("traefik"))
|
||||
Expect(res[1].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[1].APISchema).ToNot(BeNil())
|
||||
Expect(res[0].Name).To(Equal("foo"))
|
||||
Expect(res[1].Name).To(Equal("bar"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("one existing addon name and one non-existent addon name provided", func() {
|
||||
It("should return only one valid result", func() {
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux", "non-existent-addon"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"foo", "non-existent-addon"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
Expect(res[0].Name).To(Equal("foo"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -196,9 +196,13 @@ func (cmd *InitCmd) createHelmComponent() error {
|
||||
tmpl := helmComponentTmpl{}
|
||||
tmpl.Type = "helm"
|
||||
tmpl.Properties.RepoType = "helm"
|
||||
if strings.HasPrefix(cmd.HelmRepoURL, "oci") {
|
||||
tmpl.Properties.RepoType = "oci"
|
||||
}
|
||||
tmpl.Properties.URL = cmd.HelmRepoURL
|
||||
tmpl.Properties.Chart = cmd.HelmChartName
|
||||
tmpl.Properties.Version = cmd.HelmChartVersion
|
||||
tmpl.Name = "addon-" + cmd.AddonName
|
||||
|
||||
str, err := toCUEResourceString(tmpl)
|
||||
if err != nil {
|
||||
@@ -383,6 +387,7 @@ func (cmd *InitCmd) writeFiles() error {
|
||||
|
||||
// helmComponentTmpl is a template for a helm component .cue in an addon
|
||||
type helmComponentTmpl struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Properties struct {
|
||||
RepoType string `json:"repoType"`
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -503,6 +504,19 @@ func checkBondComponentExist(u unstructured.Unstructured, app v1beta1.Applicatio
|
||||
return false
|
||||
}
|
||||
|
||||
func validateAddonPackage(addonPkg *InstallPackage) error {
|
||||
if reflect.DeepEqual(addonPkg.Meta, Meta{}) {
|
||||
return fmt.Errorf("the addon package doesn't have `metadata.yaml`")
|
||||
}
|
||||
if addonPkg.Name == "" {
|
||||
return fmt.Errorf("`matadata.yaml` must define the name of addon")
|
||||
}
|
||||
if addonPkg.Version == "" {
|
||||
return fmt.Errorf("`matadata.yaml` must define the version of addon")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterDependencyRegistries will return all registries besides the target registry itself
|
||||
func FilterDependencyRegistries(i int, rs []Registry) []Registry {
|
||||
if i >= len(rs) {
|
||||
|
||||
@@ -17,9 +17,11 @@ limitations under the License.
|
||||
package addon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -402,6 +404,30 @@ func TestFilterDependencyRegistries(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckAddonPackageValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testCase Meta
|
||||
err error
|
||||
}{{
|
||||
testCase: Meta{},
|
||||
err: fmt.Errorf("the addon package doesn't have `metadata.yaml`"),
|
||||
}, {
|
||||
testCase: Meta{Version: "v1.4.0"},
|
||||
err: fmt.Errorf("`matadata.yaml` must define the name of addon"),
|
||||
}, {
|
||||
testCase: Meta{Name: "test-addon"},
|
||||
err: fmt.Errorf("`matadata.yaml` must define the version of addon"),
|
||||
}, {
|
||||
testCase: Meta{Name: "test-addon", Version: "1.4.5"},
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
err := validateAddonPackage(&InstallPackage{Meta: testCase.testCase})
|
||||
assert.Equal(t, reflect.DeepEqual(err, testCase.err), true)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
compDefYaml = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
|
||||
@@ -149,7 +149,7 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
|
||||
if addonVersion == nil {
|
||||
return nil, errors.Errorf("specified version %s not exist", utils.Sanitize(version))
|
||||
}
|
||||
klog.Infof("Addon '%s' with version '%s' found from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
|
||||
klog.V(5).Infof("Addon '%s' with version '%s' found from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
|
||||
for _, chartURL := range addonVersion.URLs {
|
||||
if !utils.IsValidURL(chartURL) {
|
||||
chartURL, err = utils.JoinURL(i.url, chartURL)
|
||||
@@ -159,10 +159,12 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
|
||||
}
|
||||
archive, err := common.HTTPGetWithOption(ctx, chartURL, i.Opts)
|
||||
if err != nil {
|
||||
klog.Warningf("failed to download the addon package %s:%s", chartURL, err.Error())
|
||||
continue
|
||||
}
|
||||
bufferedFile, err := loader.LoadArchiveFiles(bytes.NewReader(archive))
|
||||
if err != nil {
|
||||
klog.Warningf("failed to load the addon package:%s", err.Error())
|
||||
continue
|
||||
}
|
||||
addonPkg, err := loadAddonPackage(name, bufferedFile)
|
||||
@@ -172,6 +174,7 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
|
||||
addonPkg.AvailableVersions = availableVersions
|
||||
addonPkg.RegistryName = i.name
|
||||
addonPkg.Meta.SystemRequirements = LoadSystemRequirements(addonVersion.Annotations)
|
||||
klog.V(5).Infof("Addon '%s' with version '%s' loaded successfully from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
|
||||
return addonPkg, nil
|
||||
}
|
||||
return nil, ErrFetch
|
||||
|
||||
@@ -59,8 +59,8 @@ func (a *Application) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (a *Application) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (a *Application) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if a.Name != "" {
|
||||
index["name"] = a.Name
|
||||
}
|
||||
@@ -154,8 +154,8 @@ func (a *ApplicationComponent) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (a *ApplicationComponent) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (a *ApplicationComponent) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if a.Name != "" {
|
||||
index["name"] = a.Name
|
||||
}
|
||||
@@ -202,8 +202,8 @@ func (a *ApplicationPolicy) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (a *ApplicationPolicy) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (a *ApplicationPolicy) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if a.Name != "" {
|
||||
index["name"] = a.Name
|
||||
}
|
||||
@@ -348,8 +348,8 @@ func (a *ApplicationRevision) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (a *ApplicationRevision) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (a *ApplicationRevision) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if a.Version != "" {
|
||||
index["version"] = a.Version
|
||||
}
|
||||
@@ -434,8 +434,8 @@ func (w *ApplicationTrigger) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (w *ApplicationTrigger) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (w *ApplicationTrigger) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if w.AppPrimaryKey != "" {
|
||||
index["appPrimaryKey"] = w.AppPrimaryKey
|
||||
}
|
||||
|
||||
@@ -93,8 +93,8 @@ func (c *Cluster) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index set to nil for list
|
||||
func (c *Cluster) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (c *Cluster) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if c.Name != "" {
|
||||
index["name"] = c.Name
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ func (p *Env) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p *Env) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (p *Env) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ func (e *EnvBinding) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (e *EnvBinding) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (e *EnvBinding) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if e.Name != "" {
|
||||
index["name"] = e.Name
|
||||
}
|
||||
|
||||
@@ -106,6 +106,11 @@ func (j *JSONStruct) JSON() string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Properties return the map
|
||||
func (j *JSONStruct) Properties() map[string]interface{} {
|
||||
return *j
|
||||
}
|
||||
|
||||
// RawExtension Encoded as a RawExtension
|
||||
func (j *JSONStruct) RawExtension() *runtime.RawExtension {
|
||||
yamlByte, err := yaml.Marshal(j)
|
||||
|
||||
@@ -61,8 +61,8 @@ func (p Pipeline) ShortTableName() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p Pipeline) Index() map[string]string {
|
||||
var index = make(map[string]string)
|
||||
func (p Pipeline) Index() map[string]interface{} {
|
||||
var index = make(map[string]interface{})
|
||||
if p.Project != "" {
|
||||
index["project"] = p.Project
|
||||
}
|
||||
@@ -102,8 +102,8 @@ func (c *PipelineContext) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (c *PipelineContext) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (c *PipelineContext) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if c.ProjectName != "" {
|
||||
index["project_name"] = c.ProjectName
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ func (p *Project) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p *Project) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (p *Project) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
|
||||
@@ -146,8 +146,8 @@ func (u *SystemInfo) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (u *SystemInfo) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (u *SystemInfo) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if u.InstallID != "" {
|
||||
index["installID"] = u.InstallID
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ func (d *Target) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (d *Target) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (d *Target) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if d.Name != "" {
|
||||
index["name"] = d.Name
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@ func (u *User) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (u *User) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (u *User) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if u.Name != "" {
|
||||
index["name"] = u.Name
|
||||
}
|
||||
@@ -106,8 +106,8 @@ func (u *ProjectUser) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (u *ProjectUser) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (u *ProjectUser) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if u.Username != "" {
|
||||
index["username"] = u.Username
|
||||
}
|
||||
@@ -177,8 +177,8 @@ func (r *Role) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (r *Role) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (r *Role) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if r.Name != "" {
|
||||
index["name"] = r.Name
|
||||
}
|
||||
@@ -207,8 +207,8 @@ func (p *Permission) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p *Permission) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (p *Permission) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
@@ -250,8 +250,8 @@ func (p *PermissionTemplate) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p *PermissionTemplate) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (p *PermissionTemplate) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ const (
|
||||
|
||||
// LabelSyncGeneration describes the generation synced from
|
||||
LabelSyncGeneration = "ux.oam.dev/synced-generation"
|
||||
// LabelSyncRevision describes the revision name synced from
|
||||
LabelSyncRevision = "ux.oam.dev/synced-revision"
|
||||
// LabelSyncNamespace describes the namespace synced from
|
||||
LabelSyncNamespace = "ux.oam.dev/from-namespace"
|
||||
)
|
||||
@@ -56,26 +58,16 @@ const (
|
||||
FromInner = "from-inner-system"
|
||||
)
|
||||
|
||||
// DataStoreApp is a memory struct that describes the model of an application in datastore
|
||||
type DataStoreApp struct {
|
||||
AppMeta *Application
|
||||
Env *Env
|
||||
Eb *EnvBinding
|
||||
Comps []*ApplicationComponent
|
||||
Policies []*ApplicationPolicy
|
||||
Workflow *Workflow
|
||||
Targets []*Target
|
||||
Record *WorkflowRecord
|
||||
Revision *ApplicationRevision
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
// DefaultInitName is default object name for initialization
|
||||
DefaultInitName = "default"
|
||||
|
||||
// DefaultAddonProject is default addon projects
|
||||
DefaultAddonProject = "addons"
|
||||
// DefaultSystemProject is project name for the system
|
||||
DefaultSystemProject = "system"
|
||||
|
||||
// DefaultSystemProjectAlias is project alias for the system
|
||||
DefaultSystemProjectAlias = "System"
|
||||
|
||||
// DefaultInitNamespace is default namespace name for initialization
|
||||
DefaultInitNamespace = "default"
|
||||
|
||||
@@ -18,7 +18,6 @@ package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
|
||||
@@ -42,15 +41,16 @@ type Workflow struct {
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
// Workflow used by the default
|
||||
Default *bool `json:"default"`
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
EnvName string `json:"envName"`
|
||||
Steps []WorkflowStep `json:"steps,omitempty"`
|
||||
Default *bool `json:"default"`
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
EnvName string `json:"envName"`
|
||||
Mode workflowv1alpha1.WorkflowExecuteMode `json:"mode,omitempty"`
|
||||
Steps []WorkflowStep `json:"steps,omitempty"`
|
||||
}
|
||||
|
||||
// WorkflowStep defines how to execute a workflow step.
|
||||
type WorkflowStep struct {
|
||||
WorkflowStepBase `json:",inline"`
|
||||
WorkflowStepBase `json:",inline" bson:",inline"`
|
||||
SubSteps []WorkflowStepBase `json:"subSteps,omitempty"`
|
||||
}
|
||||
|
||||
@@ -87,8 +87,8 @@ func (w *Workflow) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom primary key
|
||||
func (w *Workflow) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (w *Workflow) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if w.Name != "" {
|
||||
index["name"] = w.Name
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func (w *Workflow) Index() map[string]string {
|
||||
index["envName"] = w.EnvName
|
||||
}
|
||||
if w.Default != nil {
|
||||
index["default"] = strconv.FormatBool(*w.Default)
|
||||
index["default"] = *w.Default
|
||||
}
|
||||
|
||||
return index
|
||||
@@ -116,14 +116,18 @@ type WorkflowRecord struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
StartTime time.Time `json:"startTime,omitempty"`
|
||||
EndTime time.Time `json:"endTime,omitempty"`
|
||||
Finished string `json:"finished"`
|
||||
Steps []WorkflowStepStatus `json:"steps,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Mode string `json:"mode"`
|
||||
ContextValue map[string]string `json:"contextValue,omitempty"`
|
||||
}
|
||||
|
||||
// WorkflowStepStatus is the workflow step status database model
|
||||
type WorkflowStepStatus struct {
|
||||
StepStatus `json:",inline"`
|
||||
StepStatus `json:",inline" bson:",inline"`
|
||||
SubStepsStatus []StepStatus `json:"subSteps,omitempty"`
|
||||
}
|
||||
|
||||
@@ -156,8 +160,8 @@ func (w *WorkflowRecord) PrimaryKey() string {
|
||||
}
|
||||
|
||||
// Index return custom primary key
|
||||
func (w *WorkflowRecord) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
func (w *WorkflowRecord) Index() map[string]interface{} {
|
||||
index := make(map[string]interface{})
|
||||
if w.Name != "" {
|
||||
index["name"] = w.Name
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func CreateEnv(ctx context.Context, kubeClient client.Client, ds datastore.DataS
|
||||
env.Namespace = env.Name
|
||||
}
|
||||
|
||||
// create namespace at first
|
||||
// Creating the namespace at first.
|
||||
err = util.CreateOrUpdateNamespace(ctx, kubeClient, env.Namespace,
|
||||
util.MergeOverrideLabels(map[string]string{
|
||||
oam.LabelControlPlaneNamespaceUsage: oam.VelaNamespaceUsageEnv,
|
||||
|
||||
@@ -67,6 +67,10 @@ func ListFullEnvBinding(ctx context.Context, ds datastore.DataStore, option EnvL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workflows, err := ListWorkflowForApp(ctx, ds, option.AppPrimaryKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []*apisv1.EnvBindingBase
|
||||
for _, eb := range envBindings {
|
||||
env, err := pickEnv(envs, eb.Name)
|
||||
@@ -74,7 +78,11 @@ func ListFullEnvBinding(ctx context.Context, ds datastore.DataStore, option EnvL
|
||||
klog.Errorf("envbinding invalid %s", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, assembler.ConvertEnvBindingModelToBase(eb, env, targets))
|
||||
workflow, err := pickEnvWorkflow(workflows, eb.Name)
|
||||
if err != nil {
|
||||
klog.Errorf("workflow invalid %s", err.Error())
|
||||
}
|
||||
list = append(list, assembler.ConvertEnvBindingModelToBase(eb, env, targets, workflow))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
@@ -111,3 +119,12 @@ func pickEnv(envs []*model.Env, name string) (*model.Env, error) {
|
||||
}
|
||||
return nil, bcode.ErrEnvNotExisted
|
||||
}
|
||||
|
||||
func pickEnvWorkflow(workflows []*model.Workflow, name string) (*model.Workflow, error) {
|
||||
for _, w := range workflows {
|
||||
if w.EnvName == name {
|
||||
return w, nil
|
||||
}
|
||||
}
|
||||
return nil, bcode.ErrWorkflowNotExist
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func NewDatastore(cfg datastore.Config) (ds datastore.DataStore, err error) {
|
||||
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
|
||||
}
|
||||
case "kubeapi":
|
||||
ds, err = kubeapi.New(context.Background(), cfg)
|
||||
ds, err = kubeapi.New(context.Background(), cfg, k8sClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
|
||||
}
|
||||
|
||||
@@ -639,6 +639,38 @@ func GetWorkflowForApp(ctx context.Context, ds datastore.DataStore, app *model.A
|
||||
return &workflow, nil
|
||||
}
|
||||
|
||||
// GetWorkflowByEnv get the workflow by specified environment name.
|
||||
func GetWorkflowByEnv(ctx context.Context, ds datastore.DataStore, app *model.Application, envName string) (*model.Workflow, error) {
|
||||
var workflow = model.Workflow{
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
EnvName: envName,
|
||||
}
|
||||
res, err := ds.List(ctx, &workflow, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return res[0].(*model.Workflow), nil
|
||||
}
|
||||
return nil, bcode.ErrWorkflowNotExist
|
||||
}
|
||||
|
||||
// ListWorkflowForApp list all workflows of the application
|
||||
func ListWorkflowForApp(ctx context.Context, ds datastore.DataStore, appPrimaryKey string) ([]*model.Workflow, error) {
|
||||
var workflow = model.Workflow{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
}
|
||||
workflows, err := ds.List(ctx, &workflow, &datastore.ListOptions{SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []*model.Workflow
|
||||
for _, w := range workflows {
|
||||
res = append(res, w.(*model.Workflow))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ConvertWorkflowName generate the workflow name
|
||||
func ConvertWorkflowName(envName string) string {
|
||||
return fmt.Sprintf("workflow-%s", envName)
|
||||
|
||||
@@ -67,6 +67,7 @@ type AddonService interface {
|
||||
DisableAddon(ctx context.Context, name string, force bool) error
|
||||
ListEnabledAddon(ctx context.Context) ([]*apis.AddonBaseStatus, error)
|
||||
UpdateAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
// AddonImpl2AddonRes convert pkgaddon.UIData to the type apiserver need
|
||||
@@ -116,44 +117,35 @@ func AddonImpl2AddonRes(impl *pkgaddon.UIData, config *rest.Config) (*apis.Detai
|
||||
|
||||
// NewAddonService returns an addon service
|
||||
func NewAddonService(cacheTime time.Duration) AddonService {
|
||||
config, err := clients.GetKubeConfig()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dc, err := clients.GetDiscoveryClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ds := pkgaddon.NewRegistryDataStore(kubecli)
|
||||
cache := pkgaddon.NewCache(ds)
|
||||
|
||||
// TODO(@wonderflow): it's better to add a close channel here, but it should be fine as it's only invoke once in APIServer.
|
||||
go cache.DiscoverAndRefreshLoop(cacheTime)
|
||||
|
||||
return &addonServiceImpl{
|
||||
addonRegistryCache: cache,
|
||||
addonRegistryDS: ds,
|
||||
kubeClient: kubecli,
|
||||
config: config,
|
||||
apply: apply.NewAPIApplicator(kubecli),
|
||||
mutex: new(sync.RWMutex),
|
||||
discoveryClient: dc,
|
||||
cacheTime: cacheTime,
|
||||
mutex: new(sync.RWMutex),
|
||||
discoveryClient: dc,
|
||||
}
|
||||
}
|
||||
|
||||
type addonServiceImpl struct {
|
||||
cacheTime time.Duration
|
||||
addonRegistryCache *pkgaddon.Cache
|
||||
addonRegistryDS pkgaddon.RegistryDataStore
|
||||
kubeClient client.Client
|
||||
config *rest.Config
|
||||
apply apply.Applicator
|
||||
RegistryDS pkgaddon.RegistryDataStore `inject:"registryDatastore"`
|
||||
KubeClient client.Client `inject:"kubeClient"`
|
||||
KubeConfig *rest.Config `inject:"kubeConfig"`
|
||||
Apply apply.Applicator `inject:"apply"`
|
||||
discoveryClient *discovery.DiscoveryClient
|
||||
mutex *sync.RWMutex
|
||||
}
|
||||
|
||||
mutex *sync.RWMutex
|
||||
func (u *addonServiceImpl) Init(ctx context.Context) error {
|
||||
cache := pkgaddon.NewCache(u.RegistryDS)
|
||||
// TODO(@wonderflow): it's better to add a close channel here, but it should be fine as it's only invoke once in APIServer.
|
||||
go cache.DiscoverAndRefreshLoop(ctx, u.cacheTime)
|
||||
u.addonRegistryCache = cache
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAddon will get addon information
|
||||
@@ -161,7 +153,7 @@ func (u *addonServiceImpl) GetAddon(ctx context.Context, name string, registry s
|
||||
var addon *pkgaddon.UIData
|
||||
var err error
|
||||
if registry == "" {
|
||||
registries, err := u.addonRegistryDS.ListRegistries(ctx)
|
||||
registries, err := u.RegistryDS.ListRegistries(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -175,7 +167,7 @@ func (u *addonServiceImpl) GetAddon(ctx context.Context, name string, registry s
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addonRegistry, err := u.addonRegistryDS.GetRegistry(ctx, registry)
|
||||
addonRegistry, err := u.RegistryDS.GetRegistry(ctx, registry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -189,9 +181,9 @@ func (u *addonServiceImpl) GetAddon(ctx context.Context, name string, registry s
|
||||
return nil, bcode.ErrAddonNotExist
|
||||
}
|
||||
|
||||
addon.UISchema = renderAddonCustomUISchema(ctx, u.kubeClient, name, renderDefaultUISchema(addon.APISchema))
|
||||
addon.UISchema = renderAddonCustomUISchema(ctx, u.KubeClient, name, renderDefaultUISchema(addon.APISchema))
|
||||
|
||||
a, err := AddonImpl2AddonRes(addon, u.config)
|
||||
a, err := AddonImpl2AddonRes(addon, u.KubeConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -199,12 +191,12 @@ func (u *addonServiceImpl) GetAddon(ctx context.Context, name string, registry s
|
||||
}
|
||||
|
||||
func (u *addonServiceImpl) StatusAddon(ctx context.Context, name string) (*apis.AddonStatusResponse, error) {
|
||||
status, err := pkgaddon.GetAddonStatus(ctx, u.kubeClient, name)
|
||||
status, err := pkgaddon.GetAddonStatus(ctx, u.KubeClient, name)
|
||||
if err != nil {
|
||||
return nil, bcode.ErrGetAddonApplication
|
||||
}
|
||||
var allClusters []apis.NameAlias
|
||||
clusters, err := multicluster.ListVirtualClusters(ctx, u.kubeClient)
|
||||
clusters, err := multicluster.ListVirtualClusters(ctx, u.KubeClient)
|
||||
if err != nil {
|
||||
klog.Errorf("err while list all clusters: %v", err)
|
||||
}
|
||||
@@ -235,7 +227,7 @@ func (u *addonServiceImpl) StatusAddon(ctx context.Context, name string) (*apis.
|
||||
}
|
||||
|
||||
var sec v1.Secret
|
||||
err = u.kubeClient.Get(ctx, client.ObjectKey{
|
||||
err = u.KubeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: addonutil.Addon2SecName(name),
|
||||
}, &sec)
|
||||
@@ -255,7 +247,7 @@ func (u *addonServiceImpl) StatusAddon(ctx context.Context, name string) (*apis.
|
||||
|
||||
func (u *addonServiceImpl) ListAddons(ctx context.Context, registry, query string) ([]*apis.DetailAddonResponse, error) {
|
||||
var addons []*pkgaddon.UIData
|
||||
rs, err := u.addonRegistryDS.ListRegistries(ctx)
|
||||
rs, err := u.RegistryDS.ListRegistries(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -300,7 +292,7 @@ func (u *addonServiceImpl) ListAddons(ctx context.Context, registry, query strin
|
||||
|
||||
var addonResources []*apis.DetailAddonResponse
|
||||
for _, a := range addons {
|
||||
addonRes, err := AddonImpl2AddonRes(a, u.config)
|
||||
addonRes, err := AddonImpl2AddonRes(a, u.KubeConfig)
|
||||
if err != nil {
|
||||
klog.Errorf("err while converting AddonImpl to DetailAddonResponse: %v", err)
|
||||
continue
|
||||
@@ -314,13 +306,13 @@ func (u *addonServiceImpl) ListAddons(ctx context.Context, registry, query strin
|
||||
}
|
||||
|
||||
func (u *addonServiceImpl) DeleteAddonRegistry(ctx context.Context, name string) error {
|
||||
return u.addonRegistryDS.DeleteRegistry(ctx, name)
|
||||
return u.RegistryDS.DeleteRegistry(ctx, name)
|
||||
}
|
||||
|
||||
func (u *addonServiceImpl) CreateAddonRegistry(ctx context.Context, req apis.CreateAddonRegistryRequest) (*apis.AddonRegistry, error) {
|
||||
r := addonRegistryModelFromCreateAddonRegistryRequest(req)
|
||||
|
||||
err := u.addonRegistryDS.AddRegistry(ctx, r)
|
||||
err := u.RegistryDS.AddRegistry(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -340,7 +332,7 @@ func convertAddonRegistry(r pkgaddon.Registry) *apis.AddonRegistry {
|
||||
}
|
||||
|
||||
func (u *addonServiceImpl) GetAddonRegistry(ctx context.Context, name string) (*apis.AddonRegistry, error) {
|
||||
r, err := u.addonRegistryDS.GetRegistry(ctx, name)
|
||||
r, err := u.RegistryDS.GetRegistry(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -348,7 +340,7 @@ func (u *addonServiceImpl) GetAddonRegistry(ctx context.Context, name string) (*
|
||||
}
|
||||
|
||||
func (u addonServiceImpl) UpdateAddonRegistry(ctx context.Context, name string, req apis.UpdateAddonRegistryRequest) (*apis.AddonRegistry, error) {
|
||||
r, err := u.addonRegistryDS.GetRegistry(ctx, name)
|
||||
r, err := u.RegistryDS.GetRegistry(ctx, name)
|
||||
if err != nil {
|
||||
return nil, bcode.ErrAddonRegistryNotExist
|
||||
}
|
||||
@@ -365,7 +357,7 @@ func (u addonServiceImpl) UpdateAddonRegistry(ctx context.Context, name string,
|
||||
r.Gitlab = req.Gitlab
|
||||
}
|
||||
|
||||
err = u.addonRegistryDS.UpdateRegistry(ctx, r)
|
||||
err = u.RegistryDS.UpdateRegistry(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -376,7 +368,7 @@ func (u addonServiceImpl) UpdateAddonRegistry(ctx context.Context, name string,
|
||||
func (u *addonServiceImpl) ListAddonRegistries(ctx context.Context) ([]*apis.AddonRegistry, error) {
|
||||
|
||||
var list []*apis.AddonRegistry
|
||||
registries, err := u.addonRegistryDS.ListRegistries(ctx)
|
||||
registries, err := u.RegistryDS.ListRegistries(ctx)
|
||||
if err != nil {
|
||||
// the storage configmap still not exist, don't return error add registry will create the configmap
|
||||
if errors2.IsNotFound(err) {
|
||||
@@ -396,7 +388,7 @@ func (u *addonServiceImpl) ListAddonRegistries(ctx context.Context) ([]*apis.Add
|
||||
|
||||
func (u *addonServiceImpl) EnableAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error {
|
||||
var err error
|
||||
registries, err := u.addonRegistryDS.ListRegistries(ctx)
|
||||
registries, err := u.RegistryDS.ListRegistries(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -416,7 +408,7 @@ func (u *addonServiceImpl) EnableAddon(ctx context.Context, name string, args ap
|
||||
continue
|
||||
}
|
||||
// TODO: response the additional info to velaux users
|
||||
_, err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
|
||||
_, err = pkgaddon.EnableAddon(ctx, name, args.Version, u.KubeClient, u.discoveryClient, u.Apply, u.KubeConfig, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -441,7 +433,7 @@ func (u *addonServiceImpl) EnableAddon(ctx context.Context, name string, args ap
|
||||
}
|
||||
|
||||
func (u *addonServiceImpl) DisableAddon(ctx context.Context, name string, force bool) error {
|
||||
err := pkgaddon.DisableAddon(ctx, u.kubeClient, name, u.config, force)
|
||||
err := pkgaddon.DisableAddon(ctx, u.KubeClient, name, u.KubeConfig, force)
|
||||
if err != nil {
|
||||
klog.Errorf("delete application fail: %s", err.Error())
|
||||
return err
|
||||
@@ -451,7 +443,7 @@ func (u *addonServiceImpl) DisableAddon(ctx context.Context, name string, force
|
||||
|
||||
func (u *addonServiceImpl) ListEnabledAddon(ctx context.Context) ([]*apis.AddonBaseStatus, error) {
|
||||
apps := &v1beta1.ApplicationList{}
|
||||
if err := u.kubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), client.HasLabels{oam.LabelAddonName}); err != nil {
|
||||
if err := u.KubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), client.HasLabels{oam.LabelAddonName}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []*apis.AddonBaseStatus
|
||||
@@ -470,10 +462,9 @@ func (u *addonServiceImpl) ListEnabledAddon(ctx context.Context) ([]*apis.AddonB
|
||||
}
|
||||
|
||||
func (u *addonServiceImpl) UpdateAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error {
|
||||
|
||||
var app v1beta1.Application
|
||||
// check addon application whether exist
|
||||
err := u.kubeClient.Get(ctx, client.ObjectKey{
|
||||
err := u.KubeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: addonutil.Addon2AppName(name),
|
||||
}, &app)
|
||||
@@ -481,22 +472,20 @@ func (u *addonServiceImpl) UpdateAddon(ctx context.Context, name string, args ap
|
||||
return err
|
||||
}
|
||||
|
||||
registries, err := u.addonRegistryDS.ListRegistries(ctx)
|
||||
registries, err := u.RegistryDS.ListRegistries(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, r := range registries {
|
||||
// TODO: response the additional info to velaux users
|
||||
_, err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
|
||||
_, err = pkgaddon.EnableAddon(ctx, name, args.Version, u.KubeClient, u.discoveryClient, u.Apply, u.KubeConfig, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if errors.Is(err, pkgaddon.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
// wrap this error with special bcode
|
||||
if errors.As(err, &pkgaddon.VersionUnMatchError{}) {
|
||||
return bcode.ErrAddonSystemVersionMismatch
|
||||
|
||||
@@ -19,7 +19,6 @@ package service
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
@@ -42,7 +41,7 @@ import (
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/repository"
|
||||
syncconvert "github.com/oam-dev/kubevela/pkg/apiserver/event/sync/convert"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/event/sync/convert"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
assembler "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/assembler/v1"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
@@ -52,6 +51,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
pkgUtils "github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/app"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
commonutil "github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
@@ -90,6 +90,7 @@ type ApplicationService interface {
|
||||
UpdateApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, traitType string, req apisv1.UpdateApplicationTraitRequest) (*apisv1.ApplicationTrait, error)
|
||||
ListRevisions(ctx context.Context, appName, envName, status string, page, pageSize int) (*apisv1.ListRevisionsResponse, error)
|
||||
DetailRevision(ctx context.Context, appName, revisionName string) (*apisv1.DetailRevisionResponse, error)
|
||||
RollbackWithRevision(ctx context.Context, app *model.Application, revisionName string) (*apisv1.ApplicationRollbackResponse, error)
|
||||
Statistics(ctx context.Context, app *model.Application) (*apisv1.ApplicationStatisticsResponse, error)
|
||||
ListRecords(ctx context.Context, appName string) (*apisv1.ListWorkflowRecordsResponse, error)
|
||||
CompareApp(ctx context.Context, app *model.Application, compareReq apisv1.AppCompareReq) (*apisv1.AppCompareResponse, error)
|
||||
@@ -538,7 +539,9 @@ func (c *applicationServiceImpl) ListRecords(ctx context.Context, appName string
|
||||
AppPrimaryKey: appName,
|
||||
Finished: model.UnFinished,
|
||||
}
|
||||
records, err := c.Store.List(ctx, &record, &datastore.ListOptions{})
|
||||
records, err := c.Store.List(ctx, &record, &datastore.ListOptions{SortBy: []datastore.SortOption{
|
||||
{Key: "createTime", Order: datastore.SortOrderDescending},
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -604,7 +607,8 @@ func (c *applicationServiceImpl) DetailComponent(ctx context.Context, app *model
|
||||
return nil, err
|
||||
}
|
||||
var cd v1beta1.ComponentDefinition
|
||||
if err := c.KubeClient.Get(ctx, types.NamespacedName{Name: component.Type, Namespace: velatypes.DefaultKubeVelaNS}, &cd); err != nil {
|
||||
loadCtx := utils.WithProject(ctx, "")
|
||||
if err := c.KubeClient.Get(loadCtx, types.NamespacedName{Name: component.Type, Namespace: velatypes.DefaultKubeVelaNS}, &cd); err != nil {
|
||||
klog.Warningf("component definition %s get failure. %s", pkgUtils.Sanitize(component.Type), err.Error())
|
||||
}
|
||||
|
||||
@@ -672,7 +676,7 @@ func (c *applicationServiceImpl) Deploy(ctx context.Context, app *model.Applicat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// step2: check and create deploy event
|
||||
// step2: check and create application revision
|
||||
if !req.Force {
|
||||
var lastVersion = model.ApplicationRevision{
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
@@ -698,17 +702,18 @@ func (c *applicationServiceImpl) Deploy(ctx context.Context, app *model.Applicat
|
||||
} else {
|
||||
status = revision.Status
|
||||
}
|
||||
if status != model.RevisionStatusComplete && status != model.RevisionStatusTerminated {
|
||||
klog.Warningf("last app revision can not complete %s/%s", list[0].(*model.ApplicationRevision).AppPrimaryKey, list[0].(*model.ApplicationRevision).Version)
|
||||
if status != model.RevisionStatusComplete && status != model.RevisionStatusTerminated && status != model.RevisionStatusFail {
|
||||
klog.Warningf("last app revision can not complete %s/%s,the current status is %s", list[0].(*model.ApplicationRevision).AppPrimaryKey, list[0].(*model.ApplicationRevision).Version, status)
|
||||
return nil, bcode.ErrDeployConflict
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var appRevision = &model.ApplicationRevision{
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
Version: version,
|
||||
RevisionCRName: version,
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
Version: version,
|
||||
// Setting it when syncing the workflow status
|
||||
RevisionCRName: "",
|
||||
ApplyAppConfig: string(configByte),
|
||||
Status: model.RevisionStatusInit,
|
||||
DeployUser: userName,
|
||||
@@ -745,7 +750,8 @@ func (c *applicationServiceImpl) Deploy(ctx context.Context, app *model.Applicat
|
||||
}
|
||||
|
||||
// step5: create workflow record
|
||||
if err := c.WorkflowService.CreateWorkflowRecord(ctx, app, oamApp, workflow); err != nil {
|
||||
record, err := c.WorkflowService.CreateWorkflowRecord(ctx, app, oamApp, workflow)
|
||||
if err != nil {
|
||||
klog.Warningf("create workflow record failure %s", err.Error())
|
||||
}
|
||||
|
||||
@@ -764,9 +770,14 @@ func (c *applicationServiceImpl) Deploy(ctx context.Context, app *model.Applicat
|
||||
klog.Warningf("failed to update app %s", err.Error())
|
||||
}
|
||||
|
||||
return &apisv1.ApplicationDeployResponse{
|
||||
res := &apisv1.ApplicationDeployResponse{
|
||||
ApplicationRevisionBase: c.convertRevisionModelToBase(ctx, appRevision),
|
||||
}, nil
|
||||
}
|
||||
if record != nil {
|
||||
res.WorkflowRecord = assembler.ConvertFromRecordModel(record).WorkflowRecordBase
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *applicationServiceImpl) renderOAMApplication(ctx context.Context, appModel *model.Application, reqWorkflowName, envName, version string) (*v1beta1.Application, error) {
|
||||
@@ -901,26 +912,41 @@ func (c *applicationServiceImpl) renderOAMApplication(ctx context.Context, appMo
|
||||
app.Annotations[oam.AnnotationWorkflowName] = workflow.Name
|
||||
var steps []workflowv1alpha1.WorkflowStep
|
||||
for _, step := range workflow.Steps {
|
||||
var workflowStep = workflowv1alpha1.WorkflowStep{
|
||||
WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
|
||||
Name: step.Name,
|
||||
Type: step.Type,
|
||||
Inputs: step.Inputs,
|
||||
Outputs: step.Outputs,
|
||||
},
|
||||
workflowStep := workflowv1alpha1.WorkflowStep{
|
||||
WorkflowStepBase: convertWorkflowModel2WorkflowSpec(step.WorkflowStepBase),
|
||||
}
|
||||
if step.Properties != nil {
|
||||
workflowStep.Properties = step.Properties.RawExtension()
|
||||
for _, subStep := range step.SubSteps {
|
||||
workflowStep.SubSteps = append(workflowStep.SubSteps, convertWorkflowModel2WorkflowSpec(subStep))
|
||||
}
|
||||
steps = append(steps, workflowStep)
|
||||
}
|
||||
app.Spec.Workflow = &v1beta1.Workflow{
|
||||
Steps: steps,
|
||||
Mode: &workflow.Mode,
|
||||
}
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func convertWorkflowModel2WorkflowSpec(step model.WorkflowStepBase) workflowv1alpha1.WorkflowStepBase {
|
||||
var workflowStep = workflowv1alpha1.WorkflowStepBase{
|
||||
Name: step.Name,
|
||||
Type: step.Type,
|
||||
Inputs: step.Inputs,
|
||||
Outputs: step.Outputs,
|
||||
If: step.If,
|
||||
Timeout: step.Timeout,
|
||||
DependsOn: step.DependsOn,
|
||||
Meta: &workflowv1alpha1.WorkflowStepMeta{
|
||||
Alias: step.Alias,
|
||||
},
|
||||
}
|
||||
if step.Properties != nil {
|
||||
workflowStep.Properties = step.Properties.RawExtension()
|
||||
}
|
||||
return workflowStep
|
||||
}
|
||||
|
||||
func (c *applicationServiceImpl) convertRevisionModelToBase(ctx context.Context, revision *model.ApplicationRevision) apisv1.ApplicationRevisionBase {
|
||||
var deployUser *model.User
|
||||
if revision.DeployUser != "" {
|
||||
@@ -1047,7 +1073,8 @@ func (c *applicationServiceImpl) UpdateComponent(ctx context.Context, app *model
|
||||
|
||||
func (c *applicationServiceImpl) createComponent(ctx context.Context, app *model.Application, com apisv1.CreateComponentRequest, main bool) (*apisv1.ComponentBase, error) {
|
||||
var cd v1beta1.ComponentDefinition
|
||||
if err := c.KubeClient.Get(ctx, types.NamespacedName{Name: com.ComponentType, Namespace: velatypes.DefaultKubeVelaNS}, &cd); err != nil {
|
||||
loadCtx := utils.WithProject(ctx, "")
|
||||
if err := c.KubeClient.Get(loadCtx, types.NamespacedName{Name: com.ComponentType, Namespace: velatypes.DefaultKubeVelaNS}, &cd); err != nil {
|
||||
klog.Warningf("component definition %s get failure. %s", pkgUtils.Sanitize(com.ComponentType), err.Error())
|
||||
return nil, bcode.ErrComponentTypeNotSupport
|
||||
}
|
||||
@@ -1405,13 +1432,29 @@ func (c *applicationServiceImpl) CompareApp(ctx context.Context, appModel *model
|
||||
var base, compareTarget *v1beta1.Application
|
||||
var err error
|
||||
var envNameByRevision string
|
||||
|
||||
getRunningApp := func() *v1beta1.Application {
|
||||
var envName string
|
||||
if compareReq.CompareLatestWithRunning != nil {
|
||||
envName = compareReq.CompareLatestWithRunning.Env
|
||||
}
|
||||
if compareReq.CompareRevisionWithRunning != nil {
|
||||
envName = envNameByRevision
|
||||
}
|
||||
if envName == "" {
|
||||
return nil
|
||||
}
|
||||
app, err := c.GetApplicationCRInEnv(ctx, appModel, envName)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to query the application CR %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
return app
|
||||
}
|
||||
switch {
|
||||
case compareReq.CompareLatestWithRunning != nil:
|
||||
base, err = c.renderOAMApplication(ctx, appModel, "", compareReq.CompareLatestWithRunning.Env, "")
|
||||
if err != nil {
|
||||
klog.Errorf("failed to build the latest application %s", err.Error())
|
||||
break
|
||||
}
|
||||
base = getRunningApp()
|
||||
|
||||
case compareReq.CompareRevisionWithRunning != nil || compareReq.CompareRevisionWithLatest != nil:
|
||||
var revision = ""
|
||||
if compareReq.CompareRevisionWithRunning != nil {
|
||||
@@ -1428,23 +1471,9 @@ func (c *applicationServiceImpl) CompareApp(ctx context.Context, appModel *model
|
||||
}
|
||||
|
||||
switch {
|
||||
case compareReq.CompareLatestWithRunning != nil || compareReq.CompareRevisionWithRunning != nil:
|
||||
var envName string
|
||||
if compareReq.CompareLatestWithRunning != nil {
|
||||
envName = compareReq.CompareLatestWithRunning.Env
|
||||
}
|
||||
if compareReq.CompareRevisionWithRunning != nil {
|
||||
envName = envNameByRevision
|
||||
}
|
||||
if envName == "" {
|
||||
break
|
||||
}
|
||||
compareTarget, err = c.GetApplicationCRInEnv(ctx, appModel, envName)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to query the application CR %s", err.Error())
|
||||
break
|
||||
}
|
||||
case compareReq.CompareRevisionWithLatest != nil:
|
||||
case compareReq.CompareRevisionWithRunning != nil:
|
||||
compareTarget = getRunningApp()
|
||||
case compareReq.CompareRevisionWithLatest != nil || compareReq.CompareLatestWithRunning != nil:
|
||||
compareTarget, err = c.renderOAMApplication(ctx, appModel, "", envNameByRevision, "")
|
||||
if err != nil {
|
||||
klog.Errorf("failed to build the latest application %s", err.Error())
|
||||
@@ -1513,6 +1542,13 @@ func (c *applicationServiceImpl) DryRunAppOrRevision(ctx context.Context, appMod
|
||||
}
|
||||
case "REVISION":
|
||||
app, _, err = c.getAppModelFromRevision(ctx, appModel.Name, dryRunReq.Version)
|
||||
originalApp := &v1beta1.Application{}
|
||||
if err := c.KubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: app.Name,
|
||||
Namespace: app.Namespace,
|
||||
}, originalApp); err == nil {
|
||||
app.ResourceVersion = originalApp.ResourceVersion
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1598,7 +1634,7 @@ func (c *applicationServiceImpl) resetApp(ctx context.Context, targetApp *v1beta
|
||||
for _, comp := range targetComps {
|
||||
// add or update new app's components from old app
|
||||
if utils.StringsContain(readyToAdd, comp.Name) || utils.StringsContain(readyToUpdate, comp.Name) {
|
||||
compModel, err := syncconvert.FromCRComponent(appPrimaryKey, comp)
|
||||
compModel, err := convert.FromCRComponent(appPrimaryKey, comp)
|
||||
if err != nil {
|
||||
return &apisv1.AppResetResponse{}, bcode.ErrInvalidProperties
|
||||
}
|
||||
@@ -1623,6 +1659,71 @@ func (c *applicationServiceImpl) resetApp(ctx context.Context, targetApp *v1beta
|
||||
return &apisv1.AppResetResponse{IsReset: true}, nil
|
||||
}
|
||||
|
||||
func (c *applicationServiceImpl) RollbackWithRevision(ctx context.Context, application *model.Application, revisionVersion string) (*apisv1.ApplicationRollbackResponse, error) {
|
||||
revision, err := c.DetailRevision(ctx, application.Name, revisionVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appCR, err := c.GetApplicationCRInEnv(ctx, application, revision.EnvName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var publishVersion = utils.GenerateVersion(revision.WorkflowName)
|
||||
noRevision := false
|
||||
var rollbackApplication *v1beta1.Application
|
||||
if appCR != nil {
|
||||
// The RevisionCRName is incorrect in the old version, ignore it.
|
||||
if revision.RevisionCRName == revision.Version || revision.RevisionCRName == "" {
|
||||
noRevision = true
|
||||
} else {
|
||||
_, appCR, err := app.RollbackApplicationWithRevision(ctx, c.KubeClient, appCR.Name, appCR.Namespace, revision.RevisionCRName, publishVersion)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, app.ErrNotMatchRevision):
|
||||
noRevision = true
|
||||
case errors.Is(err, app.ErrRevisionNotChange):
|
||||
return nil, bcode.ErrApplicationRevisionConflict
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
rollbackApplication = appCR
|
||||
}
|
||||
}
|
||||
|
||||
// Rollback by the local revision
|
||||
if appCR == nil || noRevision {
|
||||
rollBackApp := &v1beta1.Application{}
|
||||
if err := yaml.Unmarshal([]byte(revision.ApplyAppConfig), rollBackApp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oam.SetPublishVersion(rollBackApp, publishVersion)
|
||||
if appCR != nil {
|
||||
rollBackApp.ResourceVersion = appCR.ResourceVersion
|
||||
} else {
|
||||
rollBackApp.ResourceVersion = ""
|
||||
}
|
||||
err = c.Apply.Apply(ctx, rollBackApp)
|
||||
if err != nil {
|
||||
klog.Errorf("rollback the app %s failure %s", application.PrimaryKey(), err.Error())
|
||||
return nil, err
|
||||
}
|
||||
rollbackApplication = rollBackApp
|
||||
}
|
||||
|
||||
work, _, err := convert.FromCRWorkflow(ctx, c.KubeClient, application.PrimaryKey(), rollbackApplication, revision.EnvName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
record, err := c.WorkflowService.CreateWorkflowRecord(ctx, application, rollbackApplication, &work)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create workflow record failure %w", err)
|
||||
}
|
||||
return &apisv1.ApplicationRollbackResponse{
|
||||
WorkflowRecord: assembler.ConvertFromRecordModel(record).WorkflowRecordBase,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func dryRunApplication(ctx context.Context, c commonutil.Args, app *v1beta1.Application) (bytes.Buffer, error) {
|
||||
var buff = bytes.Buffer{}
|
||||
if _, err := fmt.Fprintf(&buff, "---\n# Application(%s) \n---\n\n", app.Name); err != nil {
|
||||
@@ -1666,9 +1767,7 @@ func dryRunApplication(ctx context.Context, c commonutil.Args, app *v1beta1.Appl
|
||||
// ignore the workflow spec
|
||||
func ignoreSomeParams(o *v1beta1.Application) {
|
||||
var defaultApplication = v1beta1.Application{}
|
||||
// only compare the spec without the workflow
|
||||
defaultApplication.Spec = o.Spec
|
||||
defaultApplication.Spec.Workflow = nil
|
||||
defaultApplication.Name = o.Name
|
||||
defaultApplication.Namespace = o.Namespace
|
||||
|
||||
@@ -1800,11 +1899,7 @@ func (c *applicationServiceImpl) handlePolicyBindingWorkflowStep(ctx context.Con
|
||||
}
|
||||
if added || deleted {
|
||||
properties["policies"] = policies
|
||||
pStr, err := json.Marshal(properties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Steps[i].Properties = string(pStr)
|
||||
w.Steps[i].Properties = properties
|
||||
needUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
|
||||
wfTypes "github.com/kubevela/workflow/pkg/types"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -530,8 +532,10 @@ var _ = Describe("Test application service function", func() {
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(compareResponse.IsDiff, true)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(compareResponse.TargetAppYAML, "")).Should(BeEmpty())
|
||||
Expect(cmp.Diff(compareResponse.BaseAppYAML, "")).ShouldNot(BeEmpty())
|
||||
// The target represents the latest config
|
||||
Expect(cmp.Diff(compareResponse.TargetAppYAML, "")).ShouldNot(BeEmpty())
|
||||
// The base represents the running config
|
||||
Expect(cmp.Diff(compareResponse.BaseAppYAML, "")).Should(BeEmpty())
|
||||
|
||||
By("compare when app's env add target, should return false")
|
||||
_, err = targetService.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: "dev-target1", Project: appModel.Project, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: "dev-target1"}})
|
||||
@@ -850,21 +854,21 @@ var _ = Describe("Test apiserver policy rest api", func() {
|
||||
WorkflowStepBase: v1.WorkflowStepBase{
|
||||
Name: "default",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["local"]}`,
|
||||
Properties: map[string]interface{}{"policies": []string{"local"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
WorkflowStepBase: v1.WorkflowStepBase{
|
||||
Name: "suspend",
|
||||
Type: "suspend",
|
||||
Properties: `{"duration": "10m"}`,
|
||||
Properties: map[string]interface{}{"duration": "10m"},
|
||||
},
|
||||
},
|
||||
{
|
||||
WorkflowStepBase: v1.WorkflowStepBase{
|
||||
Name: "second",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["cluster1"]}`,
|
||||
Properties: map[string]interface{}{"policies": []string{"cluster1"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -880,7 +884,7 @@ var _ = Describe("Test apiserver policy rest api", func() {
|
||||
WorkflowStepBase: v1.WorkflowStepBase{
|
||||
Name: "second",
|
||||
Type: "deploy",
|
||||
Properties: `{"policies":["cluster3"]}`,
|
||||
Properties: map[string]interface{}{"policies": []string{"cluster3"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1015,11 +1019,32 @@ func createTestSuspendApp(ctx context.Context, appName, envName, revisionVersion
|
||||
Traits: []common.ApplicationTrait{},
|
||||
Scopes: map[string]string{},
|
||||
}},
|
||||
Workflow: &v1beta1.Workflow{
|
||||
Steps: []workflowv1alpha1.WorkflowStep{
|
||||
{
|
||||
WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
|
||||
Type: wfTypes.WorkflowStepTypeSuspend,
|
||||
Name: "first",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: common.AppStatus{
|
||||
Workflow: &common.WorkflowStatus{
|
||||
AppRevision: recordName,
|
||||
Suspend: true,
|
||||
Phase: workflowv1alpha1.WorkflowStateSuspending,
|
||||
Steps: []workflowv1alpha1.WorkflowStepStatus{
|
||||
{
|
||||
StepStatus: workflowv1alpha1.StepStatus{
|
||||
Type: wfTypes.WorkflowStepTypeSuspend,
|
||||
Name: "first",
|
||||
ID: "first",
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseRunning,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import (
|
||||
"github.com/oam-dev/terraform-controller/api/types"
|
||||
"github.com/oam-dev/terraform-controller/api/v1beta1"
|
||||
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
@@ -207,7 +206,7 @@ func joinClusterByKubeConfigString(ctx context.Context, k8sClient client.Client,
|
||||
defer func() {
|
||||
_ = os.Remove(tmpFileName)
|
||||
}()
|
||||
clusterConfig, err := multicluster.JoinClusterByKubeConfig(ctx, k8sClient, tmpFileName, clusterName, multicluster.JoinClusterCreateNamespaceOption(velatypes.DefaultKubeVelaNS))
|
||||
clusterConfig, err := multicluster.JoinClusterByKubeConfig(ctx, k8sClient, tmpFileName, clusterName, multicluster.JoinClusterCreateNamespaceOption(""))
|
||||
if err != nil {
|
||||
if errors.Is(err, multicluster.ErrClusterExists) {
|
||||
return "", bcode.ErrClusterExistsInKubernetes
|
||||
|
||||
@@ -22,15 +22,14 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/config"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
@@ -62,7 +61,8 @@ type configServiceImpl struct {
|
||||
|
||||
// ListTemplates list the config templates
|
||||
func (u *configServiceImpl) ListTemplates(ctx context.Context, project, scope string) ([]*apis.ConfigTemplate, error) {
|
||||
queryTemplates, err := u.Factory.ListTemplates(ctx, types.DefaultKubeVelaNS, scope)
|
||||
listCtx := utils.WithProject(ctx, "")
|
||||
queryTemplates, err := u.Factory.ListTemplates(listCtx, types.DefaultKubeVelaNS, scope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -100,7 +100,8 @@ func (u *configServiceImpl) GetTemplate(ctx context.Context, tem config.Namespac
|
||||
if tem.Namespace == "" {
|
||||
tem.Namespace = types.DefaultKubeVelaNS
|
||||
}
|
||||
template, err := u.Factory.LoadTemplate(ctx, tem.Name, tem.Namespace)
|
||||
getCtx := utils.WithProject(ctx, "")
|
||||
template, err := u.Factory.LoadTemplate(getCtx, tem.Name, tem.Namespace)
|
||||
if err != nil {
|
||||
if errors.Is(err, config.ErrTemplateNotFound) {
|
||||
return nil, bcode.ErrTemplateNotFound
|
||||
@@ -133,9 +134,14 @@ func (u *configServiceImpl) CreateConfig(ctx context.Context, project string, re
|
||||
return nil, err
|
||||
}
|
||||
ns = pro.GetNamespace()
|
||||
if err := utils.CreateNamespace(ctx, u.KubeClient, ns); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
exist, err := u.Factory.IsExist(ctx, ns, req.Name)
|
||||
if err != nil {
|
||||
klog.Errorf("check config name is exist failure %s", err.Error())
|
||||
return nil, bcode.ErrConfigExist
|
||||
}
|
||||
if exist {
|
||||
return nil, bcode.ErrConfigExist
|
||||
}
|
||||
var properties = make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(req.Properties), &properties); err != nil {
|
||||
@@ -156,9 +162,6 @@ func (u *configServiceImpl) CreateConfig(ctx context.Context, project string, re
|
||||
return nil, err
|
||||
}
|
||||
if err := u.Factory.CreateOrUpdateConfig(ctx, configItem, ns); err != nil {
|
||||
if errors.Is(err, config.ErrConfigExist) {
|
||||
return nil, bcode.ErrConfigExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return convertConfig(project, *configItem), nil
|
||||
@@ -196,9 +199,12 @@ func (u *configServiceImpl) UpdateConfig(ctx context.Context, project string, na
|
||||
return nil, err
|
||||
}
|
||||
if err := u.Factory.CreateOrUpdateConfig(ctx, configItem, ns); err != nil {
|
||||
if errors.Is(err, config.ErrConfigExist) {
|
||||
if errors.Is(err, config.ErrChangeTemplate) {
|
||||
return nil, bcode.ErrChangeTemplate
|
||||
}
|
||||
if errors.Is(err, config.ErrChangeSecretType) {
|
||||
return nil, bcode.ErrChangeSecretType
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return convertConfig(project, *configItem), nil
|
||||
@@ -210,6 +216,7 @@ func (u *configServiceImpl) ListConfigs(ctx context.Context, project string, tem
|
||||
var list []*apis.Config
|
||||
scope := ""
|
||||
var projectNamespace string
|
||||
listCtx := utils.WithProject(ctx, "")
|
||||
if project != "" {
|
||||
scope = "project"
|
||||
pro, err := u.ProjectService.GetProject(ctx, project)
|
||||
@@ -218,7 +225,7 @@ func (u *configServiceImpl) ListConfigs(ctx context.Context, project string, tem
|
||||
}
|
||||
projectNamespace = pro.GetNamespace()
|
||||
// query the configs belong to the project scope from the system namespace
|
||||
configs, err := u.Factory.ListConfigs(ctx, pro.GetNamespace(), template, "", true)
|
||||
configs, err := u.Factory.ListConfigs(listCtx, pro.GetNamespace(), template, "", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -227,7 +234,7 @@ func (u *configServiceImpl) ListConfigs(ctx context.Context, project string, tem
|
||||
}
|
||||
}
|
||||
|
||||
configs, err := u.Factory.ListConfigs(ctx, types.DefaultKubeVelaNS, template, scope, true)
|
||||
configs, err := u.Factory.ListConfigs(listCtx, types.DefaultKubeVelaNS, template, scope, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -239,7 +239,9 @@ func (p *envServiceImpl) UpdateEnv(ctx context.Context, name string, req apisv1.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := managePrivilegesForEnvironment(ctx, p.KubeClient, env, false); err != nil {
|
||||
// Updating the role and role binding can't use the login user permissions.
|
||||
updateRoleCtx := utils.WithProject(ctx, "")
|
||||
if err := managePrivilegesForEnvironment(updateRoleCtx, p.KubeClient, env, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -289,12 +291,14 @@ func (p *envServiceImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequ
|
||||
}
|
||||
}
|
||||
|
||||
err = repository.CreateEnv(ctx, p.KubeClient, p.Store, newEnv)
|
||||
// Creating the namespace can't use the login user permissions.
|
||||
createNamespaceCtx := utils.WithProject(ctx, "")
|
||||
err = repository.CreateEnv(createNamespaceCtx, p.KubeClient, p.Store, newEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := managePrivilegesForEnvironment(ctx, p.KubeClient, newEnv, false); err != nil {
|
||||
if err := managePrivilegesForEnvironment(createNamespaceCtx, p.KubeClient, newEnv, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -269,8 +269,12 @@ func (e *envBindingServiceImpl) DetailEnvBinding(ctx context.Context, app *model
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workflow, err := repository.GetWorkflowByEnv(ctx, e.Store, app, env.Name)
|
||||
if err != nil {
|
||||
klog.Warningf("failed to find the workflow for the app %s in environment %s:%s", app.Name, env.Name, err.Error())
|
||||
}
|
||||
return &apisv1.DetailEnvBindingResponse{
|
||||
EnvBindingBase: *assembler.ConvertEnvBindingModelToBase(envBinding, env, targets),
|
||||
EnvBindingBase: *assembler.ConvertEnvBindingModelToBase(envBinding, env, targets, workflow),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,8 @@ type PipelineRunService interface {
|
||||
GetPipelineRunOutput(ctx context.Context, meta apis.PipelineRun, step string) (apis.GetPipelineRunOutputResponse, error)
|
||||
GetPipelineRunInput(ctx context.Context, meta apis.PipelineRun, step string) (apis.GetPipelineRunInputResponse, error)
|
||||
GetPipelineRunLog(ctx context.Context, meta apis.PipelineRun, step string) (apis.GetPipelineRunLogResponse, error)
|
||||
ResumePipelineRun(ctx context.Context, meta apis.PipelineRunMeta) error
|
||||
TerminatePipelineRun(ctx context.Context, meta apis.PipelineRunMeta) error
|
||||
}
|
||||
|
||||
type pipelineRunServiceImpl struct {
|
||||
@@ -173,6 +175,7 @@ func (p pipelineServiceImpl) ListPipelines(ctx context.Context, req apis.ListPip
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var availableProjectNames []string
|
||||
var projectMap = make(map[string]model.Project, len(projects))
|
||||
for _, project := range projects {
|
||||
@@ -184,13 +187,23 @@ func (p pipelineServiceImpl) ListPipelines(ctx context.Context, req apis.ListPip
|
||||
if len(availableProjectNames) == 0 {
|
||||
return &apis.ListPipelineResponse{}, nil
|
||||
}
|
||||
queryProjects := req.Projects
|
||||
if len(req.Projects) > 0 {
|
||||
if !pkgutils.SliceIncludeSlice(availableProjectNames, req.Projects) {
|
||||
return &apis.ListPipelineResponse{}, nil
|
||||
}
|
||||
}
|
||||
if len(req.Projects) == 0 {
|
||||
queryProjects = availableProjectNames
|
||||
}
|
||||
|
||||
pp := model.Pipeline{}
|
||||
pipelines, err := p.Store.List(ctx, &pp, &datastore.ListOptions{
|
||||
FilterOptions: datastore.FilterOptions{
|
||||
In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
Values: availableProjectNames,
|
||||
Values: queryProjects,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1287,13 +1300,90 @@ func (p pipelineRunServiceImpl) terminatePipelineRun(ctx context.Context, run *v
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p pipelineRunServiceImpl) ResumePipelineRun(ctx context.Context, meta apis.PipelineRunMeta) error {
|
||||
project := ctx.Value(&apis.CtxKeyProject).(*model.Project)
|
||||
run := v1alpha1.WorkflowRun{}
|
||||
if err := p.KubeClient.Get(ctx, types.NamespacedName{
|
||||
Namespace: project.GetNamespace(),
|
||||
Name: meta.PipelineRunName,
|
||||
}, &run); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if run.Status.Terminated || run.Status.Finished {
|
||||
return bcode.ErrPipelineRunFinished
|
||||
}
|
||||
|
||||
run.Status.Suspend = false
|
||||
steps := run.Status.Steps
|
||||
for i, step := range steps {
|
||||
if step.Type == wfTypes.WorkflowStepTypeSuspend && step.Phase == v1alpha1.WorkflowStepPhaseRunning {
|
||||
steps[i].Phase = v1alpha1.WorkflowStepPhaseSucceeded
|
||||
}
|
||||
for j, sub := range step.SubStepsStatus {
|
||||
if sub.Type == wfTypes.WorkflowStepTypeSuspend && sub.Phase == v1alpha1.WorkflowStepPhaseRunning {
|
||||
steps[i].SubStepsStatus[j].Phase = v1alpha1.WorkflowStepPhaseSucceeded
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := p.KubeClient.Status().Patch(ctx, &run, client.Merge); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p pipelineRunServiceImpl) TerminatePipelineRun(ctx context.Context, meta apis.PipelineRunMeta) error {
|
||||
project := ctx.Value(&apis.CtxKeyProject).(*model.Project)
|
||||
run := v1alpha1.WorkflowRun{}
|
||||
if err := p.KubeClient.Get(ctx, types.NamespacedName{
|
||||
Namespace: project.GetNamespace(),
|
||||
Name: meta.PipelineRunName,
|
||||
}, &run); err != nil {
|
||||
return err
|
||||
}
|
||||
if run.Status.Terminated || run.Status.Finished {
|
||||
return bcode.ErrPipelineRunFinished
|
||||
}
|
||||
|
||||
// set the pipeline run terminated to true
|
||||
run.Status.Terminated = true
|
||||
// set the pipeline run suspend to false
|
||||
run.Status.Suspend = false
|
||||
steps := run.Status.Steps
|
||||
for i, step := range steps {
|
||||
switch step.Phase {
|
||||
case v1alpha1.WorkflowStepPhaseFailed:
|
||||
if step.Reason != wfTypes.StatusReasonFailedAfterRetries && step.Reason != wfTypes.StatusReasonTimeout {
|
||||
steps[i].Reason = wfTypes.StatusReasonTerminate
|
||||
}
|
||||
case v1alpha1.WorkflowStepPhaseRunning:
|
||||
steps[i].Phase = v1alpha1.WorkflowStepPhaseFailed
|
||||
steps[i].Reason = wfTypes.StatusReasonTerminate
|
||||
default:
|
||||
}
|
||||
for j, sub := range step.SubStepsStatus {
|
||||
switch sub.Phase {
|
||||
case v1alpha1.WorkflowStepPhaseFailed:
|
||||
if sub.Reason != wfTypes.StatusReasonFailedAfterRetries && sub.Reason != wfTypes.StatusReasonTimeout {
|
||||
steps[i].SubStepsStatus[j].Reason = wfTypes.StatusReasonTerminate
|
||||
}
|
||||
case v1alpha1.WorkflowStepPhaseRunning:
|
||||
steps[i].SubStepsStatus[j].Phase = v1alpha1.WorkflowStepPhaseFailed
|
||||
steps[i].SubStepsStatus[j].Reason = wfTypes.StatusReasonTerminate
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.KubeClient.Status().Patch(ctx, &run, client.Merge); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPipelineSpec(spec model.WorkflowSpec) error {
|
||||
if len(spec.Steps) == 0 {
|
||||
return bcode.ErrNoSteps
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,13 @@ var _ = Describe("Test pipeline service functions", func() {
|
||||
Expect(pipelines.Total).Should(Equal(1))
|
||||
Expect(len(pipelines.Pipelines)).Should(Equal(1))
|
||||
Expect(pipelines.Pipelines[0].Info).ShouldNot(BeNil())
|
||||
|
||||
pipelinesFilterByProject, err := pipelineService.ListPipelines(ctx, apisv1.ListPipelineRequest{
|
||||
Detailed: true,
|
||||
Projects: []string{"not-found"},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(pipelinesFilterByProject.Pipelines)).Should(Equal(0))
|
||||
})
|
||||
|
||||
It("get pipeline contexts", func() {
|
||||
|
||||
@@ -17,12 +17,14 @@ limitations under the License.
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
@@ -30,7 +32,9 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
apiutils "github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/auth"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
@@ -290,6 +294,10 @@ func (p *projectServiceImpl) DeleteProject(ctx context.Context, name string) err
|
||||
if err := p.Store.Delete(ctx, &model.Project{Name: name}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := managePrivilegesForProject(ctx, p.K8sClient, &model.Project{Name: name}, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -317,22 +325,30 @@ func (p *projectServiceImpl) CreateProject(ctx context.Context, req apisv1.Creat
|
||||
}
|
||||
}
|
||||
|
||||
if err := utils.CreateNamespace(ctx, p.K8sClient, req.Name); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
namespace := req.Namespace
|
||||
if namespace == "" {
|
||||
namespace = req.Name
|
||||
}
|
||||
createCtx := apiutils.WithProject(ctx, "")
|
||||
if err := utils.CreateNamespace(createCtx, p.K8sClient, namespace); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return nil, bcode.ErrProjectNamespaceFail
|
||||
}
|
||||
|
||||
newProject := &model.Project{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Alias: req.Alias,
|
||||
Owner: owner,
|
||||
Namespace: req.Name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
|
||||
if err := p.Store.Add(ctx, newProject); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := managePrivilegesForProject(createCtx, p.K8sClient, newProject, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.RbacService.SyncDefaultRoleAndUsersForProject(ctx, newProject); err != nil {
|
||||
klog.Errorf("fail to sync the default role and users for the project: %s", err.Error())
|
||||
}
|
||||
@@ -340,6 +356,22 @@ func (p *projectServiceImpl) CreateProject(ctx context.Context, req apisv1.Creat
|
||||
return ConvertProjectModel2Base(newProject, user), nil
|
||||
}
|
||||
|
||||
// managePrivilegesForProject grant or revoke privileges for project
|
||||
func managePrivilegesForProject(ctx context.Context, cli client.Client, project *model.Project, revoke bool) error {
|
||||
p := &auth.ApplicationPrivilege{Cluster: types.ClusterLocalName, Namespace: project.Namespace}
|
||||
identity := &auth.Identity{Groups: []string{apiutils.KubeVelaProjectGroupPrefix + project.Name}}
|
||||
writer := &bytes.Buffer{}
|
||||
f, msg := auth.GrantPrivileges, "GrantPrivileges"
|
||||
if revoke {
|
||||
f, msg = auth.RevokePrivileges, "RevokePrivileges"
|
||||
}
|
||||
if err := f(ctx, cli, []auth.PrivilegeDescription{p}, identity, writer); err != nil {
|
||||
return err
|
||||
}
|
||||
klog.Infof("%s: %s", msg, writer.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProject update project
|
||||
func (p *projectServiceImpl) UpdateProject(ctx context.Context, projectName string, req apisv1.UpdateProjectRequest) (*apisv1.ProjectBase, error) {
|
||||
project, err := p.GetProject(ctx, projectName)
|
||||
@@ -368,6 +400,9 @@ func (p *projectServiceImpl) UpdateProject(ctx context.Context, projectName stri
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := managePrivilegesForProject(ctx, p.K8sClient, project, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ConvertProjectModel2Base(project, user), nil
|
||||
}
|
||||
|
||||
@@ -505,7 +540,11 @@ func (p *projectServiceImpl) UpdateProjectUser(ctx context.Context, projectName
|
||||
|
||||
func (p *projectServiceImpl) ListTerraformProviders(ctx context.Context, projectName string) ([]*apisv1.TerraformProvider, error) {
|
||||
l := &terraformapi.ProviderList{}
|
||||
if err := p.K8sClient.List(ctx, l, client.InNamespace(types.ProviderNamespace)); err != nil {
|
||||
listCtx := apiutils.WithProject(ctx, "")
|
||||
if err := p.K8sClient.List(listCtx, l, client.InNamespace(types.ProviderNamespace)); err != nil {
|
||||
if meta.IsNoMatchError(err) {
|
||||
return []*apisv1.TerraformProvider{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var res []*apisv1.TerraformProvider
|
||||
|
||||
@@ -101,7 +101,7 @@ var defaultProjectPermissionTemplate = []*model.PermissionTemplate{
|
||||
Name: "pipeline-management",
|
||||
Alias: "Pipeline Management",
|
||||
Resources: []string{
|
||||
"project:{projectName}/pipeline:*",
|
||||
"project:{projectName}/pipeline:*/*",
|
||||
},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
|
||||
@@ -49,7 +49,7 @@ func InitServiceBean(c config.Config) []interface{} {
|
||||
pipelineService := NewPipelineService(c.WorkflowVersion)
|
||||
pipelineRunService := NewPipelineRunService()
|
||||
contextService := NewContextService()
|
||||
needInitData = []DataInit{clusterService, userService, rbacService, projectService, targetService, systemInfoService}
|
||||
needInitData = []DataInit{clusterService, userService, rbacService, projectService, targetService, systemInfoService, addonService}
|
||||
return []interface{}{
|
||||
clusterService, rbacService, projectService, envService, targetService, workflowService, oamApplicationService,
|
||||
velaQLService, definitionService, addonService, envBindingService, systemInfoService, helmService, userService,
|
||||
|
||||
@@ -104,7 +104,7 @@ func NewDatastore(cfg datastore.Config) (ds datastore.DataStore, err error) {
|
||||
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
|
||||
}
|
||||
case "kubeapi":
|
||||
ds, err = kubeapi.New(context.Background(), cfg)
|
||||
ds, err = kubeapi.New(context.Background(), cfg, k8sClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
|
||||
}
|
||||
|
||||
@@ -148,10 +148,11 @@ func (dt *targetServiceImpl) CreateTarget(ctx context.Context, req apisv1.Create
|
||||
if req.Cluster == nil {
|
||||
req.Cluster = &apisv1.ClusterTarget{ClusterName: multicluster.ClusterLocalName, Namespace: req.Name}
|
||||
}
|
||||
if err := repository.CreateTargetNamespace(ctx, dt.K8sClient, req.Cluster.ClusterName, req.Cluster.Namespace, req.Name); err != nil {
|
||||
createTargetCtx := utils.WithProject(ctx, "")
|
||||
if err := repository.CreateTargetNamespace(createTargetCtx, dt.K8sClient, req.Cluster.ClusterName, req.Cluster.Namespace, req.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := managePrivilegesForTarget(ctx, dt.K8sClient, &target, false); err != nil {
|
||||
if err := managePrivilegesForTarget(createTargetCtx, dt.K8sClient, &target, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := repository.CreateTarget(ctx, dt.Store, &target)
|
||||
@@ -167,7 +168,8 @@ func (dt *targetServiceImpl) UpdateTarget(ctx context.Context, target *model.Tar
|
||||
return nil, err
|
||||
}
|
||||
// Compatible with historical data, if the existing Target has not been authorized, perform an update action.
|
||||
if err := managePrivilegesForTarget(ctx, dt.K8sClient, targetModel, false); err != nil {
|
||||
updateCtx := utils.WithProject(ctx, "")
|
||||
if err := managePrivilegesForTarget(updateCtx, dt.K8sClient, targetModel, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dt.DetailTarget(ctx, targetModel)
|
||||
|
||||
@@ -18,8 +18,6 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// guaranteePolicyExist check the slice whether contain the target policy, if not put it in.
|
||||
@@ -51,29 +49,24 @@ func guaranteePolicyNotExist(c []string, policy string) ([]string, bool) {
|
||||
|
||||
// extractPolicyListAndProperty can extract policy from string-format properties, and return
|
||||
// map-format properties in order to further update operation.
|
||||
func extractPolicyListAndProperty(property string) ([]string, map[string]interface{}, error) {
|
||||
func extractPolicyListAndProperty(property map[string]interface{}) ([]string, map[string]interface{}, error) {
|
||||
if len(property) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
content := map[string]interface{}{}
|
||||
err := json.Unmarshal([]byte(property), &content)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
policies := content["policies"]
|
||||
policies := property["policies"]
|
||||
if policies == nil {
|
||||
return nil, content, nil
|
||||
return nil, property, nil
|
||||
}
|
||||
list, ok := policies.([]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("the policies incorrrect")
|
||||
return nil, nil, fmt.Errorf("the policies incorrect")
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return nil, content, nil
|
||||
return nil, property, nil
|
||||
}
|
||||
res := []string{}
|
||||
for _, i := range list {
|
||||
res = append(res, i.(string))
|
||||
}
|
||||
return res, content, nil
|
||||
return res, property, nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
@@ -209,7 +210,7 @@ func TestExtractPolicyListAndProperty(t *testing.T) {
|
||||
}{noError: false},
|
||||
},
|
||||
{
|
||||
input: ``,
|
||||
input: `{}`,
|
||||
res: struct {
|
||||
policies []string
|
||||
properties map[string]interface{}
|
||||
@@ -218,7 +219,15 @@ func TestExtractPolicyListAndProperty(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
policy, properties, err := extractPolicyListAndProperty(testCase.input)
|
||||
var in = map[string]interface{}{}
|
||||
err := json.Unmarshal([]byte(testCase.input), &in)
|
||||
if testCase.res.noError {
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.Equal(t, err != nil, true)
|
||||
continue
|
||||
}
|
||||
policy, properties, err := extractPolicyListAndProperty(in)
|
||||
if testCase.res.noError {
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
|
||||
@@ -18,20 +18,27 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
|
||||
"helm.sh/helm/v3/pkg/time"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
wfContext "github.com/kubevela/workflow/pkg/context"
|
||||
"github.com/kubevela/workflow/pkg/cue/model/value"
|
||||
wfTypes "github.com/kubevela/workflow/pkg/types"
|
||||
wfUtils "github.com/kubevela/workflow/pkg/utils"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
@@ -47,6 +54,12 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
// LogSourceResource Read the step logs from the pod stdout.
|
||||
const LogSourceResource = "Resource"
|
||||
|
||||
// LogSourceURL Read the step logs from the URL.
|
||||
const LogSourceURL = "URL"
|
||||
|
||||
// WorkflowService workflow manage api
|
||||
type WorkflowService interface {
|
||||
ListApplicationWorkflow(ctx context.Context, app *model.Application) ([]*apisv1.WorkflowBase, error)
|
||||
@@ -57,13 +70,19 @@ type WorkflowService interface {
|
||||
DeleteWorkflowByApp(ctx context.Context, app *model.Application) error
|
||||
CreateOrUpdateWorkflow(ctx context.Context, app *model.Application, req apisv1.CreateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error)
|
||||
UpdateWorkflow(ctx context.Context, workflow *model.Workflow, req apisv1.UpdateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error)
|
||||
CreateWorkflowRecord(ctx context.Context, appModel *model.Application, app *v1beta1.Application, workflow *model.Workflow) error
|
||||
|
||||
GetWorkflowRecord(ctx context.Context, workflow *model.Workflow, recordName string) (*model.WorkflowRecord, error)
|
||||
CreateWorkflowRecord(ctx context.Context, appModel *model.Application, app *v1beta1.Application, workflow *model.Workflow) (*model.WorkflowRecord, error)
|
||||
ListWorkflowRecords(ctx context.Context, workflow *model.Workflow, page, pageSize int) (*apisv1.ListWorkflowRecordsResponse, error)
|
||||
DetailWorkflowRecord(ctx context.Context, workflow *model.Workflow, recordName string) (*apisv1.DetailWorkflowRecordResponse, error)
|
||||
SyncWorkflowRecord(ctx context.Context) error
|
||||
ResumeRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error
|
||||
TerminateRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error
|
||||
RollbackRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName, revisionName string) error
|
||||
RollbackRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName, revisionName string) (*apisv1.WorkflowRecordBase, error)
|
||||
GetWorkflowRecordLog(ctx context.Context, record *model.WorkflowRecord, step string) (apisv1.GetPipelineRunLogResponse, error)
|
||||
GetWorkflowRecordOutput(ctx context.Context, workflow *model.Workflow, record *model.WorkflowRecord, stepName string) (apisv1.GetPipelineRunOutputResponse, error)
|
||||
GetWorkflowRecordInput(ctx context.Context, workflow *model.Workflow, record *model.WorkflowRecord, stepName string) (apisv1.GetPipelineRunInputResponse, error)
|
||||
|
||||
CountWorkflow(ctx context.Context, app *model.Application) int64
|
||||
}
|
||||
|
||||
@@ -75,6 +94,7 @@ func NewWorkflowService() WorkflowService {
|
||||
type workflowServiceImpl struct {
|
||||
Store datastore.DataStore `inject:"datastore"`
|
||||
KubeClient client.Client `inject:"kubeClient"`
|
||||
KubeConfig *rest.Config `inject:"kubeConfig"`
|
||||
Apply apply.Applicator `inject:"apply"`
|
||||
EnvService EnvService `inject:""`
|
||||
EnvBindingService EnvBindingService `inject:""`
|
||||
@@ -153,11 +173,19 @@ func (w *workflowServiceImpl) CreateOrUpdateWorkflow(ctx context.Context, app *m
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Mode == "" {
|
||||
req.Mode = string(workflowv1alpha1.WorkflowModeStep)
|
||||
}
|
||||
if req.SubMode == "" {
|
||||
req.Mode = string(workflowv1alpha1.WorkflowModeDAG)
|
||||
}
|
||||
if workflow != nil {
|
||||
workflow.Steps = modelSteps
|
||||
workflow.Alias = req.Alias
|
||||
workflow.Description = req.Description
|
||||
workflow.Default = req.Default
|
||||
workflow.Mode.Steps = workflowv1alpha1.WorkflowMode(req.Mode)
|
||||
workflow.Mode.SubSteps = workflowv1alpha1.WorkflowMode(req.SubMode)
|
||||
if err := w.Store.Put(ctx, workflow); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -171,6 +199,10 @@ func (w *workflowServiceImpl) CreateOrUpdateWorkflow(ctx context.Context, app *m
|
||||
Default: req.Default,
|
||||
EnvName: req.EnvName,
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
Mode: workflowv1alpha1.WorkflowExecuteMode{
|
||||
Steps: workflowv1alpha1.WorkflowMode(req.Mode),
|
||||
SubSteps: workflowv1alpha1.WorkflowMode(req.SubMode),
|
||||
},
|
||||
}
|
||||
klog.Infof("create workflow %s for app %s", pkgUtils.Sanitize(req.Name), pkgUtils.Sanitize(app.PrimaryKey()))
|
||||
if err := w.Store.Add(ctx, workflow); err != nil {
|
||||
@@ -186,6 +218,15 @@ func (w *workflowServiceImpl) UpdateWorkflow(ctx context.Context, workflow *mode
|
||||
return nil, err
|
||||
}
|
||||
workflow.Description = req.Description
|
||||
workflow.Alias = req.Alias
|
||||
if req.Mode == "" {
|
||||
req.Mode = string(workflowv1alpha1.WorkflowModeStep)
|
||||
}
|
||||
if req.SubMode == "" {
|
||||
req.Mode = string(workflowv1alpha1.WorkflowModeDAG)
|
||||
}
|
||||
workflow.Mode.Steps = workflowv1alpha1.WorkflowMode(req.Mode)
|
||||
workflow.Mode.SubSteps = workflowv1alpha1.WorkflowMode(req.SubMode)
|
||||
// It is allowed to set multiple workflows as default, and only one takes effect.
|
||||
if req.Default != nil {
|
||||
workflow.Default = req.Default
|
||||
@@ -249,7 +290,9 @@ func (w *workflowServiceImpl) ListWorkflowRecords(ctx context.Context, workflow
|
||||
AppPrimaryKey: workflow.AppPrimaryKey,
|
||||
WorkflowName: workflow.Name,
|
||||
}
|
||||
records, err := w.Store.List(ctx, &record, &datastore.ListOptions{Page: page, PageSize: pageSize})
|
||||
records, err := w.Store.List(ctx, &record, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{
|
||||
{Key: "createTime", Order: datastore.SortOrderAscending},
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -345,6 +388,7 @@ func (w *workflowServiceImpl) SyncWorkflowRecord(ctx context.Context) error {
|
||||
Namespace: record.Namespace,
|
||||
}, app); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
klog.Warningf("can't find the application %s/%s, set the record status to terminated", appName, record.Namespace)
|
||||
if err := w.setRecordToTerminated(ctx, record.AppPrimaryKey, record.Name); err != nil {
|
||||
klog.Errorf("failed to set the record status to terminated %s", err.Error())
|
||||
}
|
||||
@@ -358,14 +402,22 @@ func (w *workflowServiceImpl) SyncWorkflowRecord(ctx context.Context) error {
|
||||
continue
|
||||
}
|
||||
|
||||
// This means the application workflow has not run.
|
||||
if app.Generation > app.Status.ObservedGeneration {
|
||||
continue
|
||||
}
|
||||
|
||||
// there is a ":" in the default app revision
|
||||
recordName := strings.Replace(app.Status.Workflow.AppRevision, ":", "-", 1)
|
||||
|
||||
// try to sync the status from the running application
|
||||
if app.Annotations != nil && app.Status.Workflow != nil && recordName == record.Name {
|
||||
if err := w.syncWorkflowStatus(ctx, record.AppPrimaryKey, app, record.Name, app.Name); err != nil {
|
||||
if err := w.syncWorkflowStatus(ctx, record.AppPrimaryKey, app, record.Name, app.Name, nil); err != nil {
|
||||
klog.ErrorS(err, "failed to sync workflow status", "oam app name", appName, "workflow name", record.WorkflowName, "record name", record.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if record.Name == oam.GetPublishVersion(app) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -395,6 +447,7 @@ func (w *workflowServiceImpl) SyncWorkflowRecord(ctx context.Context) error {
|
||||
var appRevision v1beta1.ApplicationRevision
|
||||
if err := w.KubeClient.Get(ctx, types.NamespacedName{Namespace: app.Namespace, Name: revision.RevisionCRName}, &appRevision); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
klog.Warningf("can't find the application revision %s/%s, set the record status to terminated", revision.RevisionCRName, app.Namespace)
|
||||
if err := w.setRecordToTerminated(ctx, record.AppPrimaryKey, record.Name); err != nil {
|
||||
klog.Errorf("failed to set the record status to terminated %s", err.Error())
|
||||
}
|
||||
@@ -411,7 +464,13 @@ func (w *workflowServiceImpl) SyncWorkflowRecord(ctx context.Context) error {
|
||||
appRevision.Spec.Application.Status.Workflow.Terminated = true
|
||||
}
|
||||
}
|
||||
if err := w.syncWorkflowStatus(ctx, record.AppPrimaryKey, &appRevision.Spec.Application, record.Name, revision.RevisionCRName); err != nil {
|
||||
if err := w.syncWorkflowStatus(ctx,
|
||||
record.AppPrimaryKey,
|
||||
&appRevision.Spec.Application,
|
||||
record.Name,
|
||||
appRevision.Name,
|
||||
appRevision.Status.WorkflowContext,
|
||||
); err != nil {
|
||||
klog.ErrorS(err, "failed to sync workflow status", "oam app name", appName, "workflow name", record.WorkflowName, "record name", record.Name)
|
||||
continue
|
||||
}
|
||||
@@ -453,7 +512,12 @@ func (w *workflowServiceImpl) setRecordToTerminated(ctx context.Context, appPrim
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) syncWorkflowStatus(ctx context.Context, appPrimaryKey string, app *v1beta1.Application, recordName, source string) error {
|
||||
func (w *workflowServiceImpl) syncWorkflowStatus(ctx context.Context,
|
||||
appPrimaryKey string,
|
||||
app *v1beta1.Application,
|
||||
recordName,
|
||||
source string,
|
||||
workflowContext map[string]string) error {
|
||||
var record = &model.WorkflowRecord{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
Name: recordName,
|
||||
@@ -472,19 +536,28 @@ func (w *workflowServiceImpl) syncWorkflowStatus(ctx context.Context, appPrimary
|
||||
return err
|
||||
}
|
||||
|
||||
if workflowContext != nil {
|
||||
record.ContextValue = workflowContext
|
||||
}
|
||||
|
||||
if app.Status.Workflow != nil {
|
||||
if app.Status.Workflow.AppRevision != record.Name {
|
||||
klog.Warningf("the app(%s) revision is not match the record(%s), try next time..", app.Name, record.Name)
|
||||
return nil
|
||||
}
|
||||
status := app.Status.Workflow
|
||||
summaryStatus := model.RevisionStatusRunning
|
||||
switch {
|
||||
case status.Phase == workflowv1alpha1.WorkflowStateFailed:
|
||||
summaryStatus = model.RevisionStatusFail
|
||||
case status.Finished:
|
||||
summaryStatus = model.RevisionStatusComplete
|
||||
case status.Terminated:
|
||||
summaryStatus = model.RevisionStatusTerminated
|
||||
record.Status = string(status.Phase)
|
||||
record.Message = status.Message
|
||||
record.Mode = status.Mode
|
||||
|
||||
if cb := app.Status.Workflow.ContextBackend; cb != nil && workflowContext == nil {
|
||||
var cm corev1.ConfigMap
|
||||
if err := w.KubeClient.Get(ctx, types.NamespacedName{Namespace: cb.Namespace, Name: cb.Name}, &cm); err != nil {
|
||||
klog.Error(err, "failed to load the context values", "Application", app.Name)
|
||||
}
|
||||
record.ContextValue = cm.Data
|
||||
}
|
||||
|
||||
record.Status = summaryStatus
|
||||
stepStatus := make(map[string]*model.WorkflowStepStatus, len(status.Steps))
|
||||
stepAlias := make(map[string]string)
|
||||
for _, step := range record.Steps {
|
||||
@@ -507,13 +580,24 @@ func (w *workflowServiceImpl) syncWorkflowStatus(ctx context.Context, appPrimary
|
||||
record.Steps[i] = *stepStatus[step.Name]
|
||||
}
|
||||
}
|
||||
record.Finished = strconv.FormatBool(status.Finished)
|
||||
|
||||
// the auto generated workflow steps should be sync
|
||||
if (len(record.Steps) == 0) && len(status.Steps) > 0 {
|
||||
for k := range stepStatus {
|
||||
record.Steps = append(record.Steps, *stepStatus[k])
|
||||
}
|
||||
}
|
||||
|
||||
record.Finished = strconv.FormatBool(status.Finished)
|
||||
record.EndTime = status.EndTime.Time
|
||||
if err := w.Store.Put(ctx, record); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
revision.Status = summaryStatus
|
||||
revision.Status = generateRevisionStatus(status.Phase)
|
||||
if app.Status.LatestRevision != nil {
|
||||
revision.RevisionCRName = app.Status.LatestRevision.Name
|
||||
}
|
||||
if err := w.Store.Put(ctx, revision); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -526,15 +610,28 @@ func (w *workflowServiceImpl) syncWorkflowStatus(ctx context.Context, appPrimary
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) CreateWorkflowRecord(ctx context.Context, appModel *model.Application, app *v1beta1.Application, workflow *model.Workflow) error {
|
||||
func generateRevisionStatus(phase workflowv1alpha1.WorkflowRunPhase) string {
|
||||
summaryStatus := model.RevisionStatusRunning
|
||||
switch {
|
||||
case phase == workflowv1alpha1.WorkflowStateFailed:
|
||||
summaryStatus = model.RevisionStatusFail
|
||||
case phase == workflowv1alpha1.WorkflowStateSucceeded:
|
||||
summaryStatus = model.RevisionStatusComplete
|
||||
case phase == workflowv1alpha1.WorkflowStateTerminated:
|
||||
summaryStatus = model.RevisionStatusTerminated
|
||||
}
|
||||
return summaryStatus
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) CreateWorkflowRecord(ctx context.Context, appModel *model.Application, app *v1beta1.Application, workflow *model.Workflow) (*model.WorkflowRecord, error) {
|
||||
if app.Annotations == nil {
|
||||
return fmt.Errorf("empty annotations in application")
|
||||
return nil, fmt.Errorf("empty annotations in application")
|
||||
}
|
||||
if app.Annotations[oam.AnnotationPublishVersion] == "" {
|
||||
return fmt.Errorf("failed to get record version from application")
|
||||
return nil, fmt.Errorf("failed to get record version from application")
|
||||
}
|
||||
if app.Annotations[oam.AnnotationDeployVersion] == "" {
|
||||
return fmt.Errorf("failed to get deploy version from application")
|
||||
return nil, fmt.Errorf("failed to get deploy version from application")
|
||||
}
|
||||
steps := make([]model.WorkflowStepStatus, len(workflow.Steps))
|
||||
for i, step := range workflow.Steps {
|
||||
@@ -555,7 +652,7 @@ func (w *workflowServiceImpl) CreateWorkflowRecord(ctx context.Context, appModel
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.Store.Add(ctx, &model.WorkflowRecord{
|
||||
workflowRecord := &model.WorkflowRecord{
|
||||
WorkflowName: workflow.Name,
|
||||
WorkflowAlias: workflow.Alias,
|
||||
AppPrimaryKey: appModel.PrimaryKey(),
|
||||
@@ -565,16 +662,18 @@ func (w *workflowServiceImpl) CreateWorkflowRecord(ctx context.Context, appModel
|
||||
Finished: "false",
|
||||
StartTime: time.Now().Time,
|
||||
Steps: steps,
|
||||
Status: model.RevisionStatusRunning,
|
||||
}); err != nil {
|
||||
return err
|
||||
Status: string(workflowv1alpha1.WorkflowStateInitializing),
|
||||
}
|
||||
|
||||
if err := w.Store.Add(ctx, workflowRecord); err != nil {
|
||||
return nil, fmt.Errorf("failed to create the workflow record %s: %w", workflowRecord.Name, err)
|
||||
}
|
||||
|
||||
if err := resetRevisionsAndRecords(ctx, w.Store, appModel.PrimaryKey(), workflow.Name, app.Annotations[oam.AnnotationDeployVersion], app.Annotations[oam.AnnotationPublishVersion]); err != nil {
|
||||
return err
|
||||
return workflowRecord, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return workflowRecord, nil
|
||||
}
|
||||
|
||||
func resetRevisionsAndRecords(ctx context.Context, ds datastore.DataStore, appName, workflowName, skipRevision, skipRecord string) error {
|
||||
@@ -642,6 +741,21 @@ func (w *workflowServiceImpl) CountWorkflow(ctx context.Context, app *model.Appl
|
||||
return count
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) GetWorkflowRecord(ctx context.Context, workflow *model.Workflow, recordName string) (*model.WorkflowRecord, error) {
|
||||
var record = &model.WorkflowRecord{
|
||||
WorkflowName: workflow.Name,
|
||||
Name: recordName,
|
||||
}
|
||||
res, err := w.Store.List(ctx, record, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return nil, bcode.ErrWorkflowRecordNotExist
|
||||
}
|
||||
return res[0].(*model.WorkflowRecord), nil
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) ResumeRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error {
|
||||
oamApp, err := w.checkRecordRunning(ctx, appModel, workflow.EnvName)
|
||||
if err != nil {
|
||||
@@ -652,7 +766,7 @@ func (w *workflowServiceImpl) ResumeRecord(ctx context.Context, appModel *model.
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.syncWorkflowStatus(ctx, appModel.PrimaryKey(), oamApp, recordName, oamApp.Name); err != nil {
|
||||
if err := w.syncWorkflowStatus(ctx, appModel.PrimaryKey(), oamApp, recordName, oamApp.Name, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -667,7 +781,7 @@ func (w *workflowServiceImpl) TerminateRecord(ctx context.Context, appModel *mod
|
||||
if err := TerminateWorkflow(ctx, w.KubeClient, oamApp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.syncWorkflowStatus(ctx, appModel.PrimaryKey(), oamApp, recordName, oamApp.Name); err != nil {
|
||||
if err := w.syncWorkflowStatus(ctx, appModel.PrimaryKey(), oamApp, recordName, oamApp.Name, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -732,7 +846,7 @@ func TerminateWorkflow(ctx context.Context, kubecli client.Client, app *v1beta1.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName, revisionVersion string) error {
|
||||
func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName, revisionVersion string) (*apisv1.WorkflowRecordBase, error) {
|
||||
if revisionVersion == "" {
|
||||
// find the latest complete revision version
|
||||
var revision = model.ApplicationRevision{
|
||||
@@ -747,13 +861,13 @@ func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if len(revisions) == 0 {
|
||||
return bcode.ErrApplicationNoReadyRevision
|
||||
return nil, bcode.ErrApplicationNoReadyRevision
|
||||
}
|
||||
revisionVersion = revisions[0].Index()["version"]
|
||||
klog.Infof("select lastest complete revision %s", revisions[0].Index()["version"])
|
||||
revisionVersion = pkgUtils.ToString(revisions[0].Index()["version"])
|
||||
klog.Infof("select lastest complete revision %s", revisionVersion)
|
||||
}
|
||||
|
||||
var record = &model.WorkflowRecord{
|
||||
@@ -761,12 +875,12 @@ func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
Name: recordName,
|
||||
}
|
||||
if err := w.Store.Get(ctx, record); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oamApp, err := w.checkRecordRunning(ctx, appModel, workflow.EnvName)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var originalRevision = &model.ApplicationRevision{
|
||||
@@ -774,7 +888,7 @@ func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
Version: record.RevisionPrimaryKey,
|
||||
}
|
||||
if err := w.Store.Get(ctx, originalRevision); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rollbackRevision = &model.ApplicationRevision{
|
||||
@@ -782,7 +896,7 @@ func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
Version: revisionVersion,
|
||||
}
|
||||
if err := w.Store.Get(ctx, rollbackRevision); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// update the original revision status to rollback
|
||||
@@ -790,12 +904,12 @@ func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
originalRevision.RollbackVersion = revisionVersion
|
||||
originalRevision.UpdateTime = time.Now().Time
|
||||
if err := w.Store.Put(ctx, originalRevision); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rollBackApp := &v1beta1.Application{}
|
||||
if err := yaml.Unmarshal([]byte(rollbackRevision.ApplyAppConfig), rollBackApp); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
// replace the application spec
|
||||
oamApp.Spec.Components = rollBackApp.Spec.Components
|
||||
@@ -807,8 +921,9 @@ func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
oamApp.Annotations[oam.AnnotationDeployVersion] = revisionVersion
|
||||
oamApp.Annotations[oam.AnnotationPublishVersion] = newRecordName
|
||||
// create a new workflow record
|
||||
if err := w.CreateWorkflowRecord(ctx, appModel, oamApp, workflow); err != nil {
|
||||
return err
|
||||
newRecord, err := w.CreateWorkflowRecord(ctx, appModel, oamApp, workflow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := w.Apply.Apply(ctx, oamApp); err != nil {
|
||||
@@ -816,10 +931,10 @@ func (w *workflowServiceImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
if err := w.Store.Delete(ctx, &model.WorkflowRecord{Name: newRecordName}); err != nil {
|
||||
klog.Error(err, "failed to delete record", newRecordName)
|
||||
}
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return &assembler.ConvertFromRecordModel(newRecord).WorkflowRecordBase, nil
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) checkRecordRunning(ctx context.Context, appModel *model.Application, envName string) (*v1beta1.Application, error) {
|
||||
@@ -846,3 +961,230 @@ func (w *workflowServiceImpl) checkRecordRunning(ctx context.Context, appModel *
|
||||
oamApp.SetGroupVersionKind(v1beta1.ApplicationKindVersionKind)
|
||||
return oamApp, nil
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) GetWorkflowRecordLog(ctx context.Context, record *model.WorkflowRecord, step string) (apisv1.GetPipelineRunLogResponse, error) {
|
||||
if len(record.ContextValue) == 0 {
|
||||
return apisv1.GetPipelineRunLogResponse{}, nil
|
||||
}
|
||||
logConfig, err := getLogConfigFromStep(record.ContextValue, step)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no log config found") {
|
||||
return apisv1.GetPipelineRunLogResponse{
|
||||
StepBase: getWorkflowStepBase(*record, step),
|
||||
Log: "",
|
||||
}, nil
|
||||
}
|
||||
return apisv1.GetPipelineRunLogResponse{}, err
|
||||
}
|
||||
var logs string
|
||||
var source string
|
||||
if logConfig.Source != nil {
|
||||
if len(logConfig.Source.Resources) > 0 {
|
||||
source = LogSourceResource
|
||||
logs, err = getResourceLogs(ctx, w.KubeConfig, w.KubeClient, logConfig.Source.Resources, nil)
|
||||
if err != nil {
|
||||
return apisv1.GetPipelineRunLogResponse{LogSource: source}, err
|
||||
}
|
||||
}
|
||||
if logConfig.Source.URL != "" {
|
||||
source = LogSourceURL
|
||||
var logsBuilder strings.Builder
|
||||
readCloser, err := wfUtils.GetLogsFromURL(ctx, logConfig.Source.URL)
|
||||
if err != nil {
|
||||
klog.Errorf("get logs from url %s failed: %v", logConfig.Source.URL, err)
|
||||
return apisv1.GetPipelineRunLogResponse{LogSource: source}, bcode.ErrReadSourceLog
|
||||
}
|
||||
//nolint:errcheck
|
||||
defer readCloser.Close()
|
||||
if _, err := io.Copy(&logsBuilder, readCloser); err != nil {
|
||||
klog.Errorf("copy logs from url %s failed: %v", logConfig.Source.URL, err)
|
||||
return apisv1.GetPipelineRunLogResponse{LogSource: source}, bcode.ErrReadSourceLog
|
||||
}
|
||||
logs = logsBuilder.String()
|
||||
}
|
||||
}
|
||||
return apisv1.GetPipelineRunLogResponse{
|
||||
LogSource: source,
|
||||
StepBase: getWorkflowStepBase(*record, step),
|
||||
Log: logs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) GetWorkflowRecordOutput(ctx context.Context, workflow *model.Workflow, record *model.WorkflowRecord, stepName string) (apisv1.GetPipelineRunOutputResponse, error) {
|
||||
outputsSpec := make(map[string]workflowv1alpha1.StepOutputs)
|
||||
stepOutputs := make([]apisv1.StepOutputBase, 0)
|
||||
|
||||
for _, step := range workflow.Steps {
|
||||
if step.Outputs != nil {
|
||||
outputsSpec[step.Name] = step.Outputs
|
||||
}
|
||||
for _, sub := range step.SubSteps {
|
||||
if sub.Outputs != nil {
|
||||
outputsSpec[sub.Name] = sub.Outputs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctxBackend := record.ContextValue
|
||||
if ctxBackend == nil {
|
||||
return apisv1.GetPipelineRunOutputResponse{}, nil
|
||||
}
|
||||
v, err := getDataFromContext(ctxBackend)
|
||||
if err != nil {
|
||||
klog.Errorf("get data from context backend failed: %v", err)
|
||||
return apisv1.GetPipelineRunOutputResponse{}, bcode.ErrGetContextBackendData
|
||||
}
|
||||
for _, s := range record.Steps {
|
||||
if stepName != "" && s.Name != stepName {
|
||||
subStepStatus, ok := haveSubStep(s, stepName)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
subVars := getStepOutputs(convertWorkflowStep(*subStepStatus), outputsSpec, v)
|
||||
stepOutputs = append(stepOutputs, subVars)
|
||||
break
|
||||
}
|
||||
stepOutputs = append(stepOutputs, getStepOutputs(convertWorkflowStep(s.StepStatus), outputsSpec, v))
|
||||
for _, sub := range s.SubStepsStatus {
|
||||
stepOutputs = append(stepOutputs, getStepOutputs(convertWorkflowStep(sub), outputsSpec, v))
|
||||
}
|
||||
if stepName != "" && s.Name == stepName {
|
||||
// already found the step
|
||||
break
|
||||
}
|
||||
}
|
||||
return apisv1.GetPipelineRunOutputResponse{StepOutputs: stepOutputs}, nil
|
||||
}
|
||||
|
||||
func (w *workflowServiceImpl) GetWorkflowRecordInput(ctx context.Context, workflow *model.Workflow, record *model.WorkflowRecord, stepName string) (apisv1.GetPipelineRunInputResponse, error) {
|
||||
// valueFromStep know which step the value came from
|
||||
valueFromStep := make(map[string]string)
|
||||
inputsSpec := make(map[string]workflowv1alpha1.StepInputs)
|
||||
stepInputs := make([]apisv1.StepInputBase, 0)
|
||||
|
||||
for _, step := range workflow.Steps {
|
||||
if step.Inputs != nil {
|
||||
inputsSpec[step.Name] = step.Inputs
|
||||
}
|
||||
if step.Outputs != nil {
|
||||
for _, o := range step.Outputs {
|
||||
valueFromStep[o.Name] = step.Name
|
||||
}
|
||||
}
|
||||
for _, sub := range step.SubSteps {
|
||||
if sub.Inputs != nil {
|
||||
inputsSpec[sub.Name] = sub.Inputs
|
||||
}
|
||||
if sub.Outputs != nil {
|
||||
for _, o := range sub.Outputs {
|
||||
valueFromStep[o.Name] = sub.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctxBackend := record.ContextValue
|
||||
if ctxBackend == nil {
|
||||
return apisv1.GetPipelineRunInputResponse{}, nil
|
||||
}
|
||||
v, err := getDataFromContext(ctxBackend)
|
||||
if err != nil {
|
||||
klog.Errorf("get data from context backend failed: %v", err)
|
||||
return apisv1.GetPipelineRunInputResponse{}, bcode.ErrGetContextBackendData
|
||||
}
|
||||
for _, s := range record.Steps {
|
||||
if stepName != "" && s.Name != stepName {
|
||||
subStepStatus, ok := haveSubStep(s, stepName)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
subVars := getStepInputs(convertWorkflowStep(*subStepStatus), inputsSpec, v, valueFromStep)
|
||||
stepInputs = append(stepInputs, subVars)
|
||||
break
|
||||
}
|
||||
stepInputs = append(stepInputs, getStepInputs(convertWorkflowStep(s.StepStatus), inputsSpec, v, valueFromStep))
|
||||
for _, sub := range s.SubStepsStatus {
|
||||
stepInputs = append(stepInputs, getStepInputs(convertWorkflowStep(sub), inputsSpec, v, valueFromStep))
|
||||
}
|
||||
if stepName != "" && s.Name == stepName {
|
||||
// already found the step
|
||||
break
|
||||
}
|
||||
}
|
||||
return apisv1.GetPipelineRunInputResponse{StepInputs: stepInputs}, nil
|
||||
}
|
||||
|
||||
func getWorkflowStepBase(record model.WorkflowRecord, step string) apisv1.StepBase {
|
||||
for _, s := range record.Steps {
|
||||
if s.Name == step {
|
||||
return apisv1.StepBase{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Type: s.Type,
|
||||
Phase: string(s.Phase),
|
||||
}
|
||||
}
|
||||
}
|
||||
return apisv1.StepBase{}
|
||||
}
|
||||
|
||||
func getLogConfigFromStep(ctxValue map[string]string, step string) (*wfTypes.LogConfig, error) {
|
||||
wc := wfContext.WorkflowContext{}
|
||||
if err := wc.LoadFromConfigMap(corev1.ConfigMap{
|
||||
Data: ctxValue,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := make(map[string]wfTypes.LogConfig)
|
||||
c := wc.GetMutableValue(wfTypes.ContextKeyLogConfig)
|
||||
if c == "" {
|
||||
return nil, fmt.Errorf("no log config found")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(c), &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stepConfig, ok := config[step]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no log config found for step %s", step)
|
||||
}
|
||||
return &stepConfig, nil
|
||||
}
|
||||
|
||||
func getDataFromContext(ctxValue map[string]string) (*value.Value, error) {
|
||||
wc := wfContext.WorkflowContext{}
|
||||
if err := wc.LoadFromConfigMap(corev1.ConfigMap{
|
||||
Data: ctxValue,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := wc.GetVar()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v.Error() != nil {
|
||||
return nil, v.Error()
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func haveSubStep(step model.WorkflowStepStatus, subStep string) (*model.StepStatus, bool) {
|
||||
for _, s := range step.SubStepsStatus {
|
||||
if s.Name == subStep {
|
||||
return &s, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func convertWorkflowStep(stepStatus model.StepStatus) workflowv1alpha1.StepStatus {
|
||||
return workflowv1alpha1.StepStatus{
|
||||
Name: stepStatus.Name,
|
||||
ID: stepStatus.ID,
|
||||
Type: stepStatus.Type,
|
||||
Phase: stepStatus.Phase,
|
||||
Message: stepStatus.Message,
|
||||
Reason: stepStatus.Reason,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
for i := 0; i < 3; i++ {
|
||||
app.Annotations[oam.AnnotationPublishVersion] = fmt.Sprintf("list-workflow-name-%d", i)
|
||||
app.Status.Workflow.AppRevision = fmt.Sprintf("list-workflow-name-%d", i)
|
||||
err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -200,7 +200,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
Name: appName,
|
||||
}, "test-workflow-2")
|
||||
Expect(err).Should(BeNil())
|
||||
err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -238,7 +238,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
Name: appName,
|
||||
}, "test-workflow-2")
|
||||
Expect(err).Should(BeNil())
|
||||
err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -259,6 +259,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
app.Status.Workflow.Finished = true
|
||||
err = workflowService.KubeClient.Create(ctx, app.DeepCopy())
|
||||
Expect(err).Should(BeNil())
|
||||
app.Status.ObservedGeneration = 1
|
||||
err = workflowService.KubeClient.Status().Patch(ctx, app, client.Merge)
|
||||
Expect(err).Should(BeNil())
|
||||
err = workflowService.SyncWorkflowRecord(ctx)
|
||||
@@ -271,7 +272,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
By("check the record")
|
||||
record, err := workflowService.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-233")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(record.Status).Should(Equal(model.RevisionStatusFail))
|
||||
Expect(record.Status).Should(Equal(string(workflowv1alpha1.WorkflowStateFailed)))
|
||||
Expect(record.Steps[0].Alias).Should(Equal("step-alias-1"))
|
||||
Expect(record.Steps[0].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseSucceeded))
|
||||
Expect(record.Steps[1].Alias).Should(Equal("step-alias-2"))
|
||||
@@ -291,7 +292,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-111"
|
||||
app.Status.Workflow.AppRevision = "test-workflow-2-111"
|
||||
app.Annotations[oam.AnnotationDeployVersion] = "1111"
|
||||
err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -328,6 +329,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
err = workflowService.KubeClient.Create(ctx, appRevision)
|
||||
Expect(err).Should(BeNil())
|
||||
appRevision.Status.Workflow = appWithRevision.Status.Workflow
|
||||
appRevision.Status.Workflow.AppRevision = app.Annotations[oam.AnnotationPublishVersion]
|
||||
err = workflowService.KubeClient.Status().Update(ctx, appRevision)
|
||||
Expect(err).Should(BeNil())
|
||||
err = workflowService.SyncWorkflowRecord(ctx)
|
||||
@@ -336,7 +338,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
By("check the record")
|
||||
anotherRecord, err := workflowService.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-111")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(anotherRecord.Status).Should(Equal(model.RevisionStatusFail))
|
||||
Expect(anotherRecord.Status).Should(Equal(string(workflowv1alpha1.WorkflowStepPhaseFailed)))
|
||||
|
||||
By("check the application revision")
|
||||
err = workflowService.Store.Get(ctx, anotherRevision)
|
||||
@@ -358,7 +360,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
app, err := createTestSuspendApp(ctx, "record-app", "default", "revision-123", "test-workflow", "test-record-3", workflowService.KubeClient)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowService.CreateWorkflowRecord(ctx, &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(ctx, &model.Application{
|
||||
Name: "record-app",
|
||||
}, app, &model.Workflow{Name: "test-workflow"})
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -370,7 +372,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
}
|
||||
err = workflowService.Store.Get(ctx, record)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(record.Status).Should(Equal(model.RevisionStatusRunning))
|
||||
Expect(record.Status).Should(Equal(string(workflowv1alpha1.WorkflowStateInitializing)))
|
||||
})
|
||||
|
||||
It("Test ResumeRecord function", func() {
|
||||
@@ -396,7 +398,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
app, err := createTestSuspendApp(ctx, appName, "resume", "revision-resume1", ResumeWorkflow, "workflow-resume-1", workflowService.KubeClient)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, app, &model.Workflow{Name: ResumeWorkflow})
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -416,7 +418,8 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
|
||||
record, err := workflowService.DetailWorkflowRecord(ctx, &model.Workflow{Name: ResumeWorkflow, AppPrimaryKey: appName}, "workflow-resume-1")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(record.Status).Should(Equal(model.RevisionStatusRunning))
|
||||
Expect(len(record.Steps)).Should(Equal(1))
|
||||
Expect(record.Steps[0].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseSucceeded))
|
||||
})
|
||||
|
||||
It("Test TerminateRecord function", func() {
|
||||
@@ -442,7 +445,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
app, err := createTestSuspendApp(ctx, appName, "terminate", "revision-terminate1", workflow.Name, "test-workflow-2-1", workflowService.KubeClient)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -461,7 +464,8 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
|
||||
record, err := workflowService.DetailWorkflowRecord(ctx, workflow, "test-workflow-2-1")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(record.Status).Should(Equal(model.RevisionStatusTerminated))
|
||||
Expect(len(record.Steps)).Should(Equal(1))
|
||||
Expect(record.Steps[0].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseFailed))
|
||||
})
|
||||
|
||||
It("Test RollbackRecord function", func() {
|
||||
@@ -486,7 +490,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
app, err := createTestSuspendApp(ctx, appName, "rollback", "revision-rollback1", workflow.Name, "test-workflow-2-2", workflowService.KubeClient)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -509,7 +513,7 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowService.RollbackRecord(ctx, &model.Application{
|
||||
_, err = workflowService.RollbackRecord(ctx, &model.Application{
|
||||
Name: appName,
|
||||
}, workflow, "test-workflow-2-2", "revision-rollback0")
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -525,12 +529,12 @@ var _ = Describe("Test workflow service functions", func() {
|
||||
By("rollback application without revision version")
|
||||
app.Annotations[oam.AnnotationPublishVersion] = "workflow-rollback-2"
|
||||
app.Status.Workflow.AppRevision = "workflow-rollback-2"
|
||||
err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
_, err = workflowService.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowService.RollbackRecord(ctx, &model.Application{
|
||||
_, err = workflowService.RollbackRecord(ctx, &model.Application{
|
||||
Name: appName,
|
||||
}, workflow, "workflow-rollback-2", "")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -89,7 +89,7 @@ func NewDatastore(cfg datastore.Config) (ds datastore.DataStore, err error) {
|
||||
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
|
||||
}
|
||||
case "kubeapi":
|
||||
ds, err = kubeapi.New(context.Background(), cfg)
|
||||
ds, err = kubeapi.New(context.Background(), cfg, k8sClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
client2 "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"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/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
@@ -56,8 +55,9 @@ var waitBackOff = wait.Backoff{
|
||||
|
||||
// InfoCalculateCronJob is the cronJob to calculate the system info store in db
|
||||
type InfoCalculateCronJob struct {
|
||||
Store datastore.DataStore `inject:"datastore"`
|
||||
cron *cron.Cron
|
||||
KubeClient client.Client `inject:"kubeClient"`
|
||||
Store datastore.DataStore `inject:"datastore"`
|
||||
cron *cron.Cron
|
||||
}
|
||||
|
||||
// Start start the worker
|
||||
@@ -230,12 +230,8 @@ func (i InfoCalculateCronJob) calculateAppInfo(ctx context.Context) (int, []stri
|
||||
}
|
||||
|
||||
func (i InfoCalculateCronJob) calculateAddonInfo(ctx context.Context) (map[string]string, error) {
|
||||
client, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apps := &v1beta1.ApplicationList{}
|
||||
if err := client.List(ctx, apps, client2.InNamespace(types.DefaultKubeVelaNS), client2.HasLabels{oam.LabelAddonName}); err != nil {
|
||||
if err := i.KubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), client.HasLabels{oam.LabelAddonName}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := map[string]string{}
|
||||
@@ -257,11 +253,7 @@ func (i InfoCalculateCronJob) calculateAddonInfo(ctx context.Context) (map[strin
|
||||
}
|
||||
|
||||
func (i InfoCalculateCronJob) calculateClusterInfo(ctx context.Context) (int, error) {
|
||||
client, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cs, err := multicluster.ListVirtualClusters(ctx, client)
|
||||
cs, err := multicluster.ListVirtualClusters(ctx, i.KubeClient)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -83,7 +83,8 @@ var _ = Describe("Test calculate cronJob", func() {
|
||||
testProject = "test-cronjob-project"
|
||||
mockDataInDs()
|
||||
i = InfoCalculateCronJob{
|
||||
Store: ds,
|
||||
Store: ds,
|
||||
KubeClient: k8sClient,
|
||||
}
|
||||
systemInfo := model.SystemInfo{InstallID: "test-id", EnableCollection: true}
|
||||
Expect(ds.Add(ctx, &systemInfo)).Should(SatisfyAny(BeNil(), DataExistMatcher{}))
|
||||
|
||||
@@ -19,7 +19,6 @@ package sync
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
@@ -31,8 +30,8 @@ import (
|
||||
)
|
||||
|
||||
type cached struct {
|
||||
generation int64
|
||||
targets int64
|
||||
revision string
|
||||
targets int64
|
||||
}
|
||||
|
||||
// initCache will initialize the cache
|
||||
@@ -49,8 +48,8 @@ func (c *CR2UX) initCache(ctx context.Context) error {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
gen, ok := app.Labels[model.LabelSyncGeneration]
|
||||
if !ok || gen == "" {
|
||||
revision, ok := app.Labels[model.LabelSyncRevision]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
namespace := app.Labels[model.LabelSyncNamespace]
|
||||
@@ -58,10 +57,9 @@ func (c *CR2UX) initCache(ctx context.Context) error {
|
||||
if strings.HasSuffix(app.Name, namespace) {
|
||||
key = app.Name
|
||||
}
|
||||
generation, _ := strconv.ParseInt(gen, 10, 64)
|
||||
|
||||
// we should check targets if we synced from app status
|
||||
c.syncCache(key, generation, 0)
|
||||
c.syncCache(key, revision, 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -79,8 +77,10 @@ func (c *CR2UX) shouldSync(ctx context.Context, targetApp *v1beta1.Application,
|
||||
}
|
||||
|
||||
// if no LabelSourceOfTruth label, it means the app is existing ones, check the existing labels and annotations
|
||||
if _, appName := targetApp.Annotations[oam.AnnotationAppName]; appName {
|
||||
return false
|
||||
if targetApp.Annotations != nil {
|
||||
if _, exist := targetApp.Annotations[oam.AnnotationAppName]; exist {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
key := formatAppComposedName(targetApp.Name, targetApp.Namespace)
|
||||
@@ -91,16 +91,15 @@ func (c *CR2UX) shouldSync(ctx context.Context, targetApp *v1beta1.Application,
|
||||
_, _, err := c.getApp(ctx, targetApp.Name, targetApp.Namespace)
|
||||
if del || err != nil {
|
||||
c.cache.Delete(key)
|
||||
} else if cd.generation == targetApp.Generation {
|
||||
klog.Infof("app %s/%s with generation(%v) hasn't updated, ignore the sync event..", targetApp.Name, targetApp.Namespace, targetApp.Generation)
|
||||
} else if cd.revision == getRevision(*targetApp) {
|
||||
klog.V(5).Infof("app %s/%s with resource revision(%v) hasn't updated, ignore the sync event..", targetApp.Name, targetApp.Namespace, targetApp.ResourceVersion)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *CR2UX) syncCache(key string, generation, targets int64) {
|
||||
func (c *CR2UX) syncCache(key string, revision string, targets int64) {
|
||||
// update cache
|
||||
c.cache.Store(key, &cached{generation: generation, targets: targets})
|
||||
c.cache.Store(key, &cached{revision: revision, targets: targets})
|
||||
}
|
||||
|
||||
@@ -68,33 +68,36 @@ var _ = Describe("Test Cache", func() {
|
||||
app1 := &v1beta1.Application{}
|
||||
app1.Name = "app1"
|
||||
app1.Namespace = "app1-ns"
|
||||
app1.Generation = 1
|
||||
Expect(cr2ux.shouldSync(ctx, app1, false)).Should(BeEquivalentTo(true))
|
||||
|
||||
app2 := &v1beta1.Application{}
|
||||
app2.Name = "app2"
|
||||
app2.Namespace = "app2-ns"
|
||||
app2.Generation = 1
|
||||
app2.Status.LatestRevision = &common.Revision{Name: "v1"}
|
||||
|
||||
Expect(cr2ux.shouldSync(ctx, app2, false)).Should(BeEquivalentTo(true))
|
||||
|
||||
// Only need to sync once.
|
||||
cr2ux.syncCache(formatAppComposedName(app2.Name, app2.Namespace), "v1", 1)
|
||||
Expect(cr2ux.shouldSync(ctx, app2, false)).Should(BeEquivalentTo(false))
|
||||
|
||||
app3 := &v1beta1.Application{}
|
||||
app3.Name = "app3"
|
||||
app3.Namespace = "app3-ns"
|
||||
app3.Generation = 3
|
||||
app3.ResourceVersion = "3"
|
||||
app3.Labels = map[string]string{
|
||||
model.LabelSyncGeneration: "1",
|
||||
model.LabelSyncNamespace: "app3-ns",
|
||||
model.LabelSourceOfTruth: model.FromUX,
|
||||
model.LabelSourceOfTruth: model.FromUX,
|
||||
}
|
||||
|
||||
Expect(cr2ux.shouldSync(ctx, app3, false)).Should(BeEquivalentTo(false))
|
||||
|
||||
Expect(ds.Put(ctx, &model.Application{Name: "app1", Labels: map[string]string{
|
||||
model.LabelSyncGeneration: "1",
|
||||
model.LabelSyncNamespace: "app1-ns",
|
||||
model.LabelSyncRevision: "v1",
|
||||
model.LabelSyncNamespace: "app1-ns",
|
||||
}})).Should(BeNil())
|
||||
cr2ux.syncCache(formatAppComposedName(app1.Name, app1.Namespace), 1, 0)
|
||||
cr2ux.syncCache(formatAppComposedName(app1.Name, app1.Namespace), "v1", 0)
|
||||
app1.Status.LatestRevision = &common.Revision{Name: "v1"}
|
||||
Expect(cr2ux.shouldSync(ctx, app1, false)).Should(BeEquivalentTo(false))
|
||||
Expect(cr2ux.shouldSync(ctx, app1, true)).Should(BeEquivalentTo(true))
|
||||
Expect(ds.Delete(ctx, &model.Application{Name: "app1"})).Should(BeNil())
|
||||
|
||||
@@ -25,25 +25,32 @@ import (
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
apitypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/event/sync/convert"
|
||||
v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/step"
|
||||
)
|
||||
|
||||
// ConvertApp2DatastoreApp will convert Application CR to datastore application related resources
|
||||
func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.Application) (*model.DataStoreApp, error) {
|
||||
func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.Application) (*DataStoreApp, error) {
|
||||
cli := c.cli
|
||||
|
||||
appName := c.getAppMetaName(ctx, targetApp.Name, targetApp.Namespace)
|
||||
|
||||
project := model.DefaultInitName
|
||||
project := v1.CreateProjectRequest{
|
||||
Name: model.DefaultInitName,
|
||||
}
|
||||
sourceOfTruth := model.FromCR
|
||||
if _, ok := targetApp.Labels[oam.LabelAddonName]; ok && strings.HasPrefix(targetApp.Name, "addon-") {
|
||||
project = model.DefaultAddonProject
|
||||
if _, ok := targetApp.Labels[oam.LabelAddonName]; ok && strings.HasPrefix(targetApp.Name, "addon-") && targetApp.Namespace == apitypes.DefaultKubeVelaNS {
|
||||
project = v1.CreateProjectRequest{
|
||||
Name: model.DefaultSystemProject,
|
||||
Alias: model.DefaultSystemProjectAlias,
|
||||
Namespace: targetApp.Namespace,
|
||||
}
|
||||
sourceOfTruth = model.FromInner
|
||||
}
|
||||
|
||||
@@ -51,18 +58,20 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.
|
||||
Name: appName,
|
||||
Description: model.AutoGenDesc,
|
||||
Alias: targetApp.Name,
|
||||
Project: project,
|
||||
Project: project.Name,
|
||||
Labels: map[string]string{
|
||||
model.LabelSyncNamespace: targetApp.Namespace,
|
||||
model.LabelSyncGeneration: strconv.FormatInt(targetApp.Generation, 10),
|
||||
model.LabelSyncRevision: getRevision(*targetApp),
|
||||
model.LabelSourceOfTruth: sourceOfTruth,
|
||||
},
|
||||
}
|
||||
appMeta.CreateTime = targetApp.CreationTimestamp.Time
|
||||
appMeta.UpdateTime = time.Now()
|
||||
// 1. convert app meta and env
|
||||
dsApp := &model.DataStoreApp{
|
||||
dsApp := &DataStoreApp{
|
||||
AppMeta: appMeta,
|
||||
Project: &project,
|
||||
}
|
||||
|
||||
// 2. convert the target
|
||||
@@ -72,54 +81,25 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.
|
||||
return nil, fmt.Errorf("fail to list the targets, %w", err)
|
||||
}
|
||||
var envTargetNames map[string]string
|
||||
dsApp.Targets, envTargetNames = convert.FromCRTargets(ctx, c.cli, targetApp, existTargets, project)
|
||||
dsApp.Targets, envTargetNames = convert.FromCRTargets(ctx, c.cli, targetApp, existTargets, project.Name)
|
||||
|
||||
// 3. convert the environment
|
||||
existEnv := &model.Env{Namespace: targetApp.Namespace}
|
||||
existEnvs, err := c.ds.List(ctx, existEnv, nil)
|
||||
// 3. generate the environment
|
||||
env, newProject, err := c.generateEnv(ctx, project.Name, targetApp.Namespace, envTargetNames)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to list the env, %w", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(existEnvs) > 0 {
|
||||
env := existEnvs[0].(*model.Env)
|
||||
dsApp.AppMeta.Project = env.Project
|
||||
for name, project := range envTargetNames {
|
||||
if !utils.StringsContain(env.Targets, name) && project == env.Project {
|
||||
env.Targets = append(env.Targets, name)
|
||||
}
|
||||
}
|
||||
dsApp.Env = env
|
||||
}
|
||||
if dsApp.Env == nil {
|
||||
var newProject string
|
||||
var targetNames []string
|
||||
for name, project := range envTargetNames {
|
||||
if newProject == "" {
|
||||
newProject = project
|
||||
}
|
||||
if newProject == project {
|
||||
targetNames = append(targetNames, name)
|
||||
}
|
||||
}
|
||||
var namespace corev1.Namespace
|
||||
envName := model.AutoGenEnvNamePrefix + targetApp.Namespace
|
||||
// Get the env name from the label of namespace
|
||||
// If the namespace created by `vela env init`
|
||||
if c.cli.Get(ctx, types.NamespacedName{Name: targetApp.Namespace}, &namespace) == nil && namespace.Labels != nil {
|
||||
if env := namespace.Labels[oam.LabelNamespaceOfEnvName]; env != "" {
|
||||
envName = env
|
||||
}
|
||||
}
|
||||
dsApp.Env = &model.Env{
|
||||
Name: envName,
|
||||
Namespace: targetApp.Namespace,
|
||||
Description: model.AutoGenDesc,
|
||||
Project: newProject,
|
||||
Alias: model.AutoGenEnvNamePrefix + targetApp.Namespace,
|
||||
Targets: targetNames,
|
||||
klog.V(5).Infof("generate the environment %s for the application %s", env.Name, targetApp.Name)
|
||||
dsApp.Env = env
|
||||
if newProject != "" {
|
||||
project = v1.CreateProjectRequest{
|
||||
Name: newProject,
|
||||
Namespace: targetApp.Namespace,
|
||||
}
|
||||
dsApp.Project = &project
|
||||
dsApp.Env.Project = newProject
|
||||
dsApp.AppMeta.Project = newProject
|
||||
}
|
||||
|
||||
dsApp.Eb = &model.EnvBinding{
|
||||
AppPrimaryKey: appMeta.PrimaryKey(),
|
||||
Name: dsApp.Env.Name,
|
||||
@@ -140,11 +120,10 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.
|
||||
}
|
||||
|
||||
// 5. convert workflow
|
||||
wf, steps, err := convert.FromCRWorkflow(ctx, cli, appMeta.PrimaryKey(), targetApp)
|
||||
wf, steps, err := convert.FromCRWorkflow(ctx, cli, appMeta.PrimaryKey(), targetApp, dsApp.Env.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wf.EnvName = dsApp.Env.Name
|
||||
dsApp.Workflow = &wf
|
||||
|
||||
// 6. convert policy, some policies are references in workflow step, we need to sync all the outside policy to make that work
|
||||
@@ -171,6 +150,8 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.
|
||||
// 7. convert the revision
|
||||
if revision := convert.FromCRApplicationRevision(ctx, cli, targetApp, *dsApp.Workflow, dsApp.Env.Name); revision != nil {
|
||||
dsApp.Revision = revision
|
||||
} else {
|
||||
klog.Warningf("can't generate the application revision(%s) for the app %s", getRevision(*targetApp), targetApp.Name)
|
||||
}
|
||||
// 8. convert the workflow record
|
||||
if record := convert.FromCRWorkflowRecord(targetApp, *dsApp.Workflow, dsApp.Revision); record != nil {
|
||||
@@ -178,3 +159,73 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.
|
||||
}
|
||||
return dsApp, nil
|
||||
}
|
||||
|
||||
func (c *CR2UX) generateEnv(ctx context.Context, defaultProject string, envNamespace string, envTargetNames map[string]string) (*model.Env, string, error) {
|
||||
existEnv := &model.Env{Namespace: envNamespace}
|
||||
existEnvs, err := c.ds.List(ctx, existEnv, nil)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("fail to list the env, %w", err)
|
||||
}
|
||||
if len(existEnvs) > 0 {
|
||||
env := existEnvs[0].(*model.Env)
|
||||
for name, project := range envTargetNames {
|
||||
if !utils.StringsContain(env.Targets, name) && project == env.Project {
|
||||
env.Targets = append(env.Targets, name)
|
||||
}
|
||||
}
|
||||
return env, "", nil
|
||||
}
|
||||
|
||||
// generate new environment
|
||||
var newProject string
|
||||
var targetNames []string
|
||||
for name, project := range envTargetNames {
|
||||
if newProject == "" {
|
||||
newProject = project
|
||||
}
|
||||
if newProject == project {
|
||||
targetNames = append(targetNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
envName := model.AutoGenEnvNamePrefix + envNamespace
|
||||
alias := envName
|
||||
if envNamespace == apitypes.DefaultKubeVelaNS {
|
||||
envName = model.DefaultSystemProject
|
||||
alias = model.DefaultSystemProjectAlias
|
||||
}
|
||||
// Get the env name from the label of namespace
|
||||
// If the namespace created by `vela env init`
|
||||
var namespace corev1.Namespace
|
||||
if c.cli.Get(ctx, types.NamespacedName{Name: envNamespace}, &namespace) == nil && namespace.Labels != nil {
|
||||
if env := namespace.Labels[oam.LabelNamespaceOfEnvName]; env != "" {
|
||||
envName = env
|
||||
}
|
||||
}
|
||||
env := &model.Env{
|
||||
Name: envName,
|
||||
Namespace: envNamespace,
|
||||
Description: model.AutoGenDesc,
|
||||
Alias: alias,
|
||||
Targets: targetNames,
|
||||
Project: defaultProject,
|
||||
}
|
||||
if defaultProject == model.DefaultInitName && newProject != "" {
|
||||
return env, newProject, nil
|
||||
}
|
||||
return env, "", nil
|
||||
}
|
||||
|
||||
func getRevision(app v1beta1.Application) string {
|
||||
if app.Status.LatestRevision == nil {
|
||||
return ""
|
||||
}
|
||||
return app.Status.LatestRevision.Name
|
||||
}
|
||||
|
||||
func getSyncedRevision(rev *model.ApplicationRevision) string {
|
||||
if rev == nil {
|
||||
return ""
|
||||
}
|
||||
return rev.Version
|
||||
}
|
||||
|
||||
@@ -32,7 +32,9 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/policy"
|
||||
)
|
||||
|
||||
@@ -85,17 +87,19 @@ func FromCRPolicy(appPrimaryKey string, policyCR v1beta1.AppPolicy, creator stri
|
||||
}
|
||||
|
||||
// FromCRWorkflow converts Application CR Workflow section into velaux data store workflow
|
||||
func FromCRWorkflow(ctx context.Context, cli client.Client, appPrimaryKey string, app *v1beta1.Application) (model.Workflow, []workflowv1alpha1.WorkflowStep, error) {
|
||||
func FromCRWorkflow(ctx context.Context, cli client.Client, appPrimaryKey string, app *v1beta1.Application, envName string) (model.Workflow, []workflowv1alpha1.WorkflowStep, error) {
|
||||
var defaultWorkflow = true
|
||||
name := app.Annotations[oam.AnnotationWorkflowName]
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("workflow-%s", envName)
|
||||
}
|
||||
dataWf := model.Workflow{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
// every namespace has a synced env
|
||||
EnvName: model.AutoGenEnvNamePrefix + app.Namespace,
|
||||
// every application has a synced workflow
|
||||
Name: model.AutoGenWorkflowNamePrefix + appPrimaryKey,
|
||||
Alias: model.AutoGenWorkflowNamePrefix + app.Name,
|
||||
Description: model.AutoGenDesc,
|
||||
Default: &defaultWorkflow,
|
||||
EnvName: envName,
|
||||
Name: name,
|
||||
Alias: fmt.Sprintf("%s Workflow", utils.FirstUpper(envName)),
|
||||
Description: model.AutoGenDesc,
|
||||
Default: &defaultWorkflow,
|
||||
}
|
||||
if app.Spec.Workflow == nil {
|
||||
return dataWf, nil, nil
|
||||
@@ -224,6 +228,8 @@ func FromCRWorkflowRecord(app *v1beta1.Application, workflow model.Workflow, rev
|
||||
Name: strings.Replace(app.Status.Workflow.AppRevision, ":", "-", 1),
|
||||
Namespace: app.Namespace,
|
||||
Finished: model.UnFinished,
|
||||
StartTime: app.Status.Workflow.StartTime.Time,
|
||||
EndTime: app.Status.Workflow.EndTime.Time,
|
||||
RevisionPrimaryKey: revision.Version,
|
||||
Steps: steps,
|
||||
Status: model.RevisionStatusRunning,
|
||||
@@ -250,14 +256,30 @@ func FromCRApplicationRevision(ctx context.Context, cli client.Client, app *v1be
|
||||
if app.Status.Workflow == nil || app.Status.Workflow.AppRevision == "" {
|
||||
return nil
|
||||
}
|
||||
versions := strings.Split(app.Status.Workflow.AppRevision, ":")
|
||||
versionName := versions[0]
|
||||
var appRevision v1beta1.ApplicationRevision
|
||||
ctxTimeout, cancel := context.WithTimeout(ctx, time.Second*20)
|
||||
defer cancel()
|
||||
if err := cli.Get(ctxTimeout, types.NamespacedName{Namespace: app.Namespace, Name: versionName}, &appRevision); err != nil {
|
||||
klog.Errorf("failed to get the application revision %s", err.Error())
|
||||
return nil
|
||||
publishVersion := app.Status.Workflow.AppRevision
|
||||
var appRevision *v1beta1.ApplicationRevision
|
||||
var appRevisionList v1beta1.ApplicationRevisionList
|
||||
if err := cli.List(ctx, &appRevisionList,
|
||||
client.HasLabels{fmt.Sprintf("%s=%s", oam.LabelAppName, app.Name)},
|
||||
client.InNamespace(app.Namespace)); err == nil && len(appRevisionList.Items) > 0 {
|
||||
for i, rev := range appRevisionList.Items {
|
||||
if rev.Annotations[oam.AnnotationPublishVersion] == publishVersion {
|
||||
appRevision = &appRevisionList.Items[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if appRevision == nil {
|
||||
versions := strings.Split(app.Status.Workflow.AppRevision, ":")
|
||||
versionName := versions[0]
|
||||
var loadAR v1beta1.ApplicationRevision
|
||||
ctxTimeout, cancel := context.WithTimeout(ctx, time.Second*20)
|
||||
defer cancel()
|
||||
if err := cli.Get(ctxTimeout, types.NamespacedName{Namespace: app.Namespace, Name: versionName}, &loadAR); err != nil {
|
||||
klog.Errorf("failed to get the application revision %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
appRevision = &loadAR
|
||||
}
|
||||
configByte, _ := yaml.Marshal(appRevision.Spec.Application)
|
||||
return &model.ApplicationRevision{
|
||||
@@ -268,7 +290,7 @@ func FromCRApplicationRevision(ctx context.Context, cli client.Client, app *v1be
|
||||
AppPrimaryKey: workflow.AppPrimaryKey,
|
||||
RevisionCRName: appRevision.Name,
|
||||
WorkflowName: workflow.Name,
|
||||
Version: versionName,
|
||||
Version: appRevision.Name,
|
||||
ApplyAppConfig: string(configByte),
|
||||
TriggerType: "SyncFromCR",
|
||||
EnvName: envName,
|
||||
|
||||
@@ -84,9 +84,11 @@ func (c *CR2UX) AddOrUpdate(ctx context.Context, targetApp *v1beta1.Application)
|
||||
klog.Errorf("Convert App to data store err %v", err)
|
||||
return err
|
||||
}
|
||||
if err = StoreProject(ctx, dsApp.AppMeta.Project, ds, c.projectService); err != nil {
|
||||
klog.Errorf("get or create project for sync process err %v", err)
|
||||
return err
|
||||
if dsApp.Project != nil {
|
||||
if err = StoreProject(ctx, *dsApp.Project, ds, c.projectService); err != nil {
|
||||
klog.Errorf("get or create project for sync process err %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = StoreTargets(ctx, dsApp, ds, c.targetService); err != nil {
|
||||
@@ -131,7 +133,10 @@ func (c *CR2UX) AddOrUpdate(ctx context.Context, targetApp *v1beta1.Application)
|
||||
}
|
||||
|
||||
// update cache
|
||||
c.syncCache(dsApp.AppMeta.PrimaryKey(), targetApp.Generation, int64(len(dsApp.Targets)))
|
||||
key := formatAppComposedName(targetApp.Name, targetApp.Namespace)
|
||||
syncedVersion := getSyncedRevision(dsApp.Revision)
|
||||
c.syncCache(key, syncedVersion, int64(len(dsApp.Targets)))
|
||||
klog.Infof("application %s/%s revision %s synced successful", targetApp.Name, targetApp.Namespace, syncedVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"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/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/service"
|
||||
@@ -115,6 +116,8 @@ var _ = Describe("Test CR convert to ux", func() {
|
||||
app1 := &v1beta1.Application{}
|
||||
Expect(common2.ReadYamlToObject("testdata/test-app1.yaml", app1)).Should(BeNil())
|
||||
app1.Namespace = appNS1
|
||||
envName := model.AutoGenEnvNamePrefix + app1.Namespace
|
||||
|
||||
Expect(cr2ux.AddOrUpdate(context.Background(), app1)).Should(BeNil())
|
||||
comp1 := model.ApplicationComponent{AppPrimaryKey: apName1, Name: "nginx"}
|
||||
Expect(ds.Get(context.Background(), &comp1)).Should(BeNil())
|
||||
@@ -128,14 +131,15 @@ var _ = Describe("Test CR convert to ux", func() {
|
||||
Expect(ds.Get(ctx, &appPlc1)).Should(BeNil())
|
||||
appPlc2 := model.ApplicationPolicy{AppPrimaryKey: app1.Name, Name: "topology-local"}
|
||||
Expect(ds.Get(ctx, &appPlc2)).Should(BeNil())
|
||||
appwf1 := model.Workflow{AppPrimaryKey: app1.Name, Name: model.AutoGenWorkflowNamePrefix + app1.Name}
|
||||
|
||||
appwf1 := model.Workflow{AppPrimaryKey: app1.Name, Name: "workflow-" + envName}
|
||||
Expect(ds.Get(ctx, &appwf1)).Should(BeNil())
|
||||
Expect(len(appwf1.Steps)).Should(BeEquivalentTo(1))
|
||||
|
||||
app2 := &v1beta1.Application{}
|
||||
Expect(common2.ReadYamlToObject("testdata/test-app2.yaml", app2)).Should(BeNil())
|
||||
app1.Namespace = appNS1
|
||||
app1.Generation = 2
|
||||
app1.Status.LatestRevision = &common.Revision{Name: "v2"}
|
||||
app1.Spec = app2.Spec
|
||||
Expect(cr2ux.AddOrUpdate(context.Background(), app1)).Should(BeNil())
|
||||
comp3 := model.ApplicationComponent{AppPrimaryKey: apName1, Name: "blog"}
|
||||
|
||||
@@ -20,8 +20,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/model"
|
||||
@@ -32,9 +30,23 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// DataStoreApp is a memory struct that describes the model of an application in datastore
|
||||
type DataStoreApp struct {
|
||||
Project *v1.CreateProjectRequest
|
||||
AppMeta *model.Application
|
||||
Env *model.Env
|
||||
Eb *model.EnvBinding
|
||||
Comps []*model.ApplicationComponent
|
||||
Policies []*model.ApplicationPolicy
|
||||
Workflow *model.Workflow
|
||||
Targets []*model.Target
|
||||
Record *model.WorkflowRecord
|
||||
Revision *model.ApplicationRevision
|
||||
}
|
||||
|
||||
// StoreProject will create project for synced application
|
||||
func StoreProject(ctx context.Context, name string, ds datastore.DataStore, projectService service.ProjectService) error {
|
||||
err := ds.Get(ctx, &model.Project{Name: name})
|
||||
func StoreProject(ctx context.Context, project v1.CreateProjectRequest, ds datastore.DataStore, projectService service.ProjectService) error {
|
||||
err := ds.Get(ctx, &model.Project{Name: project.Name})
|
||||
if err == nil {
|
||||
// it means the record already exists, don't need to add anything
|
||||
return nil
|
||||
@@ -44,18 +56,16 @@ func StoreProject(ctx context.Context, name string, ds datastore.DataStore, proj
|
||||
return err
|
||||
}
|
||||
if projectService != nil {
|
||||
_, err := projectService.CreateProject(ctx, v1.CreateProjectRequest{
|
||||
Name: name,
|
||||
Alias: cases.Title(language.Und).String(name),
|
||||
Owner: model.DefaultAdminUserName,
|
||||
Description: model.AutoGenProj})
|
||||
project.Owner = model.DefaultAdminUserName
|
||||
project.Description = model.AutoGenProj
|
||||
_, err := projectService.CreateProject(ctx, project)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StoreAppMeta will sync application metadata from CR to datastore
|
||||
func StoreAppMeta(ctx context.Context, app *model.DataStoreApp, ds datastore.DataStore) error {
|
||||
func StoreAppMeta(ctx context.Context, app *DataStoreApp, ds datastore.DataStore) error {
|
||||
oldApp := &model.Application{Name: app.AppMeta.Name}
|
||||
err := ds.Get(ctx, oldApp)
|
||||
if err == nil {
|
||||
@@ -71,7 +81,7 @@ func StoreAppMeta(ctx context.Context, app *model.DataStoreApp, ds datastore.Dat
|
||||
}
|
||||
|
||||
// StoreEnv will sync application namespace from CR to datastore env, one namespace belongs to one env
|
||||
func StoreEnv(ctx context.Context, app *model.DataStoreApp, ds datastore.DataStore, envService service.EnvService) error {
|
||||
func StoreEnv(ctx context.Context, app *DataStoreApp, ds datastore.DataStore, envService service.EnvService) error {
|
||||
curEnv := &model.Env{Name: app.Env.Name}
|
||||
err := ds.Get(ctx, curEnv)
|
||||
if err == nil {
|
||||
@@ -235,7 +245,7 @@ func StorePolicy(ctx context.Context, appPrimaryKey string, expPolicies []*model
|
||||
}
|
||||
|
||||
// StoreWorkflow will sync workflow application CR to datastore, it updates the only one workflow from the application with specified name
|
||||
func StoreWorkflow(ctx context.Context, dsApp *model.DataStoreApp, ds datastore.DataStore) error {
|
||||
func StoreWorkflow(ctx context.Context, dsApp *DataStoreApp, ds datastore.DataStore) error {
|
||||
old := &model.Workflow{AppPrimaryKey: dsApp.AppMeta.Name, Name: dsApp.Workflow.Name}
|
||||
err := ds.Get(ctx, old)
|
||||
if err == nil {
|
||||
@@ -251,7 +261,7 @@ func StoreWorkflow(ctx context.Context, dsApp *model.DataStoreApp, ds datastore.
|
||||
}
|
||||
|
||||
// StoreWorkflowRecord will sync workflow status to datastore.
|
||||
func StoreWorkflowRecord(ctx context.Context, dsApp *model.DataStoreApp, ds datastore.DataStore) error {
|
||||
func StoreWorkflowRecord(ctx context.Context, dsApp *DataStoreApp, ds datastore.DataStore) error {
|
||||
if dsApp.Record == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -267,7 +277,7 @@ func StoreWorkflowRecord(ctx context.Context, dsApp *model.DataStoreApp, ds data
|
||||
}
|
||||
|
||||
// StoreApplicationRevision will sync the application revision to datastore.
|
||||
func StoreApplicationRevision(ctx context.Context, dsApp *model.DataStoreApp, ds datastore.DataStore) error {
|
||||
func StoreApplicationRevision(ctx context.Context, dsApp *DataStoreApp, ds datastore.DataStore) error {
|
||||
if dsApp.Revision == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -285,7 +295,7 @@ func StoreApplicationRevision(ctx context.Context, dsApp *model.DataStoreApp, ds
|
||||
}
|
||||
|
||||
// StoreTargets will sync targets from application CR to datastore
|
||||
func StoreTargets(ctx context.Context, dsApp *model.DataStoreApp, ds datastore.DataStore, targetService service.TargetService) error {
|
||||
func StoreTargets(ctx context.Context, dsApp *DataStoreApp, ds datastore.DataStore, targetService service.TargetService) error {
|
||||
for _, t := range dsApp.Targets {
|
||||
err := ds.Get(ctx, t)
|
||||
if err == nil {
|
||||
|
||||
@@ -87,7 +87,7 @@ func NewDatastore(cfg datastore.Config) (ds datastore.DataStore, err error) {
|
||||
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
|
||||
}
|
||||
case "kubeapi":
|
||||
ds, err = kubeapi.New(context.Background(), cfg)
|
||||
ds, err = kubeapi.New(context.Background(), cfg, k8sClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ func (a *ApplicationSync) Start(ctx context.Context, errorChan chan error) {
|
||||
app := getApp(obj)
|
||||
if app.DeletionTimestamp == nil {
|
||||
a.Queue.Add(app)
|
||||
klog.Infof("watched update/add app event, namespace: %s, name: %s", app.Namespace, app.Name)
|
||||
klog.V(4).Infof("watched update/add app event, namespace: %s, name: %s", app.Namespace, app.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func (a *ApplicationSync) Start(ctx context.Context, errorChan chan error) {
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
app := getApp(obj)
|
||||
klog.Infof("watched delete app event, namespace: %s, name: %s", app.Namespace, app.Name)
|
||||
klog.V(4).Infof("watched delete app event, namespace: %s, name: %s", app.Namespace, app.Name)
|
||||
a.Queue.Forget(app)
|
||||
a.Queue.Done(app)
|
||||
err = cu.DeleteApp(ctx, app)
|
||||
|
||||
@@ -26,8 +26,10 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
|
||||
"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/apiserver/domain/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/repository"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
@@ -105,7 +107,7 @@ var _ = Describe("Test Worker CR sync to datastore", func() {
|
||||
Expect(appPlc1.CreateTime.IsZero()).Should(BeFalse())
|
||||
appPlc2 := model.ApplicationPolicy{AppPrimaryKey: app1.Name, Name: "topology-local"}
|
||||
Expect(ds.Get(ctx, &appPlc2)).Should(BeNil())
|
||||
appwf1 := model.Workflow{AppPrimaryKey: app1.Name, Name: model.AutoGenWorkflowNamePrefix + app1.Name}
|
||||
appwf1 := model.Workflow{AppPrimaryKey: app1.Name, Name: repository.ConvertWorkflowName(env.Name)}
|
||||
Expect(ds.Get(ctx, &appwf1)).Should(BeNil())
|
||||
|
||||
By("create test app2 and check the syncing results")
|
||||
@@ -132,6 +134,9 @@ var _ = Describe("Test Worker CR sync to datastore", func() {
|
||||
app2.Spec = newapp2.Spec
|
||||
Expect(k8sClient.Update(context.TODO(), app2)).Should(BeNil())
|
||||
|
||||
app2.Status.LatestRevision = &common.Revision{Name: "v3"}
|
||||
Expect(k8sClient.Status().Update(context.TODO(), app2)).Should(BeNil())
|
||||
|
||||
Eventually(func() error {
|
||||
appm := model.ApplicationComponent{AppPrimaryKey: formatAppComposedName(app2.Name, app2.Namespace), Name: "nginx2"}
|
||||
return ds.Get(ctx, &appm)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user