mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-02 01:30:47 +00:00
Compare commits
16 Commits
v1.3.0-bet
...
v1.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aa4412a0f | ||
|
|
ef4b9816e1 | ||
|
|
1c5aab1852 | ||
|
|
966dbc1c74 | ||
|
|
4eafb46c87 | ||
|
|
a97a4d0ed7 | ||
|
|
77c02f9eec | ||
|
|
3157efd421 | ||
|
|
8ff93b33e2 | ||
|
|
c6b9abe4c4 | ||
|
|
150ef6e99e | ||
|
|
0ada407fbe | ||
|
|
c4af1ba643 | ||
|
|
de84421487 | ||
|
|
38a8a7f88a | ||
|
|
b4ddf0e4c3 |
4
.github/workflows/apiserver-test.yaml
vendored
4
.github/workflows/apiserver-test.yaml
vendored
@@ -65,7 +65,7 @@ jobs:
|
||||
- name: Setup Kind Cluster (Worker)
|
||||
run: |
|
||||
kind delete cluster --name worker
|
||||
kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4 --name worker
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca --name worker
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
kind get kubeconfig --name worker --internal > /tmp/worker.kubeconfig
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
- name: Setup Kind Cluster (Hub)
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
|
||||
4
.github/workflows/e2e-multicluster-test.yml
vendored
4
.github/workflows/e2e-multicluster-test.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
- name: Setup Kind Cluster (Worker)
|
||||
run: |
|
||||
kind delete cluster --name worker
|
||||
kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4 --name worker
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca --name worker
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
kind get kubeconfig --name worker --internal > /tmp/worker.kubeconfig
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
- name: Setup Kind Cluster (Hub)
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
|
||||
2
.github/workflows/e2e-rollout-test.yml
vendored
2
.github/workflows/e2e-rollout-test.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
- name: Setup Kind Cluster
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
|
||||
2
.github/workflows/e2e-test.yml
vendored
2
.github/workflows/e2e-test.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
- name: Setup Kind Cluster
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
|
||||
@@ -51,7 +51,8 @@ Full documentation is available on the [KubeVela website](https://kubevela.io/).
|
||||
- Wechat Group (*Chinese*): Broker wechat to add you into the user group.
|
||||
|
||||
<img src="https://static.kubevela.net/images/barnett-wechat.jpg" width="200" />
|
||||
- Bi-weekly Community Call: [Meeting Notes](https://docs.google.com/document/d/1nqdFEyULekyksFHtFvgvFAYE-0AMHKoS3RMnaKsarjs)
|
||||
- Bi-weekly Community Call: [Meeting Notes](https://docs.google.com/document/d/1nqdFEyULekyksFHtFvgvFAYE-0AMHKoS3RMnaKsarjs).
|
||||
- Bi-weekly Chinese Community Call: [Video Records](https://space.bilibili.com/180074935/channel/seriesdetail?sid=1842207).
|
||||
|
||||
## Talks and Conferences
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ var (
|
||||
// Workflow meta
|
||||
var (
|
||||
WorkflowKind = "Workflow"
|
||||
WorkflowGroupVersionKind = SchemeGroupVersion.WithKind(PolicyKind)
|
||||
WorkflowGroupVersionKind = SchemeGroupVersion.WithKind(WorkflowKind)
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -61,6 +61,18 @@ const (
|
||||
AnnoIngressControllerHTTPSPort = "ingress.controller/https-port"
|
||||
// AnnoIngressControllerHTTPPort define ingress controller listen port for http
|
||||
AnnoIngressControllerHTTPPort = "ingress.controller/http-port"
|
||||
// LabelConfigType is the label for config type
|
||||
LabelConfigType = "config.oam.dev/type"
|
||||
// LabelConfigCatalog is the label for config catalog
|
||||
LabelConfigCatalog = "config.oam.dev/catalog"
|
||||
// LabelConfigSubType is the sub-type for a config type
|
||||
LabelConfigSubType = "config.oam.dev/sub-type"
|
||||
// LabelConfigProject is the label for config project
|
||||
LabelConfigProject = "config.oam.dev/project"
|
||||
// LabelConfigSyncToMultiCluster is the label to decide whether a config will be synchronized to multi-cluster
|
||||
LabelConfigSyncToMultiCluster = "config.oam.dev/multi-cluster"
|
||||
// AnnotationConfigAlias is the annotation for config alias
|
||||
AnnotationConfigAlias = "config.oam.dev/alias"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -118,3 +130,13 @@ var DefaultFilterAnnots = []string{
|
||||
oam.AnnotationFilterAnnotationKeys,
|
||||
oam.AnnotationLastAppliedConfiguration,
|
||||
}
|
||||
|
||||
// ConfigType is the type of config
|
||||
type ConfigType string
|
||||
|
||||
const (
|
||||
// TerraformProvider is the config type for terraform provider
|
||||
TerraformProvider = "terraform-provider"
|
||||
// DexConnector is the config type for dex connector
|
||||
DexConnector = "config-dex-connector"
|
||||
)
|
||||
|
||||
@@ -125,18 +125,20 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai
|
||||
| `kubeClient.burst` | The burst for reconcile clients, default is 100 | `100` |
|
||||
|
||||
|
||||
## Uninstalling the Chart
|
||||
## Uninstallation
|
||||
|
||||
To uninstall/delete the KubeVela helm release
|
||||
### Vela CLI
|
||||
|
||||
To uninstall KubeVela, you can just run the following command by vela CLI:
|
||||
|
||||
```shell
|
||||
$ helm uninstall -n vela-system kubevela
|
||||
vela uninstall --force
|
||||
```
|
||||
|
||||
The command removes all the Kubernetes components associated with kubevela and deletes the release.
|
||||
### Helm CLI
|
||||
|
||||
**Notice**: You must disable all the addons before uninstallation, this is a script for convenience.
|
||||
|
||||
**Notice**: If you enable fluxcd addon when install the chart by set `enableFluxcdAddon=true` .Uninstall wouldn't disable the fluxcd addon ,and it will be kept in the cluster.Please guarantee there is no application in cluster use this addon and disable it firstly before uninstall the helm chart.
|
||||
You can use this script to disable all addons.
|
||||
```shell
|
||||
#! /bin/sh
|
||||
addon=$(vela addon list|grep enabled|awk {'print $1'})
|
||||
@@ -156,6 +158,15 @@ if [ $fluxcd ]; then
|
||||
fi
|
||||
```
|
||||
|
||||
To uninstall the KubeVela helm release:
|
||||
|
||||
```shell
|
||||
$ helm uninstall -n vela-system kubevela
|
||||
```
|
||||
|
||||
Finally, this command will remove all the Kubernetes resources associated with KubeVela and remove this chart release.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -27,9 +27,5 @@ Welcome to use the KubeVela! Enjoy your shipping application journey!
|
||||
| . \| |_| || |_) || __/ \ V /| __/| || (_| |
|
||||
|_|\_\\__,_||_.__/ \___| \_/ \___||_| \__,_|
|
||||
|
||||
** Please note before uninstalling **
|
||||
|
||||
If you enable fluxcd addon when install the chart by set `enableFluxcdAddon=true` .
|
||||
Uninstall wouldn't disable the fluxcd addon ,and it will be kept in the cluster.
|
||||
Please guarantee there is no application in cluster using this addon and disable it firstly before uninstall the helm chart.
|
||||
And you can find the script of one-short disable all addons from the uninstalling section of https://github.com/oam-dev/kubevela/blob/master/charts/vela-core/README.md.
|
||||
You can refer to https://kubevela.io for more details.
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/config-dex-connector.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
custom.definition.oam.dev/alias.config.oam.dev: Dex Connector
|
||||
definition.oam.dev/description: Config information to authenticate Dex connectors
|
||||
labels:
|
||||
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
|
||||
custom.definition.oam.dev/multi-cluster.config.oam.dev: "false"
|
||||
custom.definition.oam.dev/type.config.oam.dev: dex-connector
|
||||
name: config-dex-connector
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "v1"
|
||||
kind: "Secret"
|
||||
metadata: {
|
||||
name: parameter.name
|
||||
namespace: context.namespace
|
||||
labels: {
|
||||
"config.oam.dev/catalog": "velacore-config"
|
||||
"config.oam.dev/type": "dex-connector"
|
||||
"config.oam.dev/multi-cluster": "false"
|
||||
"config.oam.dev/identifier": parameter.name
|
||||
"config.oam.dev/sub-type": parameter.type
|
||||
}
|
||||
}
|
||||
type: "Opaque"
|
||||
|
||||
if parameter.type == "github" {
|
||||
stringData: parameter.github
|
||||
}
|
||||
if parameter.type == "ldap" {
|
||||
stringData: parameter.ldap
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Config type
|
||||
type: "github" | "ldap"
|
||||
github?: {
|
||||
// +usage=GitHub client ID
|
||||
clientID: string
|
||||
// +usage=GitHub client secret
|
||||
clientSecret: string
|
||||
// +usage=GitHub call back URL
|
||||
callbackURL: string
|
||||
}
|
||||
ldap?: {
|
||||
host: string
|
||||
insecureNoSSL: *true | bool
|
||||
insecureSkipVerify: bool
|
||||
startTLS: bool
|
||||
usernamePrompt: string
|
||||
userSearch: {
|
||||
baseDN: string
|
||||
username: string
|
||||
idAttr: string
|
||||
emailAttr: string
|
||||
nameAttr: string
|
||||
}
|
||||
}
|
||||
}
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/config-image-registry.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
custom.definition.oam.dev/alias.config.oam.dev: Image Registry
|
||||
definition.oam.dev/description: Config information to authenticate image registry
|
||||
labels:
|
||||
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
|
||||
custom.definition.oam.dev/multi-cluster.config.oam.dev: "true"
|
||||
custom.definition.oam.dev/type.config.oam.dev: image-registry
|
||||
name: config-image-registry
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
output: {
|
||||
apiVersion: "v1"
|
||||
kind: "Secret"
|
||||
metadata: {
|
||||
name: context.name
|
||||
namespace: context.namespace
|
||||
labels: {
|
||||
"config.oam.dev/catalog": "velacore-config"
|
||||
"config.oam.dev/type": "image-registry"
|
||||
"config.oam.dev/multi-cluster": "true"
|
||||
"config.oam.dev/identifier": parameter.registry
|
||||
"config.oam.dev/sub-type": "auth"
|
||||
}
|
||||
}
|
||||
type: "kubernetes.io/dockerconfigjson"
|
||||
stringData: {
|
||||
if parameter.auth != _|_ {
|
||||
".dockerconfigjson": json.Marshal({
|
||||
auths: "\(parameter.registry)": {
|
||||
username: parameter.auth.username
|
||||
password: parameter.auth.password
|
||||
if parameter.auth.email != _|_ {
|
||||
email: parameter.auth.email
|
||||
}
|
||||
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Image registry FQDN
|
||||
registry: string
|
||||
// +usage=Authenticate the image registry
|
||||
auth?: {
|
||||
// +usage=Private Image registry username
|
||||
username: string
|
||||
// +usage=Private Image registry password
|
||||
password: string
|
||||
// +usage=Private Image registry email
|
||||
email?: string
|
||||
}
|
||||
}
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
|
||||
308
charts/vela-core/templates/defwithtemplate/cron-task.yaml
Normal file
308
charts/vela-core/templates/defwithtemplate/cron-task.yaml
Normal file
@@ -0,0 +1,308 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/cron-task.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Describes cron jobs that run code or a script to completion.
|
||||
name: cron-task
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "batch/v1beta1"
|
||||
kind: "CronJob"
|
||||
spec: {
|
||||
schedule: parameter.schedule
|
||||
concurrencyPolicy: parameter.concurrencyPolicy
|
||||
suspend: parameter.suspend
|
||||
successfulJobsHistoryLimit: parameter.successfulJobsHistoryLimit
|
||||
failedJobsHistoryLimit: parameter.failedJobsHistoryLimit
|
||||
if parameter.startingDeadlineSeconds != _|_ {
|
||||
startingDeadlineSeconds: parameter.startingDeadlineSeconds
|
||||
}
|
||||
jobTemplate: {
|
||||
if parameter.labels != _|_ {
|
||||
metadata: labels: parameter.labels
|
||||
}
|
||||
if parameter.annotations != _|_ {
|
||||
metadata: annotations: parameter.annotations
|
||||
}
|
||||
spec: {
|
||||
parallelism: parameter.count
|
||||
completions: parameter.count
|
||||
if parameter.ttlSecondsAfterFinished != _|_ {
|
||||
ttlSecondsAfterFinished: parameter.ttlSecondsAfterFinished
|
||||
}
|
||||
if parameter.activeDeadlineSeconds != _|_ {
|
||||
activeDeadlineSeconds: parameter.activeDeadlineSeconds
|
||||
}
|
||||
backoffLimit: parameter.backoffLimit
|
||||
template: {
|
||||
if parameter.labels != _|_ {
|
||||
metadata: labels: parameter.labels
|
||||
}
|
||||
if parameter.annotations != _|_ {
|
||||
metadata: annotations: parameter.annotations
|
||||
}
|
||||
spec: {
|
||||
restartPolicy: parameter.restart
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
if parameter["imagePullPolicy"] != _|_ {
|
||||
imagePullPolicy: parameter.imagePullPolicy
|
||||
}
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
if parameter["env"] != _|_ {
|
||||
env: parameter.env
|
||||
}
|
||||
if parameter["cpu"] != _|_ {
|
||||
resources: {
|
||||
limits: cpu: parameter.cpu
|
||||
requests: cpu: parameter.cpu
|
||||
}
|
||||
}
|
||||
if parameter["memory"] != _|_ {
|
||||
resources: {
|
||||
limits: memory: parameter.memory
|
||||
requests: memory: parameter.memory
|
||||
}
|
||||
}
|
||||
if parameter["volumes"] != _|_ {
|
||||
volumeMounts: [ for v in parameter.volumes {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
}}]
|
||||
}
|
||||
}]
|
||||
if parameter["volumes"] != _|_ {
|
||||
volumes: [ for v in parameter.volumes {
|
||||
{
|
||||
name: v.name
|
||||
if v.type == "pvc" {
|
||||
persistentVolumeClaim: claimName: v.claimName
|
||||
}
|
||||
if v.type == "configMap" {
|
||||
configMap: {
|
||||
defaultMode: v.defaultMode
|
||||
name: v.cmName
|
||||
if v.items != _|_ {
|
||||
items: v.items
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.type == "secret" {
|
||||
secret: {
|
||||
defaultMode: v.defaultMode
|
||||
secretName: v.secretName
|
||||
if v.items != _|_ {
|
||||
items: v.items
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.type == "emptyDir" {
|
||||
emptyDir: medium: v.medium
|
||||
}
|
||||
}}]
|
||||
}
|
||||
if parameter["imagePullSecrets"] != _|_ {
|
||||
imagePullSecrets: [ for v in parameter.imagePullSecrets {
|
||||
name: v
|
||||
},
|
||||
]
|
||||
}
|
||||
if parameter.hostAliases != _|_ {
|
||||
hostAliases: [ for v in parameter.hostAliases {
|
||||
ip: v.ip
|
||||
hostnames: v.hostnames
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the labels in the workload
|
||||
labels?: [string]: string
|
||||
|
||||
// +usage=Specify the annotations in the workload
|
||||
annotations?: [string]: string
|
||||
|
||||
// +usage=Specify the schedule in Cron format, see https://en.wikipedia.org/wiki/Cron
|
||||
schedule: string
|
||||
|
||||
// +usage=Specify deadline in seconds for starting the job if it misses scheduled
|
||||
startingDeadlineSeconds?: int
|
||||
|
||||
// +usage=suspend subsequent executions
|
||||
suspend: *false | bool
|
||||
|
||||
// +usage=Specifies how to treat concurrent executions of a Job
|
||||
concurrencyPolicy: *"Allow" | "Allow" | "Forbid" | "Replace"
|
||||
|
||||
// +usage=The number of successful finished jobs to retain
|
||||
successfulJobsHistoryLimit: *3 | int
|
||||
|
||||
// +usage=The number of failed finished jobs to retain
|
||||
failedJobsHistoryLimit: *1 | int
|
||||
|
||||
// +usage=Specify number of tasks to run in parallel
|
||||
// +short=c
|
||||
count: *1 | int
|
||||
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
|
||||
// +usage=Specify image pull policy for your service
|
||||
imagePullPolicy?: "Always" | "Never" | "IfNotPresent"
|
||||
|
||||
// +usage=Specify image pull secrets for your service
|
||||
imagePullSecrets?: [...string]
|
||||
|
||||
// +usage=Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never.
|
||||
restart: *"Never" | string
|
||||
|
||||
// +usage=Commands to run in the container
|
||||
cmd?: [...string]
|
||||
|
||||
// +usage=Define arguments by using environment variables
|
||||
env?: [...{
|
||||
// +usage=Environment variable name
|
||||
name: string
|
||||
// +usage=The value of the environment variable
|
||||
value?: string
|
||||
// +usage=Specifies a source the value of this var should come from
|
||||
valueFrom?: {
|
||||
// +usage=Selects a key of a secret in the pod's namespace
|
||||
secretKeyRef: {
|
||||
// +usage=The name of the secret in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the secret to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=Number of CPU units for the service, like `0.5` (0.5 CPU core), `1` (1 CPU core)
|
||||
cpu?: string
|
||||
|
||||
// +usage=Specifies the attributes of the memory resource required for the container.
|
||||
memory?: string
|
||||
|
||||
// +usage=Declare volumes and volumeMounts
|
||||
volumes?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
// +usage=Specify volume type, options: "pvc","configMap","secret","emptyDir"
|
||||
type: "pvc" | "configMap" | "secret" | "emptyDir"
|
||||
if type == "pvc" {
|
||||
claimName: string
|
||||
}
|
||||
if type == "configMap" {
|
||||
defaultMode: *420 | int
|
||||
cmName: string
|
||||
items?: [...{
|
||||
key: string
|
||||
path: string
|
||||
mode: *511 | int
|
||||
}]
|
||||
}
|
||||
if type == "secret" {
|
||||
defaultMode: *420 | int
|
||||
secretName: string
|
||||
items?: [...{
|
||||
key: string
|
||||
path: string
|
||||
mode: *511 | int
|
||||
}]
|
||||
}
|
||||
if type == "emptyDir" {
|
||||
medium: *"" | "Memory"
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=An optional list of hosts and IPs that will be injected into the pod's hosts file
|
||||
hostAliases?: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
|
||||
// +usage=Limits the lifetime of a Job that has finished
|
||||
ttlSecondsAfterFinished?: int
|
||||
|
||||
// +usage=The duration in seconds relative to the startTime that the job may be continuously active before the system tries to terminate it
|
||||
activeDeadlineSeconds?: int
|
||||
|
||||
// +usage=The number of retries before marking this job failed
|
||||
backoffLimit: *6 | int
|
||||
|
||||
// +usage=Instructions for assessing whether the container is alive.
|
||||
livenessProbe?: #HealthProbe
|
||||
|
||||
// +usage=Instructions for assessing whether the container is in a suitable state to serve traffic.
|
||||
readinessProbe?: #HealthProbe
|
||||
}
|
||||
#HealthProbe: {
|
||||
|
||||
// +usage=Instructions for assessing container health by executing a command. Either this attribute or the httpGet attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the httpGet attribute and the tcpSocket attribute.
|
||||
exec?: {
|
||||
// +usage=A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures.
|
||||
command: [...string]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by executing an HTTP GET request. Either this attribute or the exec attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the tcpSocket attribute.
|
||||
httpGet?: {
|
||||
// +usage=The endpoint, relative to the port, to which the HTTP GET request should be directed.
|
||||
path: string
|
||||
// +usage=The TCP socket within the container to which the HTTP GET request should be directed.
|
||||
port: int
|
||||
httpHeaders?: [...{
|
||||
name: string
|
||||
value: string
|
||||
}]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by probing a TCP socket. Either this attribute or the exec attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the httpGet attribute.
|
||||
tcpSocket?: {
|
||||
// +usage=The TCP socket within the container that should be probed to assess container health.
|
||||
port: int
|
||||
}
|
||||
|
||||
// +usage=Number of seconds after the container is started before the first probe is initiated.
|
||||
initialDelaySeconds: *0 | int
|
||||
|
||||
// +usage=How often, in seconds, to execute the probe.
|
||||
periodSeconds: *10 | int
|
||||
|
||||
// +usage=Number of seconds after which the probe times out.
|
||||
timeoutSeconds: *1 | int
|
||||
|
||||
// +usage=Minimum consecutive successes for the probe to be considered successful after having failed.
|
||||
successThreshold: *1 | int
|
||||
|
||||
// +usage=Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).
|
||||
failureThreshold: *3 | int
|
||||
}
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
type: cronjobs.batch
|
||||
|
||||
@@ -453,6 +453,12 @@ spec:
|
||||
|
||||
// +usage=Instructions for assessing whether the container is in a suitable state to serve traffic.
|
||||
readinessProbe?: #HealthProbe
|
||||
|
||||
// +usage=Specify the hostAliases to add
|
||||
hostAliases: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
}
|
||||
#HealthProbe: {
|
||||
|
||||
@@ -494,12 +500,6 @@ spec:
|
||||
|
||||
// +usage=Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).
|
||||
failureThreshold: *3 | int
|
||||
|
||||
// +usage=Specify the hostAliases to add
|
||||
hostAliases: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/config-dex-connector.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
custom.definition.oam.dev/alias.config.oam.dev: Dex Connector
|
||||
definition.oam.dev/description: Config information to authenticate Dex connectors
|
||||
labels:
|
||||
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
|
||||
custom.definition.oam.dev/multi-cluster.config.oam.dev: "false"
|
||||
custom.definition.oam.dev/type.config.oam.dev: dex-connector
|
||||
name: config-dex-connector
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "v1"
|
||||
kind: "Secret"
|
||||
metadata: {
|
||||
name: parameter.name
|
||||
namespace: context.namespace
|
||||
labels: {
|
||||
"config.oam.dev/catalog": "velacore-config"
|
||||
"config.oam.dev/type": "dex-connector"
|
||||
"config.oam.dev/multi-cluster": "false"
|
||||
"config.oam.dev/identifier": parameter.name
|
||||
"config.oam.dev/sub-type": parameter.type
|
||||
}
|
||||
}
|
||||
type: "Opaque"
|
||||
|
||||
if parameter.type == "github" {
|
||||
stringData: parameter.github
|
||||
}
|
||||
if parameter.type == "ldap" {
|
||||
stringData: parameter.ldap
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Config type
|
||||
type: "github" | "ldap"
|
||||
github?: {
|
||||
// +usage=GitHub client ID
|
||||
clientID: string
|
||||
// +usage=GitHub client secret
|
||||
clientSecret: string
|
||||
// +usage=GitHub call back URL
|
||||
callbackURL: string
|
||||
}
|
||||
ldap?: {
|
||||
host: string
|
||||
insecureNoSSL: *true | bool
|
||||
insecureSkipVerify: bool
|
||||
startTLS: bool
|
||||
usernamePrompt: string
|
||||
userSearch: {
|
||||
baseDN: string
|
||||
username: string
|
||||
idAttr: string
|
||||
emailAttr: string
|
||||
nameAttr: string
|
||||
}
|
||||
}
|
||||
}
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/config-image-registry.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
custom.definition.oam.dev/alias.config.oam.dev: Image Registry
|
||||
definition.oam.dev/description: Config information to authenticate image registry
|
||||
labels:
|
||||
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
|
||||
custom.definition.oam.dev/multi-cluster.config.oam.dev: "true"
|
||||
custom.definition.oam.dev/type.config.oam.dev: image-registry
|
||||
name: config-image-registry
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
output: {
|
||||
apiVersion: "v1"
|
||||
kind: "Secret"
|
||||
metadata: {
|
||||
name: context.name
|
||||
namespace: context.namespace
|
||||
labels: {
|
||||
"config.oam.dev/catalog": "velacore-config"
|
||||
"config.oam.dev/type": "image-registry"
|
||||
"config.oam.dev/multi-cluster": "true"
|
||||
"config.oam.dev/identifier": parameter.registry
|
||||
"config.oam.dev/sub-type": "auth"
|
||||
}
|
||||
}
|
||||
type: "kubernetes.io/dockerconfigjson"
|
||||
stringData: {
|
||||
if parameter.auth != _|_ {
|
||||
".dockerconfigjson": json.Marshal({
|
||||
auths: "\(parameter.registry)": {
|
||||
username: parameter.auth.username
|
||||
password: parameter.auth.password
|
||||
if parameter.auth.email != _|_ {
|
||||
email: parameter.auth.email
|
||||
}
|
||||
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Image registry FQDN
|
||||
registry: string
|
||||
// +usage=Authenticate the image registry
|
||||
auth?: {
|
||||
// +usage=Private Image registry username
|
||||
username: string
|
||||
// +usage=Private Image registry password
|
||||
password: string
|
||||
// +usage=Private Image registry email
|
||||
email?: string
|
||||
}
|
||||
}
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
|
||||
308
charts/vela-minimal/templates/defwithtemplate/cron-task.yaml
Normal file
308
charts/vela-minimal/templates/defwithtemplate/cron-task.yaml
Normal file
@@ -0,0 +1,308 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/cron-task.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Describes cron jobs that run code or a script to completion.
|
||||
name: cron-task
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "batch/v1beta1"
|
||||
kind: "CronJob"
|
||||
spec: {
|
||||
schedule: parameter.schedule
|
||||
concurrencyPolicy: parameter.concurrencyPolicy
|
||||
suspend: parameter.suspend
|
||||
successfulJobsHistoryLimit: parameter.successfulJobsHistoryLimit
|
||||
failedJobsHistoryLimit: parameter.failedJobsHistoryLimit
|
||||
if parameter.startingDeadlineSeconds != _|_ {
|
||||
startingDeadlineSeconds: parameter.startingDeadlineSeconds
|
||||
}
|
||||
jobTemplate: {
|
||||
if parameter.labels != _|_ {
|
||||
metadata: labels: parameter.labels
|
||||
}
|
||||
if parameter.annotations != _|_ {
|
||||
metadata: annotations: parameter.annotations
|
||||
}
|
||||
spec: {
|
||||
parallelism: parameter.count
|
||||
completions: parameter.count
|
||||
if parameter.ttlSecondsAfterFinished != _|_ {
|
||||
ttlSecondsAfterFinished: parameter.ttlSecondsAfterFinished
|
||||
}
|
||||
if parameter.activeDeadlineSeconds != _|_ {
|
||||
activeDeadlineSeconds: parameter.activeDeadlineSeconds
|
||||
}
|
||||
backoffLimit: parameter.backoffLimit
|
||||
template: {
|
||||
if parameter.labels != _|_ {
|
||||
metadata: labels: parameter.labels
|
||||
}
|
||||
if parameter.annotations != _|_ {
|
||||
metadata: annotations: parameter.annotations
|
||||
}
|
||||
spec: {
|
||||
restartPolicy: parameter.restart
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
if parameter["imagePullPolicy"] != _|_ {
|
||||
imagePullPolicy: parameter.imagePullPolicy
|
||||
}
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
if parameter["env"] != _|_ {
|
||||
env: parameter.env
|
||||
}
|
||||
if parameter["cpu"] != _|_ {
|
||||
resources: {
|
||||
limits: cpu: parameter.cpu
|
||||
requests: cpu: parameter.cpu
|
||||
}
|
||||
}
|
||||
if parameter["memory"] != _|_ {
|
||||
resources: {
|
||||
limits: memory: parameter.memory
|
||||
requests: memory: parameter.memory
|
||||
}
|
||||
}
|
||||
if parameter["volumes"] != _|_ {
|
||||
volumeMounts: [ for v in parameter.volumes {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
}}]
|
||||
}
|
||||
}]
|
||||
if parameter["volumes"] != _|_ {
|
||||
volumes: [ for v in parameter.volumes {
|
||||
{
|
||||
name: v.name
|
||||
if v.type == "pvc" {
|
||||
persistentVolumeClaim: claimName: v.claimName
|
||||
}
|
||||
if v.type == "configMap" {
|
||||
configMap: {
|
||||
defaultMode: v.defaultMode
|
||||
name: v.cmName
|
||||
if v.items != _|_ {
|
||||
items: v.items
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.type == "secret" {
|
||||
secret: {
|
||||
defaultMode: v.defaultMode
|
||||
secretName: v.secretName
|
||||
if v.items != _|_ {
|
||||
items: v.items
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.type == "emptyDir" {
|
||||
emptyDir: medium: v.medium
|
||||
}
|
||||
}}]
|
||||
}
|
||||
if parameter["imagePullSecrets"] != _|_ {
|
||||
imagePullSecrets: [ for v in parameter.imagePullSecrets {
|
||||
name: v
|
||||
},
|
||||
]
|
||||
}
|
||||
if parameter.hostAliases != _|_ {
|
||||
hostAliases: [ for v in parameter.hostAliases {
|
||||
ip: v.ip
|
||||
hostnames: v.hostnames
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the labels in the workload
|
||||
labels?: [string]: string
|
||||
|
||||
// +usage=Specify the annotations in the workload
|
||||
annotations?: [string]: string
|
||||
|
||||
// +usage=Specify the schedule in Cron format, see https://en.wikipedia.org/wiki/Cron
|
||||
schedule: string
|
||||
|
||||
// +usage=Specify deadline in seconds for starting the job if it misses scheduled
|
||||
startingDeadlineSeconds?: int
|
||||
|
||||
// +usage=suspend subsequent executions
|
||||
suspend: *false | bool
|
||||
|
||||
// +usage=Specifies how to treat concurrent executions of a Job
|
||||
concurrencyPolicy: *"Allow" | "Allow" | "Forbid" | "Replace"
|
||||
|
||||
// +usage=The number of successful finished jobs to retain
|
||||
successfulJobsHistoryLimit: *3 | int
|
||||
|
||||
// +usage=The number of failed finished jobs to retain
|
||||
failedJobsHistoryLimit: *1 | int
|
||||
|
||||
// +usage=Specify number of tasks to run in parallel
|
||||
// +short=c
|
||||
count: *1 | int
|
||||
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
|
||||
// +usage=Specify image pull policy for your service
|
||||
imagePullPolicy?: "Always" | "Never" | "IfNotPresent"
|
||||
|
||||
// +usage=Specify image pull secrets for your service
|
||||
imagePullSecrets?: [...string]
|
||||
|
||||
// +usage=Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never.
|
||||
restart: *"Never" | string
|
||||
|
||||
// +usage=Commands to run in the container
|
||||
cmd?: [...string]
|
||||
|
||||
// +usage=Define arguments by using environment variables
|
||||
env?: [...{
|
||||
// +usage=Environment variable name
|
||||
name: string
|
||||
// +usage=The value of the environment variable
|
||||
value?: string
|
||||
// +usage=Specifies a source the value of this var should come from
|
||||
valueFrom?: {
|
||||
// +usage=Selects a key of a secret in the pod's namespace
|
||||
secretKeyRef: {
|
||||
// +usage=The name of the secret in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the secret to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=Number of CPU units for the service, like `0.5` (0.5 CPU core), `1` (1 CPU core)
|
||||
cpu?: string
|
||||
|
||||
// +usage=Specifies the attributes of the memory resource required for the container.
|
||||
memory?: string
|
||||
|
||||
// +usage=Declare volumes and volumeMounts
|
||||
volumes?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
// +usage=Specify volume type, options: "pvc","configMap","secret","emptyDir"
|
||||
type: "pvc" | "configMap" | "secret" | "emptyDir"
|
||||
if type == "pvc" {
|
||||
claimName: string
|
||||
}
|
||||
if type == "configMap" {
|
||||
defaultMode: *420 | int
|
||||
cmName: string
|
||||
items?: [...{
|
||||
key: string
|
||||
path: string
|
||||
mode: *511 | int
|
||||
}]
|
||||
}
|
||||
if type == "secret" {
|
||||
defaultMode: *420 | int
|
||||
secretName: string
|
||||
items?: [...{
|
||||
key: string
|
||||
path: string
|
||||
mode: *511 | int
|
||||
}]
|
||||
}
|
||||
if type == "emptyDir" {
|
||||
medium: *"" | "Memory"
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=An optional list of hosts and IPs that will be injected into the pod's hosts file
|
||||
hostAliases?: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
|
||||
// +usage=Limits the lifetime of a Job that has finished
|
||||
ttlSecondsAfterFinished?: int
|
||||
|
||||
// +usage=The duration in seconds relative to the startTime that the job may be continuously active before the system tries to terminate it
|
||||
activeDeadlineSeconds?: int
|
||||
|
||||
// +usage=The number of retries before marking this job failed
|
||||
backoffLimit: *6 | int
|
||||
|
||||
// +usage=Instructions for assessing whether the container is alive.
|
||||
livenessProbe?: #HealthProbe
|
||||
|
||||
// +usage=Instructions for assessing whether the container is in a suitable state to serve traffic.
|
||||
readinessProbe?: #HealthProbe
|
||||
}
|
||||
#HealthProbe: {
|
||||
|
||||
// +usage=Instructions for assessing container health by executing a command. Either this attribute or the httpGet attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the httpGet attribute and the tcpSocket attribute.
|
||||
exec?: {
|
||||
// +usage=A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures.
|
||||
command: [...string]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by executing an HTTP GET request. Either this attribute or the exec attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the tcpSocket attribute.
|
||||
httpGet?: {
|
||||
// +usage=The endpoint, relative to the port, to which the HTTP GET request should be directed.
|
||||
path: string
|
||||
// +usage=The TCP socket within the container to which the HTTP GET request should be directed.
|
||||
port: int
|
||||
httpHeaders?: [...{
|
||||
name: string
|
||||
value: string
|
||||
}]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by probing a TCP socket. Either this attribute or the exec attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the httpGet attribute.
|
||||
tcpSocket?: {
|
||||
// +usage=The TCP socket within the container that should be probed to assess container health.
|
||||
port: int
|
||||
}
|
||||
|
||||
// +usage=Number of seconds after the container is started before the first probe is initiated.
|
||||
initialDelaySeconds: *0 | int
|
||||
|
||||
// +usage=How often, in seconds, to execute the probe.
|
||||
periodSeconds: *10 | int
|
||||
|
||||
// +usage=Number of seconds after which the probe times out.
|
||||
timeoutSeconds: *1 | int
|
||||
|
||||
// +usage=Minimum consecutive successes for the probe to be considered successful after having failed.
|
||||
successThreshold: *1 | int
|
||||
|
||||
// +usage=Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).
|
||||
failureThreshold: *3 | int
|
||||
}
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
type: cronjobs.batch
|
||||
|
||||
@@ -453,6 +453,12 @@ spec:
|
||||
|
||||
// +usage=Instructions for assessing whether the container is in a suitable state to serve traffic.
|
||||
readinessProbe?: #HealthProbe
|
||||
|
||||
// +usage=Specify the hostAliases to add
|
||||
hostAliases: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
}
|
||||
#HealthProbe: {
|
||||
|
||||
@@ -494,12 +500,6 @@ spec:
|
||||
|
||||
// +usage=Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).
|
||||
failureThreshold: *3 | int
|
||||
|
||||
// +usage=Specify the hostAliases to add
|
||||
hostAliases: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
|
||||
@@ -5,7 +5,7 @@ This guide helps you get started developing KubeVela.
|
||||
## Prerequisites
|
||||
|
||||
1. Golang version 1.17+
|
||||
2. Kubernetes version v1.18+ with `~/.kube/config` configured.
|
||||
2. Kubernetes version v1.20+ with `~/.kube/config` configured.
|
||||
3. ginkgo 1.14.0+ (just for [E2E test](./developer-guide.md#e2e-test))
|
||||
4. golangci-lint 1.38.0+, it will install automatically if you run `make`, you can [install it manually](https://golangci-lint.run/usage/install/#local-installation) if the installation is too slow.
|
||||
5. kubebuilder v3.1.0+ and you need to manually install the dependency tools for unit test.
|
||||
@@ -177,7 +177,7 @@ To execute the e2e test of the API module, the mongodb service needs to exist lo
|
||||
# save your config
|
||||
mv ~/.kube/config ~/.kube/config.save
|
||||
|
||||
kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4 --name worker
|
||||
kind create cluster --image kindest/node:v1.20.7@sha256:688fba5ce6b825be62a7c7fe1415b35da2bdfbb5a69227c499ea4cc0008661ca --name worker
|
||||
kind get kubeconfig --name worker --internal > /tmp/worker.kubeconfig
|
||||
kind get kubeconfig --name worker > /tmp/worker.client.kubeconfig
|
||||
|
||||
|
||||
@@ -4304,6 +4304,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/repository/chart_repos": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/xml",
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/xml"
|
||||
],
|
||||
"tags": [
|
||||
"repository",
|
||||
"helm"
|
||||
],
|
||||
"summary": "list chart repo",
|
||||
"operationId": "listRepo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "the config project",
|
||||
"name": "project",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/bcode.Bcode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/repository/charts": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
@@ -4326,6 +4369,12 @@
|
||||
"description": "helm repository url",
|
||||
"name": "repoUrl",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "secret of the repo",
|
||||
"name": "secretName",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -4369,6 +4418,12 @@
|
||||
"description": "helm repository url",
|
||||
"name": "repoUrl",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "secret of the repo",
|
||||
"name": "secretName",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -4409,6 +4464,12 @@
|
||||
"description": "helm repository url",
|
||||
"name": "repoUrl",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "secret of the repo",
|
||||
"name": "secretName",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -7148,10 +7209,10 @@
|
||||
},
|
||||
"v1.ApplicationDeployResponse": {
|
||||
"required": [
|
||||
"createTime",
|
||||
"version",
|
||||
"note",
|
||||
"status",
|
||||
"note",
|
||||
"createTime",
|
||||
"envName",
|
||||
"triggerType"
|
||||
],
|
||||
@@ -8244,10 +8305,10 @@
|
||||
"v1.DetailAddonResponse": {
|
||||
"required": [
|
||||
"name",
|
||||
"icon",
|
||||
"invisible",
|
||||
"version",
|
||||
"description",
|
||||
"icon",
|
||||
"version",
|
||||
"invisible",
|
||||
"schema",
|
||||
"uiSchema",
|
||||
"definitions",
|
||||
@@ -8328,12 +8389,12 @@
|
||||
"v1.DetailApplicationResponse": {
|
||||
"required": [
|
||||
"alias",
|
||||
"description",
|
||||
"createTime",
|
||||
"name",
|
||||
"project",
|
||||
"updateTime",
|
||||
"description",
|
||||
"icon",
|
||||
"name",
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"policies",
|
||||
"envBindings",
|
||||
"applicationType",
|
||||
@@ -8394,20 +8455,20 @@
|
||||
},
|
||||
"v1.DetailClusterResponse": {
|
||||
"required": [
|
||||
"updateTime",
|
||||
"kubeConfig",
|
||||
"name",
|
||||
"icon",
|
||||
"reason",
|
||||
"provider",
|
||||
"status",
|
||||
"apiServerURL",
|
||||
"dashboardURL",
|
||||
"createTime",
|
||||
"kubeConfigSecret",
|
||||
"icon",
|
||||
"labels",
|
||||
"kubeConfig",
|
||||
"updateTime",
|
||||
"name",
|
||||
"alias",
|
||||
"description",
|
||||
"labels",
|
||||
"status",
|
||||
"kubeConfigSecret",
|
||||
"reason",
|
||||
"createTime",
|
||||
"provider",
|
||||
"resourceInfo"
|
||||
],
|
||||
"properties": {
|
||||
@@ -8465,14 +8526,14 @@
|
||||
},
|
||||
"v1.DetailComponentResponse": {
|
||||
"required": [
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"appPrimaryKey",
|
||||
"type",
|
||||
"main",
|
||||
"name",
|
||||
"creator",
|
||||
"alias",
|
||||
"name",
|
||||
"main",
|
||||
"createTime",
|
||||
"type",
|
||||
"definition"
|
||||
],
|
||||
"properties": {
|
||||
@@ -8574,13 +8635,13 @@
|
||||
},
|
||||
"v1.DetailPolicyResponse": {
|
||||
"required": [
|
||||
"type",
|
||||
"description",
|
||||
"creator",
|
||||
"properties",
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"name",
|
||||
"type"
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"createTime": {
|
||||
@@ -8610,17 +8671,17 @@
|
||||
},
|
||||
"v1.DetailRevisionResponse": {
|
||||
"required": [
|
||||
"envName",
|
||||
"reason",
|
||||
"deployUser",
|
||||
"updateTime",
|
||||
"status",
|
||||
"note",
|
||||
"triggerType",
|
||||
"workflowName",
|
||||
"version",
|
||||
"deployUser",
|
||||
"createTime",
|
||||
"status",
|
||||
"workflowName",
|
||||
"envName",
|
||||
"appPrimaryKey",
|
||||
"version"
|
||||
"reason",
|
||||
"note"
|
||||
],
|
||||
"properties": {
|
||||
"appPrimaryKey": {
|
||||
@@ -8674,9 +8735,9 @@
|
||||
},
|
||||
"v1.DetailTargetResponse": {
|
||||
"required": [
|
||||
"name",
|
||||
"createTime",
|
||||
"project",
|
||||
"createTime",
|
||||
"name",
|
||||
"updateTime"
|
||||
],
|
||||
"properties": {
|
||||
@@ -8717,11 +8778,11 @@
|
||||
},
|
||||
"v1.DetailUserResponse": {
|
||||
"required": [
|
||||
"name",
|
||||
"email",
|
||||
"disabled",
|
||||
"createTime",
|
||||
"lastLoginTime",
|
||||
"name",
|
||||
"email",
|
||||
"projects",
|
||||
"roles"
|
||||
],
|
||||
@@ -8762,12 +8823,12 @@
|
||||
},
|
||||
"v1.DetailWorkflowRecordResponse": {
|
||||
"required": [
|
||||
"workflowName",
|
||||
"workflowAlias",
|
||||
"applicationRevision",
|
||||
"status",
|
||||
"name",
|
||||
"namespace",
|
||||
"workflowName",
|
||||
"workflowAlias",
|
||||
"applicationRevision",
|
||||
"deployTime",
|
||||
"deployUser",
|
||||
"note",
|
||||
@@ -8819,14 +8880,14 @@
|
||||
},
|
||||
"v1.DetailWorkflowResponse": {
|
||||
"required": [
|
||||
"description",
|
||||
"enable",
|
||||
"envName",
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"name",
|
||||
"alias",
|
||||
"enable",
|
||||
"default",
|
||||
"envName"
|
||||
"description",
|
||||
"name",
|
||||
"alias"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
@@ -9021,8 +9082,8 @@
|
||||
},
|
||||
"v1.EnvBindingTarget": {
|
||||
"required": [
|
||||
"name",
|
||||
"alias"
|
||||
"alias",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"alias": {
|
||||
@@ -9405,11 +9466,11 @@
|
||||
},
|
||||
"v1.LoginUserInfoResponse": {
|
||||
"required": [
|
||||
"name",
|
||||
"email",
|
||||
"disabled",
|
||||
"createTime",
|
||||
"lastLoginTime",
|
||||
"name",
|
||||
"email",
|
||||
"projects",
|
||||
"platformPermissions",
|
||||
"projectPermissions"
|
||||
@@ -9733,11 +9794,11 @@
|
||||
},
|
||||
"v1.SystemInfoResponse": {
|
||||
"required": [
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"installID",
|
||||
"enableCollection",
|
||||
"loginType",
|
||||
"createTime",
|
||||
"updateTime",
|
||||
"systemVersion"
|
||||
],
|
||||
"properties": {
|
||||
|
||||
@@ -3,6 +3,12 @@ kind: Application
|
||||
metadata:
|
||||
name: config-dex-connector-dev
|
||||
namespace: vela-system
|
||||
labels:
|
||||
"app.oam.dev/source-of-truth": "from-inner-system"
|
||||
"config.oam.dev/catalog": "velacore-config"
|
||||
"config.oam.dev/type": "config-dex-connector"
|
||||
"config.oam.dev/sub-type": "github"
|
||||
project: abc
|
||||
spec:
|
||||
components:
|
||||
- name: dev
|
||||
@@ -12,4 +18,4 @@ spec:
|
||||
github:
|
||||
clientID: "aa"
|
||||
clientSecret: "bb"
|
||||
callbackURL: "http://localhost:8080/callback"
|
||||
redirectURI: "http://localhost:8080/callback"
|
||||
@@ -1,11 +1,16 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: config-image-registry-account-auth-dev
|
||||
name: image-dev
|
||||
namespace: vela-system
|
||||
labels:
|
||||
"app.oam.dev/source-of-truth": "from-inner-system"
|
||||
"config.oam.dev/catalog": "velacore-config"
|
||||
"config.oam.dev/type": "config-image-registry"
|
||||
project: abc
|
||||
spec:
|
||||
components:
|
||||
- name: account-auth
|
||||
- name: image-dev
|
||||
type: config-image-registry
|
||||
properties:
|
||||
registry: "registry.cn-beijing.aliyuncs.com"
|
||||
|
||||
@@ -12,12 +12,11 @@ spec:
|
||||
type: ref-objects
|
||||
properties:
|
||||
objects:
|
||||
- apiVersion: v1
|
||||
kind: Secret
|
||||
name: image-registry-dev
|
||||
- name: reg-demo
|
||||
resource: secret
|
||||
policies:
|
||||
- type: topology
|
||||
name: dev
|
||||
properties:
|
||||
clusters: ["bj"]
|
||||
# namespaces: ["ns1"]
|
||||
namespace: default
|
||||
|
||||
1
go.mod
1
go.mod
@@ -145,6 +145,7 @@ require (
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
|
||||
github.com/emicklei/proto v1.6.15 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
|
||||
3
go.sum
3
go.sum
@@ -420,8 +420,9 @@ github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
|
||||
@@ -30,9 +30,38 @@ const (
|
||||
// SystemInfo systemInfo model
|
||||
type SystemInfo struct {
|
||||
BaseModel
|
||||
InstallID string `json:"installID"`
|
||||
EnableCollection bool `json:"enableCollection"`
|
||||
LoginType string `json:"loginType"`
|
||||
InstallID string `json:"installID"`
|
||||
EnableCollection bool `json:"enableCollection"`
|
||||
LoginType string `json:"loginType"`
|
||||
DexConfig DexConfig `json:"dexConfig,omitempty"`
|
||||
}
|
||||
|
||||
// DexConfig dex config
|
||||
type DexConfig struct {
|
||||
Issuer string `json:"issuer"`
|
||||
Web DexWeb `json:"web"`
|
||||
Storage DexStorage `json:"storage"`
|
||||
StaticClients []DexStaticClient `json:"staticClients"`
|
||||
Connectors []interface{} `json:"connectors,omitempty"`
|
||||
EnablePasswordDB bool `json:"enablePasswordDB"`
|
||||
}
|
||||
|
||||
// DexStorage dex storage
|
||||
type DexStorage struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// DexWeb dex web
|
||||
type DexWeb struct {
|
||||
HTTP string `json:"http"`
|
||||
}
|
||||
|
||||
// DexStaticClient dex static client
|
||||
type DexStaticClient struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Secret string `json:"secret"`
|
||||
RedirectURIs []string `json:"redirectURIs"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
|
||||
@@ -184,6 +184,25 @@ type AddonArgsResponse struct {
|
||||
Args map[string]string `json:"args"`
|
||||
}
|
||||
|
||||
// ConfigType define the format for listing configuration types
|
||||
type ConfigType struct {
|
||||
Definitions []string `json:"definitions"`
|
||||
Alias string `json:"alias"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// Config define the metadata of a config
|
||||
type Config struct {
|
||||
ConfigType string `json:"configType"`
|
||||
Name string `json:"name"`
|
||||
Project string `json:"project"`
|
||||
Identifier string `json:"identifier"`
|
||||
Description string `json:"description"`
|
||||
CreatedTime *time.Time `json:"createdTime"`
|
||||
UpdatedTime *time.Time `json:"updatedTime"`
|
||||
}
|
||||
|
||||
// AccessKeyRequest request parameters to access cloud provider
|
||||
type AccessKeyRequest struct {
|
||||
AccessKeyID string `json:"accessKeyID"`
|
||||
@@ -382,6 +401,15 @@ type CreateApplicationRequest struct {
|
||||
Component *CreateComponentRequest `json:"component"`
|
||||
}
|
||||
|
||||
// CreateConfigRequest is the request body to creates a config
|
||||
type CreateConfigRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias"`
|
||||
Project string `json:"project"`
|
||||
ComponentType string `json:"componentType" validate:"checkname"`
|
||||
Properties string `json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateApplicationRequest update application base config
|
||||
type UpdateApplicationRequest struct {
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
@@ -1081,14 +1109,22 @@ type DetailRevisionResponse struct {
|
||||
|
||||
// SystemInfoResponse get SystemInfo
|
||||
type SystemInfoResponse struct {
|
||||
model.SystemInfo
|
||||
SystemInfo
|
||||
SystemVersion SystemVersion `json:"systemVersion"`
|
||||
}
|
||||
|
||||
// SystemInfo system info
|
||||
type SystemInfo struct {
|
||||
InstallID string `json:"installID"`
|
||||
EnableCollection bool `json:"enableCollection"`
|
||||
LoginType string `json:"loginType"`
|
||||
}
|
||||
|
||||
// SystemInfoRequest request by update SystemInfo
|
||||
type SystemInfoRequest struct {
|
||||
EnableCollection bool `json:"enableCollection"`
|
||||
LoginType string `json:"loginType"`
|
||||
VelaAddress string `json:"velaAddress,omitempty"`
|
||||
}
|
||||
|
||||
// SystemVersion contains KubeVela version
|
||||
@@ -1277,3 +1313,14 @@ type LoginUserInfoResponse struct {
|
||||
PlatformPermissions []PermissionBase `json:"platformPermissions"`
|
||||
ProjectPermissions map[string][]PermissionBase `json:"projectPermissions"`
|
||||
}
|
||||
|
||||
// ChartRepoResponse the response body of chart repo
|
||||
type ChartRepoResponse struct {
|
||||
URL string `json:"url"`
|
||||
SecretName string `json:"secretName"`
|
||||
}
|
||||
|
||||
// ChartRepoResponseList the response body of list chart repo
|
||||
type ChartRepoResponseList struct {
|
||||
ChartRepoResponse []*ChartRepoResponse `json:"repos"`
|
||||
}
|
||||
|
||||
@@ -686,7 +686,7 @@ func (c *applicationUsecaseImpl) DetailPolicy(ctx context.Context, app *model.Ap
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Deploy deploy app to cluster
|
||||
// Deploy deploys app to cluster
|
||||
// means to render oam application config and apply to cluster.
|
||||
// An event record is generated for each deploy.
|
||||
func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Application, req apisv1.ApplicationDeployRequest) (*apisv1.ApplicationDeployResponse, error) {
|
||||
@@ -704,6 +704,23 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// sync configs to clusters
|
||||
// TODO(zzxwill) need to check the type of the componentDefinition, if it is `Cloud`, skip the sync
|
||||
targets, err := listTarget(ctx, c.ds, app.Project, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var clusterTargets []*model.ClusterTarget
|
||||
for i, t := range targets {
|
||||
if t.Cluster != nil {
|
||||
clusterTargets = append(clusterTargets, targets[i].Cluster)
|
||||
}
|
||||
}
|
||||
|
||||
if err := SyncConfigs(ctx, c.kubeClient, app.Project, clusterTargets); err != nil {
|
||||
return nil, fmt.Errorf("sync config failure %w", err)
|
||||
}
|
||||
|
||||
// step2: check and create deploy event
|
||||
if !req.Force {
|
||||
var lastVersion = model.ApplicationRevision{
|
||||
|
||||
@@ -18,6 +18,7 @@ package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -25,26 +26,30 @@ import (
|
||||
"github.com/coreos/go-oidc"
|
||||
"github.com/form3tech-oss/jwt-go"
|
||||
"golang.org/x/oauth2"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
const (
|
||||
secretDexConfig = "dex-config"
|
||||
keyDex = "dex"
|
||||
dexConfigName = "dex-config"
|
||||
secretDexConfigKey = "config.yaml"
|
||||
dexAddonName = "addon-dex"
|
||||
jwtIssuer = "vela-issuer"
|
||||
signedKey = "vela-singned"
|
||||
|
||||
// GrantTypeAccess is the grant type for access token
|
||||
GrantTypeAccess = "access"
|
||||
@@ -52,12 +57,15 @@ const (
|
||||
GrantTypeRefresh = "refresh"
|
||||
)
|
||||
|
||||
var signedKey = ""
|
||||
|
||||
// AuthenticationUsecase is the usecase of authentication
|
||||
type AuthenticationUsecase interface {
|
||||
Login(ctx context.Context, loginReq apisv1.LoginRequest) (*apisv1.LoginResponse, error)
|
||||
RefreshToken(ctx context.Context, refreshToken string) (*apisv1.RefreshTokenResponse, error)
|
||||
GetDexConfig(ctx context.Context) (*apisv1.DexConfigResponse, error)
|
||||
GetLoginType(ctx context.Context) (*apisv1.GetLoginTypeResponse, error)
|
||||
UpdateDexConfig(ctx context.Context) error
|
||||
}
|
||||
|
||||
type authenticationUsecaseImpl struct {
|
||||
@@ -148,7 +156,7 @@ func (a *authenticationUsecaseImpl) newLocalHandler(req apisv1.LoginRequest) (*l
|
||||
func (a *authenticationUsecaseImpl) Login(ctx context.Context, loginReq apisv1.LoginRequest) (*apisv1.LoginResponse, error) {
|
||||
var handler authHandler
|
||||
var err error
|
||||
sysInfo, err := a.sysUsecase.GetSystemInfo(ctx)
|
||||
sysInfo, err := a.sysUsecase.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -175,11 +183,11 @@ func (a *authenticationUsecaseImpl) Login(ctx context.Context, loginReq apisv1.L
|
||||
if userBase.Disabled {
|
||||
return nil, bcode.ErrUserAlreadyDisabled
|
||||
}
|
||||
accessToken, err := a.generateJWTToken(userBase.Name, GrantTypeAccess, time.Hour)
|
||||
accessToken, err := a.generateJWTToken(ctx, userBase.Name, GrantTypeAccess, time.Hour)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refreshToken, err := a.generateJWTToken(userBase.Name, GrantTypeRefresh, time.Hour*24)
|
||||
refreshToken, err := a.generateJWTToken(ctx, userBase.Name, GrantTypeRefresh, time.Hour*24)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -190,7 +198,7 @@ func (a *authenticationUsecaseImpl) Login(ctx context.Context, loginReq apisv1.L
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *authenticationUsecaseImpl) generateJWTToken(username, grantType string, expireDuration time.Duration) (string, error) {
|
||||
func (a *authenticationUsecaseImpl) generateJWTToken(ctx context.Context, username, grantType string, expireDuration time.Duration) (string, error) {
|
||||
expire := time.Now().Add(expireDuration)
|
||||
claims := model.CustomClaims{
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
@@ -202,8 +210,24 @@ func (a *authenticationUsecaseImpl) generateJWTToken(username, grantType string,
|
||||
GrantType: grantType,
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
signed, err := a.getSignedKey(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token.SignedString([]byte(signed))
|
||||
}
|
||||
|
||||
return token.SignedString([]byte(signedKey))
|
||||
func (a *authenticationUsecaseImpl) getSignedKey(ctx context.Context) (string, error) {
|
||||
if signedKey != "" {
|
||||
return signedKey, nil
|
||||
}
|
||||
info, err := a.sysUsecase.Get(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
signedKey = info.InstallID
|
||||
|
||||
return signedKey, nil
|
||||
}
|
||||
|
||||
func (a *authenticationUsecaseImpl) RefreshToken(ctx context.Context, refreshToken string) (*apisv1.RefreshTokenResponse, error) {
|
||||
@@ -212,7 +236,7 @@ func (a *authenticationUsecaseImpl) RefreshToken(ctx context.Context, refreshTok
|
||||
return nil, err
|
||||
}
|
||||
if claim.GrantType == GrantTypeRefresh {
|
||||
accessToken, err := a.generateJWTToken(claim.Username, GrantTypeAccess, time.Hour)
|
||||
accessToken, err := a.generateJWTToken(ctx, claim.Username, GrantTypeAccess, time.Hour)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -253,33 +277,10 @@ func ParseToken(tokenString string) (*model.CustomClaims, error) {
|
||||
}
|
||||
|
||||
func (a *authenticationUsecaseImpl) GetDexConfig(ctx context.Context) (*apisv1.DexConfigResponse, error) {
|
||||
secret := &v1.Secret{}
|
||||
if err := a.kubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: secretDexConfig,
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
}, secret); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, bcode.ErrDexConfigNotFound
|
||||
}
|
||||
log.Logger.Errorf("failed to get dex config: %s", err.Error())
|
||||
config, err := getDexConfig(ctx, a.kubeClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var config struct {
|
||||
Issuer string `json:"issuer"`
|
||||
StaticClients []struct {
|
||||
ID string `json:"id"`
|
||||
Secret string `json:"secret"`
|
||||
RedirectURIs []string `json:"redirectURIs"`
|
||||
} `json:"staticClients"`
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(secret.Data[secretDexConfigKey], &config); err != nil {
|
||||
log.Logger.Errorf("failed to unmarshal dex config: %s", err.Error())
|
||||
return nil, bcode.ErrInvalidDexConfig
|
||||
}
|
||||
if len(config.StaticClients) < 1 || len(config.StaticClients[0].RedirectURIs) < 1 {
|
||||
return nil, bcode.ErrInvalidDexConfig
|
||||
}
|
||||
return &apisv1.DexConfigResponse{
|
||||
Issuer: config.Issuer,
|
||||
ClientID: config.StaticClients[0].ID,
|
||||
@@ -288,8 +289,121 @@ func (a *authenticationUsecaseImpl) GetDexConfig(ctx context.Context) (*apisv1.D
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *authenticationUsecaseImpl) UpdateDexConfig(ctx context.Context) error {
|
||||
connectors, err := utils.GetDexConnectors(ctx, a.kubeClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dexConfig := &corev1.Secret{}
|
||||
if err := a.kubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: dexConfigName,
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
}, dexConfig); err != nil {
|
||||
if !kerrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
config := &model.JSONStruct{}
|
||||
if dexConfig == nil || dexConfig.Data == nil {
|
||||
(*config)["connectors"] = connectors
|
||||
c, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.kubeClient.Create(ctx, &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: dexConfigName,
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: map[string][]byte{
|
||||
secretDexConfigKey: c,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = yaml.Unmarshal(dexConfig.Data[secretDexConfigKey], config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
(*config)["connectors"] = connectors
|
||||
c, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dexConfig.Data[secretDexConfigKey] = c
|
||||
if err := a.kubeClient.Update(ctx, dexConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return restartDex(ctx, a.kubeClient)
|
||||
}
|
||||
|
||||
func restartDex(ctx context.Context, kubeClient client.Client) error {
|
||||
dexApp := &v1beta1.Application{}
|
||||
if err := kubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: dexAddonName,
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
}, dexApp); err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
return bcode.ErrDexNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
for i, comp := range dexApp.Spec.Components {
|
||||
if comp.Name == keyDex {
|
||||
var v model.JSONStruct
|
||||
err := json.Unmarshal(comp.Properties.Raw, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// restart the dex server
|
||||
if _, ok := v["values"]; ok {
|
||||
v["values"].(map[string]interface{})["env"] = map[string]string{
|
||||
"TIME_STAMP": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
dexApp.Spec.Components[i].Properties = v.RawExtension()
|
||||
if err := kubeClient.Update(ctx, dexApp); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDexConfig(ctx context.Context, kubeClient client.Client) (*model.DexConfig, error) {
|
||||
dexConfigSecret := &corev1.Secret{}
|
||||
if err := kubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: dexConfigName,
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
}, dexConfigSecret); err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
return nil, bcode.ErrDexConfigNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if dexConfigSecret.Data == nil {
|
||||
return nil, bcode.ErrInvalidDexConfig
|
||||
}
|
||||
|
||||
config := &model.DexConfig{}
|
||||
if err := yaml.Unmarshal(dexConfigSecret.Data[secretDexConfigKey], config); err != nil {
|
||||
log.Logger.Errorf("failed to unmarshal dex config: %s", err.Error())
|
||||
return nil, bcode.ErrInvalidDexConfig
|
||||
}
|
||||
if len(config.StaticClients) < 1 || len(config.StaticClients[0].RedirectURIs) < 1 {
|
||||
return nil, bcode.ErrInvalidDexConfig
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (a *authenticationUsecaseImpl) GetLoginType(ctx context.Context) (*apisv1.GetLoginTypeResponse, error) {
|
||||
sysInfo, err := a.sysUsecase.GetSystemInfo(ctx)
|
||||
sysInfo, err := a.sysUsecase.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package usecase
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -30,10 +31,16 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"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/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
@@ -51,7 +58,7 @@ var _ = Describe("Test authentication usecase functions", func() {
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
authUsecase = &authenticationUsecaseImpl{kubeClient: k8sClient, ds: ds}
|
||||
sysUsecase = &systemInfoUsecaseImpl{ds: ds}
|
||||
sysUsecase = &systemInfoUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
userUsecase = &userUsecaseImpl{ds: ds, sysUsecase: sysUsecase}
|
||||
})
|
||||
It("Test Dex login", func() {
|
||||
@@ -99,37 +106,85 @@ var _ = Describe("Test authentication usecase functions", func() {
|
||||
Expect(resp.Name).Should(Equal("test-login"))
|
||||
})
|
||||
|
||||
It("Test get dex config", func() {
|
||||
It("Test update dex config", func() {
|
||||
err := k8sClient.Create(context.Background(), &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vela-system",
|
||||
},
|
||||
})
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
err = k8sClient.Create(context.Background(), &corev1.Secret{
|
||||
webserver, err := ioutil.ReadFile("./testdata/dex-config-def.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
var cd v1beta1.ComponentDefinition
|
||||
err = yaml.Unmarshal(webserver, &cd)
|
||||
Expect(err).Should(Succeed())
|
||||
err = k8sClient.Create(context.Background(), &cd)
|
||||
Expect(err).Should(Succeed())
|
||||
err = k8sClient.Create(context.Background(), &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretDexConfig,
|
||||
Name: "addon-dex",
|
||||
Namespace: "vela-system",
|
||||
},
|
||||
StringData: map[string]string{
|
||||
secretDexConfigKey: `
|
||||
issuer: https://dex.oam.dev
|
||||
staticClients:
|
||||
- id: client-id
|
||||
secret: client-secret
|
||||
redirectURIs:
|
||||
- http://localhost:8080/auth/callback
|
||||
`,
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: "dex",
|
||||
// only for test here
|
||||
Type: "dex-config",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"values":{"test":"test"}}`)},
|
||||
Traits: []common.ApplicationTrait{},
|
||||
Scopes: map[string]string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
config, err := authUsecase.GetDexConfig(context.Background())
|
||||
err = k8sClient.Create(context.Background(), &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
Namespace: "vela-system",
|
||||
Labels: map[string]string{
|
||||
"app.oam.dev/source-of-truth": "from-inner-system",
|
||||
"config.oam.dev/catalog": "velacore-config",
|
||||
"config.oam.dev/type": "config-dex-connector",
|
||||
"config.oam.dev/sub-type": "ldap",
|
||||
"project": "abc",
|
||||
},
|
||||
},
|
||||
StringData: map[string]string{
|
||||
"ldap": `{"clientID":"clientID","clientSecret":"clientSecret"}`,
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
By("try to update dex config without config secret")
|
||||
err = authUsecase.UpdateDexConfig(context.Background())
|
||||
Expect(err).Should(BeNil())
|
||||
dexConfigSecret := &corev1.Secret{}
|
||||
err = k8sClient.Get(context.Background(), types.NamespacedName{Name: "dex-config", Namespace: "vela-system"}, dexConfigSecret)
|
||||
Expect(err).Should(BeNil())
|
||||
config := &model.DexConfig{}
|
||||
err = yaml.Unmarshal(dexConfigSecret.Data[secretDexConfigKey], config)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(config.Connectors)).Should(Equal(1))
|
||||
By("try to update dex config with config secret")
|
||||
err = authUsecase.UpdateDexConfig(context.Background())
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(config.Issuer).Should(Equal("https://dex.oam.dev"))
|
||||
Expect(config.ClientID).Should(Equal("client-id"))
|
||||
Expect(config.ClientSecret).Should(Equal("client-secret"))
|
||||
Expect(config.RedirectURL).Should(Equal("http://localhost:8080/auth/callback"))
|
||||
})
|
||||
|
||||
It("Test get dex config", func() {
|
||||
_, err := authUsecase.GetDexConfig(context.Background())
|
||||
Expect(err).Should(Equal(bcode.ErrInvalidDexConfig))
|
||||
_, err = sysUsecase.UpdateSystemInfo(context.Background(), apisv1.SystemInfoRequest{
|
||||
LoginType: model.LoginTypeDex,
|
||||
VelaAddress: "http://velaux.com",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
config, err := authUsecase.GetDexConfig(context.Background())
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(config.Issuer).Should(Equal("http://velaux.com/dex"))
|
||||
Expect(config.ClientID).Should(Equal("velaux"))
|
||||
Expect(config.ClientSecret).Should(Equal("velaux-secret"))
|
||||
Expect(config.RedirectURL).Should(Equal("http://velaux.com/callback"))
|
||||
})
|
||||
})
|
||||
|
||||
393
pkg/apiserver/rest/usecase/config.go
Normal file
393
pkg/apiserver/rest/usecase/config.go
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"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/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/definition"
|
||||
)
|
||||
|
||||
const (
|
||||
definitionAlias = definition.UserPrefix + "alias.config.oam.dev"
|
||||
definitionType = definition.UserPrefix + "type.config.oam.dev"
|
||||
|
||||
velaCoreConfig = "velacore-config"
|
||||
)
|
||||
|
||||
// ConfigHandler handle CRUD of configs
|
||||
type ConfigHandler interface {
|
||||
ListConfigTypes(ctx context.Context, query string) ([]*apis.ConfigType, error)
|
||||
GetConfigType(ctx context.Context, configType string) (*apis.ConfigType, error)
|
||||
CreateConfig(ctx context.Context, req apis.CreateConfigRequest) error
|
||||
GetConfigs(ctx context.Context, configType string) ([]*apis.Config, error)
|
||||
GetConfig(ctx context.Context, configType, name string) (*apis.Config, error)
|
||||
DeleteConfig(ctx context.Context, configType, name string) error
|
||||
}
|
||||
|
||||
// NewConfigUseCase returns a config use case
|
||||
func NewConfigUseCase(authenticationUseCase AuthenticationUsecase) ConfigHandler {
|
||||
k8sClient, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &configUseCaseImpl{
|
||||
authenticationUseCase: authenticationUseCase,
|
||||
kubeClient: k8sClient,
|
||||
}
|
||||
}
|
||||
|
||||
type configUseCaseImpl struct {
|
||||
kubeClient client.Client
|
||||
authenticationUseCase AuthenticationUsecase
|
||||
}
|
||||
|
||||
// ListConfigTypes returns all config types
|
||||
func (u *configUseCaseImpl) ListConfigTypes(ctx context.Context, query string) ([]*apis.ConfigType, error) {
|
||||
defs := &v1beta1.ComponentDefinitionList{}
|
||||
if err := u.kubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tfDefs []v1beta1.ComponentDefinition
|
||||
var configTypes []*apis.ConfigType
|
||||
|
||||
for _, d := range defs.Items {
|
||||
if d.Labels[definitionType] == types.TerraformProvider {
|
||||
tfDefs = append(tfDefs, d)
|
||||
continue
|
||||
}
|
||||
configTypes = append(configTypes, &apis.ConfigType{
|
||||
Alias: d.Annotations[definitionAlias],
|
||||
Name: d.Name,
|
||||
Definitions: []string{d.Name},
|
||||
Description: d.Annotations[types.AnnoDefinitionDescription],
|
||||
})
|
||||
}
|
||||
|
||||
tfType := &apis.ConfigType{
|
||||
Alias: "Terraform Cloud Provider",
|
||||
Name: types.TerraformProvider,
|
||||
}
|
||||
definitions := make([]string, len(tfDefs))
|
||||
|
||||
for i, tf := range tfDefs {
|
||||
definitions[i] = tf.Name
|
||||
}
|
||||
tfType.Definitions = definitions
|
||||
|
||||
return append(configTypes, tfType), nil
|
||||
}
|
||||
|
||||
// GetConfigType returns a config type
|
||||
func (u *configUseCaseImpl) GetConfigType(ctx context.Context, configType string) (*apis.ConfigType, error) {
|
||||
d := &v1beta1.ComponentDefinition{}
|
||||
if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: configType}, d); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get config type")
|
||||
}
|
||||
|
||||
t := &apis.ConfigType{
|
||||
Alias: d.Annotations[definitionAlias],
|
||||
Name: configType,
|
||||
Description: d.Annotations[types.AnnoDefinitionDescription],
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (u *configUseCaseImpl) CreateConfig(ctx context.Context, req apis.CreateConfigRequest) error {
|
||||
app := v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: req.Name,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Annotations: map[string]string{
|
||||
types.AnnotationConfigAlias: req.Alias,
|
||||
},
|
||||
Labels: map[string]string{
|
||||
model.LabelSourceOfTruth: model.FromInner,
|
||||
types.LabelConfigCatalog: velaCoreConfig,
|
||||
types.LabelConfigType: req.ComponentType,
|
||||
types.LabelConfigProject: req.Project,
|
||||
},
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: req.Name,
|
||||
Type: req.ComponentType,
|
||||
Properties: &runtime.RawExtension{Raw: []byte(req.Properties)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := u.kubeClient.Create(ctx, &app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// try to check whether the underlying config secrets is successfully created
|
||||
var succeeded bool
|
||||
var configApp v1beta1.Application
|
||||
for i := 0; i < 100; i++ {
|
||||
if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: req.Name}, &configApp); err == nil {
|
||||
if configApp.Status.Phase == common.ApplicationRunning {
|
||||
succeeded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
// clean up failed application
|
||||
if !succeeded {
|
||||
if err := u.kubeClient.Delete(ctx, &app); err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New("failed to create config")
|
||||
}
|
||||
|
||||
if succeeded && req.ComponentType == types.DexConnector {
|
||||
return u.authenticationUseCase.UpdateDexConfig(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *configUseCaseImpl) GetConfigs(ctx context.Context, configType string) ([]*apis.Config, error) {
|
||||
switch configType {
|
||||
case types.TerraformProvider:
|
||||
defs := &v1beta1.ComponentDefinitionList{}
|
||||
if err := u.kubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{
|
||||
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
|
||||
definition.UserPrefix + "type.config.oam.dev": types.TerraformProvider,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var configs []*apis.Config
|
||||
for _, d := range defs.Items {
|
||||
subConfigs, err := u.getConfigsByConfigType(ctx, d.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configs = append(configs, subConfigs...)
|
||||
}
|
||||
return configs, nil
|
||||
|
||||
default:
|
||||
return u.getConfigsByConfigType(ctx, configType)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (u *configUseCaseImpl) getConfigsByConfigType(ctx context.Context, configType string) ([]*apis.Config, error) {
|
||||
var apps = &v1beta1.ApplicationList{}
|
||||
if err := u.kubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{
|
||||
model.LabelSourceOfTruth: model.FromInner,
|
||||
types.LabelConfigCatalog: velaCoreConfig,
|
||||
types.LabelConfigType: configType,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configs := make([]*apis.Config, len(apps.Items))
|
||||
for i, a := range apps.Items {
|
||||
configs[i] = &apis.Config{
|
||||
ConfigType: a.Labels[types.LabelConfigType],
|
||||
Name: a.Name,
|
||||
Project: a.Labels[types.LabelConfigProject],
|
||||
CreatedTime: &(a.CreationTimestamp.Time),
|
||||
}
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
func (u *configUseCaseImpl) GetConfig(ctx context.Context, configType, name string) (*apis.Config, error) {
|
||||
var a = &v1beta1.Application{}
|
||||
if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := &apis.Config{
|
||||
ConfigType: a.Labels[types.LabelConfigType],
|
||||
Name: a.Name,
|
||||
Project: a.Labels[types.LabelConfigProject],
|
||||
CreatedTime: &a.CreationTimestamp.Time,
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (u *configUseCaseImpl) DeleteConfig(ctx context.Context, configType, name string) error {
|
||||
var a = &v1beta1.Application{}
|
||||
if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, a); err != nil {
|
||||
return err
|
||||
}
|
||||
return u.kubeClient.Delete(ctx, a)
|
||||
}
|
||||
|
||||
// ApplicationDeployTarget is the struct of application deploy target
|
||||
type ApplicationDeployTarget struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Clusters []string `json:"clusters"`
|
||||
}
|
||||
|
||||
// SyncConfigs will sync configs to working clusters
|
||||
func SyncConfigs(ctx context.Context, k8sClient client.Client, project string, targets []*model.ClusterTarget) error {
|
||||
name := fmt.Sprintf("config-sync-%s", project)
|
||||
// get all configs which can be synced to working clusters in the project
|
||||
var secrets v1.SecretList
|
||||
if err := k8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{
|
||||
types.LabelConfigCatalog: velaCoreConfig,
|
||||
types.LabelConfigProject: project,
|
||||
types.LabelConfigSyncToMultiCluster: "true",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(secrets.Items) == 0 {
|
||||
return nil
|
||||
}
|
||||
objects := make([]map[string]string, len(secrets.Items))
|
||||
for i, s := range secrets.Items {
|
||||
objects[i] = map[string]string{
|
||||
"name": s.Name,
|
||||
"resource": "secret",
|
||||
}
|
||||
}
|
||||
objectsBytes, err := json.Marshal(map[string][]map[string]string{"objects": objects})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var app = &v1beta1.Application{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, app); err != nil {
|
||||
if !kerrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
// config sync application doesn't exist, create one
|
||||
|
||||
scratch := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
model.LabelSourceOfTruth: model.FromInner,
|
||||
types.LabelConfigCatalog: velaCoreConfig,
|
||||
types.LabelConfigProject: project,
|
||||
},
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: name,
|
||||
Type: "ref-objects",
|
||||
Properties: &runtime.RawExtension{Raw: objectsBytes},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := k8sClient.Create(ctx, scratch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// config sync application exists, update it
|
||||
app.Spec.Components = []common.ApplicationComponent{
|
||||
{
|
||||
Name: name,
|
||||
Type: "ref-objects",
|
||||
Properties: &runtime.RawExtension{Raw: objectsBytes},
|
||||
},
|
||||
}
|
||||
currentTargets := make([]ApplicationDeployTarget, len(app.Spec.Policies))
|
||||
for i, p := range app.Spec.Policies {
|
||||
var t ApplicationDeployTarget
|
||||
if err := json.Unmarshal(p.Properties.Raw, &t); err != nil {
|
||||
return err
|
||||
}
|
||||
currentTargets[i] = t
|
||||
}
|
||||
|
||||
mergedTarget := mergeTargets(currentTargets, targets)
|
||||
mergedPolicies := make([]v1beta1.AppPolicy, len(mergedTarget))
|
||||
for i, t := range mergedTarget {
|
||||
properties, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mergedPolicies[i] = v1beta1.AppPolicy{
|
||||
Type: "topology",
|
||||
Name: t.Namespace,
|
||||
Properties: &runtime.RawExtension{
|
||||
Raw: properties,
|
||||
},
|
||||
}
|
||||
}
|
||||
app.Spec.Policies = mergedPolicies
|
||||
out, _ := yaml.Marshal(app)
|
||||
fmt.Println(string(out))
|
||||
|
||||
return k8sClient.Update(ctx, app)
|
||||
}
|
||||
|
||||
func mergeTargets(currentTargets []ApplicationDeployTarget, targets []*model.ClusterTarget) []ApplicationDeployTarget {
|
||||
var mergedTargets []ApplicationDeployTarget
|
||||
for _, c := range currentTargets {
|
||||
var hasSameNamespace bool
|
||||
for _, t := range targets {
|
||||
if c.Namespace == t.Namespace {
|
||||
hasSameNamespace = true
|
||||
clusters := append(c.Clusters, t.ClusterName)
|
||||
mergedTargets = append(mergedTargets, ApplicationDeployTarget{Namespace: c.Namespace, Clusters: clusters})
|
||||
}
|
||||
}
|
||||
if !hasSameNamespace {
|
||||
mergedTargets = append(mergedTargets, c)
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range targets {
|
||||
var hasSameNamespace bool
|
||||
for _, c := range currentTargets {
|
||||
if c.Namespace == t.Namespace {
|
||||
hasSameNamespace = true
|
||||
}
|
||||
}
|
||||
if !hasSameNamespace {
|
||||
mergedTargets = append(mergedTargets, ApplicationDeployTarget{Namespace: t.Namespace, Clusters: []string{t.ClusterName}})
|
||||
}
|
||||
}
|
||||
|
||||
return mergedTargets
|
||||
}
|
||||
340
pkg/apiserver/rest/usecase/config_test.go
Normal file
340
pkg/apiserver/rest/usecase/config_test.go
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
. "github.com/agiledragon/gomonkey/v2"
|
||||
"gotest.tools/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/definition"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
)
|
||||
|
||||
func TestListConfigTypes(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
corev1.AddToScheme(s)
|
||||
def1 := &v1beta1.ComponentDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ComponentDefinition",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "def1",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
|
||||
definitionType: types.TerraformProvider,
|
||||
},
|
||||
},
|
||||
}
|
||||
def2 := &v1beta1.ComponentDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ComponentDefinition",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "def2",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Annotations: map[string]string{
|
||||
definitionAlias: "Def2",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(def1, def2).Build()
|
||||
|
||||
patches := ApplyFunc(multicluster.GetMulticlusterKubernetesClient, func() (client.Client, *rest.Config, error) {
|
||||
return k8sClient, nil, nil
|
||||
})
|
||||
defer patches.Reset()
|
||||
|
||||
h := NewConfigUseCase(nil)
|
||||
|
||||
type args struct {
|
||||
h ConfigHandler
|
||||
}
|
||||
|
||||
type want struct {
|
||||
configTypes []*apis.ConfigType
|
||||
errMsg string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
h: h,
|
||||
},
|
||||
want: want{
|
||||
configTypes: []*apis.ConfigType{
|
||||
{
|
||||
Name: "def2",
|
||||
Alias: "Def2",
|
||||
Definitions: []string{"def2"},
|
||||
},
|
||||
{
|
||||
Alias: "Terraform Cloud Provider",
|
||||
Name: types.TerraformProvider,
|
||||
Definitions: []string{
|
||||
"def1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := tc.args.h.ListConfigTypes(ctx, "")
|
||||
if tc.want.errMsg != "" || err != nil {
|
||||
assert.ErrorContains(t, err, tc.want.errMsg)
|
||||
}
|
||||
assert.DeepEqual(t, got, tc.want.configTypes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfigType(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
corev1.AddToScheme(s)
|
||||
def2 := &v1beta1.ComponentDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ComponentDefinition",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "def2",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Annotations: map[string]string{
|
||||
definitionAlias: "Def2",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(def2).Build()
|
||||
|
||||
patches := ApplyFunc(multicluster.GetMulticlusterKubernetesClient, func() (client.Client, *rest.Config, error) {
|
||||
return k8sClient, nil, nil
|
||||
})
|
||||
defer patches.Reset()
|
||||
|
||||
h := NewConfigUseCase(nil)
|
||||
|
||||
type args struct {
|
||||
h ConfigHandler
|
||||
name string
|
||||
}
|
||||
|
||||
type want struct {
|
||||
configType *apis.ConfigType
|
||||
errMsg string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
h: h,
|
||||
name: "def2",
|
||||
},
|
||||
want: want{
|
||||
configType: &apis.ConfigType{
|
||||
Alias: "Def2",
|
||||
Name: "def2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
args: args{
|
||||
h: h,
|
||||
name: "def99",
|
||||
},
|
||||
want: want{
|
||||
errMsg: "failed to get config type",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := tc.args.h.GetConfigType(ctx, tc.args.name)
|
||||
if tc.want.errMsg != "" || err != nil {
|
||||
assert.ErrorContains(t, err, tc.want.errMsg)
|
||||
}
|
||||
assert.DeepEqual(t, got, tc.want.configType)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateConfig(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
corev1.AddToScheme(s)
|
||||
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
|
||||
h := &configUseCaseImpl{kubeClient: k8sClient}
|
||||
|
||||
type args struct {
|
||||
h ConfigHandler
|
||||
req apis.CreateConfigRequest
|
||||
}
|
||||
|
||||
type want struct {
|
||||
errMsg string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "delete config when it's not ready",
|
||||
args: args{
|
||||
h: h,
|
||||
req: apis.CreateConfigRequest{
|
||||
Name: "a",
|
||||
ComponentType: "b",
|
||||
Project: "c",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.args.h.CreateConfig(ctx, tc.args.req)
|
||||
if tc.want.errMsg != "" || err != nil {
|
||||
assert.ErrorContains(t, err, tc.want.errMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfigs(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
corev1.AddToScheme(s)
|
||||
def1 := &v1beta1.ComponentDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ComponentDefinition",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "def1",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
|
||||
definitionType: types.TerraformProvider,
|
||||
},
|
||||
},
|
||||
}
|
||||
def2 := &v1beta1.ComponentDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ComponentDefinition",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "def2",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Annotations: map[string]string{
|
||||
definitionAlias: "Def2",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(def1, def2).Build()
|
||||
|
||||
h := &configUseCaseImpl{kubeClient: k8sClient}
|
||||
|
||||
type args struct {
|
||||
configType string
|
||||
h ConfigHandler
|
||||
}
|
||||
|
||||
type want struct {
|
||||
configs []*apis.Config
|
||||
errMsg string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
args: args{
|
||||
configType: types.TerraformProvider,
|
||||
h: h,
|
||||
},
|
||||
want: want{
|
||||
configs: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := tc.args.h.GetConfigs(ctx, tc.args.configType)
|
||||
if tc.want.errMsg != "" || err != nil {
|
||||
assert.ErrorContains(t, err, tc.want.errMsg)
|
||||
}
|
||||
assert.DeepEqual(t, got, tc.want.configs)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -20,32 +20,48 @@ import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/helm"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
// NewHelmUsecase return a helmHandler
|
||||
func NewHelmUsecase() HelmHandler {
|
||||
c, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get kube client failure %s", err.Error())
|
||||
}
|
||||
return defaultHelmHandler{
|
||||
helper: helm.NewHelperWithCache(),
|
||||
helper: helm.NewHelperWithCache(),
|
||||
k8sClient: c,
|
||||
}
|
||||
}
|
||||
|
||||
// HelmHandler responsible handle helm related interface
|
||||
type HelmHandler interface {
|
||||
ListChartNames(ctx context.Context, url string, skipCache bool) ([]string, error)
|
||||
ListChartVersions(ctx context.Context, url string, chartName string, skipCache bool) (repo.ChartVersions, error)
|
||||
GetChartValues(ctx context.Context, url string, chartName string, version string, skipCache bool) (map[string]interface{}, error)
|
||||
ListChartNames(ctx context.Context, url string, secretName string, skipCache bool) ([]string, error)
|
||||
ListChartVersions(ctx context.Context, url string, chartName string, secretName string, skipCache bool) (repo.ChartVersions, error)
|
||||
GetChartValues(ctx context.Context, url string, chartName string, version string, secretName string, skipCache bool) (map[string]interface{}, error)
|
||||
ListChartRepo(ctx context.Context, projectName string) (*v1.ChartRepoResponseList, error)
|
||||
}
|
||||
|
||||
type defaultHelmHandler struct {
|
||||
helper *helm.Helper
|
||||
helper *helm.Helper
|
||||
k8sClient client.Client
|
||||
}
|
||||
|
||||
func (d defaultHelmHandler) ListChartNames(ctx context.Context, url string, skipCache bool) ([]string, error) {
|
||||
func (d defaultHelmHandler) ListChartNames(ctx context.Context, url string, secretName string, skipCache bool) ([]string, error) {
|
||||
// TODO(wangyikewxgm): support authority helm repo
|
||||
charts, err := d.helper.ListChartsFromRepo(url, skipCache)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("cannot fetch charts repo: %s, error: %s", url, err.Error())
|
||||
@@ -54,7 +70,7 @@ func (d defaultHelmHandler) ListChartNames(ctx context.Context, url string, skip
|
||||
return charts, nil
|
||||
}
|
||||
|
||||
func (d defaultHelmHandler) ListChartVersions(ctx context.Context, url string, chartName string, skipCache bool) (repo.ChartVersions, error) {
|
||||
func (d defaultHelmHandler) ListChartVersions(ctx context.Context, url string, chartName string, secretName string, skipCache bool) (repo.ChartVersions, error) {
|
||||
chartVersions, err := d.helper.ListVersions(url, chartName, skipCache)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("cannot fetch chart versions repo: %s, chart: %s error: %s", url, chartName, err.Error())
|
||||
@@ -67,7 +83,7 @@ func (d defaultHelmHandler) ListChartVersions(ctx context.Context, url string, c
|
||||
return chartVersions, nil
|
||||
}
|
||||
|
||||
func (d defaultHelmHandler) GetChartValues(ctx context.Context, url string, chartName string, version string, skipCache bool) (map[string]interface{}, error) {
|
||||
func (d defaultHelmHandler) GetChartValues(ctx context.Context, url string, chartName string, version string, secretName string, skipCache bool) (map[string]interface{}, error) {
|
||||
v, err := d.helper.GetValuesFromChart(url, chartName, version, skipCache)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("cannot fetch chart values repo: %s, chart: %s, version: %s, error: %s", url, chartName, version, err.Error())
|
||||
@@ -78,6 +94,50 @@ func (d defaultHelmHandler) GetChartValues(ctx context.Context, url string, char
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d defaultHelmHandler) ListChartRepo(ctx context.Context, projectName string) (*v1.ChartRepoResponseList, error) {
|
||||
var res []*v1.ChartRepoResponse
|
||||
var err error
|
||||
|
||||
if len(projectName) != 0 {
|
||||
projectSecrets := corev1.SecretList{}
|
||||
opts := []client.ListOption{
|
||||
client.MatchingLabels{oam.LabelConfigType: "helm-repository", oam.LabelProject: projectName},
|
||||
client.InNamespace(types.DefaultKubeVelaNS),
|
||||
}
|
||||
err = d.k8sClient.List(ctx, &projectSecrets, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range projectSecrets.Items {
|
||||
res = append(res, &v1.ChartRepoResponse{URL: string(item.Data["url"]), SecretName: item.Name})
|
||||
}
|
||||
}
|
||||
|
||||
globalSecrets := corev1.SecretList{}
|
||||
selector := metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{oam.LabelConfigType: "helm-repository"},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{Key: oam.LabelProject, Operator: metav1.LabelSelectorOpDoesNotExist},
|
||||
},
|
||||
}
|
||||
|
||||
ls, _ := metav1.LabelSelectorAsSelector(&selector)
|
||||
err = d.k8sClient.List(ctx, &globalSecrets, &client.ListOptions{
|
||||
LabelSelector: ls,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range globalSecrets.Items {
|
||||
res = append(res, &v1.ChartRepoResponse{URL: string(item.Data["url"]), SecretName: item.Name})
|
||||
}
|
||||
|
||||
return &v1.ChartRepoResponseList{ChartRepoResponse: res}, nil
|
||||
}
|
||||
|
||||
// this func will flatten a nested map, the key will flatten with separator "." and the value's type will be keep
|
||||
// src is the map you want to flatten the output will be set in dest map
|
||||
// eg : src is {a:{b:{c:true}}} , the dest is {a.b.c:true}
|
||||
|
||||
@@ -17,9 +17,19 @@ limitations under the License.
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -37,6 +47,63 @@ func TestFlattenKeyFunc(t *testing.T) {
|
||||
assert.Equal(t, dstMap, res)
|
||||
}
|
||||
|
||||
var _ = Describe("Test helm repo list", func() {
|
||||
ctx := context.Background()
|
||||
var pSec, gSec v1.Secret
|
||||
|
||||
BeforeEach(func() {
|
||||
pSec = v1.Secret{}
|
||||
gSec = v1.Secret{}
|
||||
Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
Expect(yaml.Unmarshal([]byte(projectSecret), &pSec)).Should(BeNil())
|
||||
Expect(yaml.Unmarshal([]byte(globalSecret), &gSec)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &pSec)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &gSec)).Should(BeNil())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(k8sClient.Delete(ctx, &gSec)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, &pSec)).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test list with project ", func() {
|
||||
u := NewHelmUsecase()
|
||||
list, err := u.ListChartRepo(ctx, "my-project")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(list.ChartRepoResponse)).Should(BeEquivalentTo(2))
|
||||
found := 0
|
||||
for _, response := range list.ChartRepoResponse {
|
||||
if response.SecretName == "project-helm-repo" {
|
||||
Expect(response.URL).Should(BeEquivalentTo("https://kedacore.github.io/charts"))
|
||||
found++
|
||||
}
|
||||
if response.SecretName == "global-helm-repo" {
|
||||
Expect(response.URL).Should(BeEquivalentTo("https://charts.bitnami.com/bitnami"))
|
||||
found++
|
||||
}
|
||||
}
|
||||
Expect(found).Should(BeEquivalentTo(2))
|
||||
})
|
||||
|
||||
It("Test list func with not exist project", func() {
|
||||
u := NewHelmUsecase()
|
||||
list, err := u.ListChartRepo(ctx, "not-exist-project")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(list.ChartRepoResponse)).Should(BeEquivalentTo(1))
|
||||
Expect(list.ChartRepoResponse[0].URL).Should(BeEquivalentTo("https://charts.bitnami.com/bitnami"))
|
||||
Expect(list.ChartRepoResponse[0].SecretName).Should(BeEquivalentTo("global-helm-repo"))
|
||||
})
|
||||
|
||||
It("Test list func without project", func() {
|
||||
u := NewHelmUsecase()
|
||||
list, err := u.ListChartRepo(ctx, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(list.ChartRepoResponse)).Should(BeEquivalentTo(1))
|
||||
Expect(list.ChartRepoResponse[0].URL).Should(BeEquivalentTo("https://charts.bitnami.com/bitnami"))
|
||||
Expect(list.ChartRepoResponse[0].SecretName).Should(BeEquivalentTo("global-helm-repo"))
|
||||
})
|
||||
})
|
||||
|
||||
var (
|
||||
src = `{
|
||||
"OAMSpecVer":"v0.2",
|
||||
@@ -175,4 +242,29 @@ var (
|
||||
"webhookService.port": 11443,
|
||||
"webhookService.type": "ClusterIP"
|
||||
}`
|
||||
globalSecret = `
|
||||
apiVersion: v1
|
||||
stringData:
|
||||
url: https://charts.bitnami.com/bitnami
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
config.oam.dev/type: helm-repository
|
||||
name: global-helm-repo
|
||||
namespace: vela-system
|
||||
type: Opaque
|
||||
`
|
||||
projectSecret = `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: project-helm-repo
|
||||
namespace: vela-system
|
||||
labels:
|
||||
config.oam.dev/type: helm-repository
|
||||
core.oam.dev/project: my-project
|
||||
stringData:
|
||||
url: https://kedacore.github.io/charts
|
||||
type: Opaque
|
||||
`
|
||||
)
|
||||
|
||||
@@ -207,6 +207,14 @@ var ResourceMaps = map[string]resourceMetadata{
|
||||
"permission": {},
|
||||
"systemSetting": {},
|
||||
"definition": {},
|
||||
"configType": {
|
||||
pathName: "configType",
|
||||
subResources: map[string]resourceMetadata{
|
||||
"config": {
|
||||
pathName: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var existResourcePaths = convert(ResourceMaps)
|
||||
|
||||
@@ -18,31 +18,52 @@ package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// SystemInfoUsecase is usecase for systemInfoCollection
|
||||
type SystemInfoUsecase interface {
|
||||
Get(ctx context.Context) (*model.SystemInfo, error)
|
||||
GetSystemInfo(ctx context.Context) (*v1.SystemInfoResponse, error)
|
||||
UpdateSystemInfo(ctx context.Context, sysInfo v1.SystemInfoRequest) (*v1.SystemInfoResponse, error)
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
type systemInfoUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
ds datastore.DataStore
|
||||
kubeClient client.Client
|
||||
}
|
||||
|
||||
// NewSystemInfoUsecase return a systemInfoCollectionUsecase
|
||||
func NewSystemInfoUsecase(ds datastore.DataStore) SystemInfoUsecase {
|
||||
return &systemInfoUsecaseImpl{ds: ds}
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("failed to get kube client: %s", err.Error())
|
||||
}
|
||||
return &systemInfoUsecaseImpl{ds: ds, kubeClient: kubecli}
|
||||
}
|
||||
|
||||
func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInfoResponse, error) {
|
||||
func (u systemInfoUsecaseImpl) Get(ctx context.Context) (*model.SystemInfo, error) {
|
||||
// first get request will init systemInfoCollection{installId: {random}, enableCollection: true}
|
||||
info := &model.SystemInfo{}
|
||||
entities, err := u.ds.List(ctx, info, &datastore.ListOptions{})
|
||||
@@ -54,7 +75,7 @@ func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInf
|
||||
if info.LoginType == "" {
|
||||
info.LoginType = model.LoginTypeLocal
|
||||
}
|
||||
return &v1.SystemInfoResponse{SystemInfo: *info, SystemVersion: v1.SystemVersion{VelaVersion: version.VelaVersion, GitVersion: version.GitRevision}}, nil
|
||||
return info, nil
|
||||
}
|
||||
installID := rand.String(16)
|
||||
info.InstallID = installID
|
||||
@@ -64,11 +85,26 @@ func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInf
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v1.SystemInfoResponse{SystemInfo: *info, SystemVersion: v1.SystemVersion{VelaVersion: version.VelaVersion, GitVersion: version.GitRevision}}, nil
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInfoResponse, error) {
|
||||
// first get request will init systemInfoCollection{installId: {random}, enableCollection: true}
|
||||
info, err := u.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v1.SystemInfoResponse{
|
||||
SystemInfo: convertInfoToBase(info),
|
||||
SystemVersion: v1.SystemVersion{
|
||||
VelaVersion: version.VelaVersion,
|
||||
GitVersion: version.GitRevision,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u systemInfoUsecaseImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1.SystemInfoRequest) (*v1.SystemInfoResponse, error) {
|
||||
info, err := u.GetSystemInfo(ctx)
|
||||
info, err := u.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -78,10 +114,116 @@ func (u systemInfoUsecaseImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1.
|
||||
LoginType: sysInfo.LoginType,
|
||||
BaseModel: model.BaseModel{
|
||||
CreateTime: info.CreateTime,
|
||||
}}
|
||||
},
|
||||
}
|
||||
|
||||
if sysInfo.LoginType == model.LoginTypeDex {
|
||||
if err := generateDexConfig(ctx, u.kubeClient, sysInfo.VelaAddress, &modifiedInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = u.ds.Put(ctx, &modifiedInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v1.SystemInfoResponse{SystemInfo: modifiedInfo, SystemVersion: v1.SystemVersion{VelaVersion: version.VelaVersion, GitVersion: version.GitRevision}}, nil
|
||||
return &v1.SystemInfoResponse{
|
||||
SystemInfo: v1.SystemInfo{
|
||||
InstallID: modifiedInfo.InstallID,
|
||||
EnableCollection: modifiedInfo.EnableCollection,
|
||||
LoginType: modifiedInfo.LoginType,
|
||||
},
|
||||
SystemVersion: v1.SystemVersion{VelaVersion: version.VelaVersion, GitVersion: version.GitRevision},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u systemInfoUsecaseImpl) Init(ctx context.Context) error {
|
||||
_, err := initDexConfig(ctx, u.kubeClient, "http://velaux.com", &model.SystemInfo{})
|
||||
return err
|
||||
}
|
||||
|
||||
func convertInfoToBase(info *model.SystemInfo) v1.SystemInfo {
|
||||
return v1.SystemInfo{
|
||||
InstallID: info.InstallID,
|
||||
EnableCollection: info.EnableCollection,
|
||||
LoginType: info.LoginType,
|
||||
}
|
||||
}
|
||||
|
||||
func generateDexConfig(ctx context.Context, kubeClient client.Client, velaAddress string, info *model.SystemInfo) error {
|
||||
secret, err := initDexConfig(ctx, kubeClient, velaAddress, info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
connectors, err := utils.GetDexConnectors(ctx, kubeClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config, err := model.NewJSONStructByStruct(info.DexConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
(*config)["connectors"] = connectors
|
||||
c, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !reflect.DeepEqual(secret.Data[secretDexConfigKey], c) {
|
||||
secret.Data[secretDexConfigKey] = c
|
||||
if err := kubeClient.Update(ctx, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := restartDex(ctx, kubeClient); err != nil && !errors.Is(err, bcode.ErrDexNotFound) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initDexConfig(ctx context.Context, kubeClient client.Client, velaAddress string, info *model.SystemInfo) (*corev1.Secret, error) {
|
||||
dexConfig := model.DexConfig{
|
||||
Issuer: fmt.Sprintf("%s/dex", velaAddress),
|
||||
Web: model.DexWeb{
|
||||
HTTP: "0.0.0.0:5556",
|
||||
},
|
||||
Storage: model.DexStorage{
|
||||
Type: "memory",
|
||||
},
|
||||
StaticClients: []model.DexStaticClient{
|
||||
{
|
||||
ID: "velaux",
|
||||
Name: "Vela UX",
|
||||
Secret: "velaux-secret",
|
||||
RedirectURIs: []string{fmt.Sprintf("%s/callback", velaAddress)},
|
||||
},
|
||||
},
|
||||
EnablePasswordDB: true,
|
||||
}
|
||||
info.DexConfig = dexConfig
|
||||
|
||||
secret := &corev1.Secret{}
|
||||
if err := kubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: dexConfigName,
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
}, secret); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
config, err := yaml.Marshal(info.DexConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := kubeClient.Create(ctx, &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: dexConfigName,
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: map[string][]byte{
|
||||
secretDexConfigKey: config,
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
26
pkg/apiserver/rest/usecase/testdata/dex-config-def.yaml
vendored
Normal file
26
pkg/apiserver/rest/usecase/testdata/dex-config-def.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Dex config allow users to specify dex config in properties
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: dex-config
|
||||
namespace: vela-system
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import "encoding/yaml"
|
||||
|
||||
output: {
|
||||
apiVersion: "v1"
|
||||
kind: "Secret"
|
||||
metadata: name: context.name
|
||||
namespace: context.namespace
|
||||
type: "Opaque"
|
||||
stringData: "config.yaml": yaml.Marshal(parameter)
|
||||
}
|
||||
parameter: {}
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
@@ -96,7 +96,7 @@ func (u *userUsecaseImpl) Init(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
// print default password of admin user in log
|
||||
log.Logger.Infof("init admin user, password is %s", pwd)
|
||||
log.Logger.Infof("initialized admin username and password: admin / %s\n", pwd)
|
||||
secret := &corev1.Secret{}
|
||||
if err := u.k8sClient.Get(ctx, k8stypes.NamespacedName{
|
||||
Name: admin,
|
||||
@@ -192,7 +192,7 @@ func (u *userUsecaseImpl) DeleteUser(ctx context.Context, username string) error
|
||||
|
||||
// CreateUser create user
|
||||
func (u *userUsecaseImpl) CreateUser(ctx context.Context, req apisv1.CreateUserRequest) (*apisv1.UserBase, error) {
|
||||
sysInfo, err := u.sysUsecase.GetSystemInfo(ctx)
|
||||
sysInfo, err := u.sysUsecase.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -220,7 +220,7 @@ func (u *userUsecaseImpl) CreateUser(ctx context.Context, req apisv1.CreateUserR
|
||||
|
||||
// UpdateUser update user
|
||||
func (u *userUsecaseImpl) UpdateUser(ctx context.Context, user *model.User, req apisv1.UpdateUserRequest) (*apisv1.UserBase, error) {
|
||||
sysInfo, err := u.sysUsecase.GetSystemInfo(ctx)
|
||||
sysInfo, err := u.sysUsecase.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -25,11 +25,14 @@ import (
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
@@ -74,7 +77,12 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test HandleApplicationWebhook function", func() {
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: "project-webhook"})
|
||||
var ns = corev1.Namespace{}
|
||||
ns.Name = types.DefaultKubeVelaNS
|
||||
err := k8sClient.Create(context.TODO(), &ns)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
_, err = projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: "project-webhook"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = targetUsecase.CreateTarget(context.TODO(), apisv1.CreateTargetRequest{Name: "dev-target-webhook", Project: "project-webhook"})
|
||||
|
||||
@@ -33,4 +33,6 @@ var (
|
||||
ErrUserInconsistentPassword = NewBcode(401, 14007, "the password is inconsistent with the user")
|
||||
// ErrUsernameNotExist is the error of username not exist
|
||||
ErrUsernameNotExist = NewBcode(401, 14008, "the username is not exist")
|
||||
// ErrDexNotFound is the error of dex not found
|
||||
ErrDexNotFound = NewBcode(200, 14009, "the dex is not found")
|
||||
)
|
||||
|
||||
52
pkg/apiserver/rest/utils/dex.go
Normal file
52
pkg/apiserver/rest/utils/dex.go
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
)
|
||||
|
||||
// GetDexConnectors returns the dex connectors for Dex connector controller
|
||||
func GetDexConnectors(ctx context.Context, k8sClient client.Client) ([]map[string]interface{}, error) {
|
||||
secrets := &v1.SecretList{}
|
||||
if err := k8sClient.List(ctx, secrets, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{types.LabelConfigType: types.DexConnector}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
connectors := make([]map[string]interface{}, len(secrets.Items))
|
||||
for i, s := range secrets.Items {
|
||||
var data map[string]interface{}
|
||||
key := s.Labels[types.LabelConfigSubType]
|
||||
err := json.Unmarshal(s.Data[key], &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
connectors[i] = map[string]interface{}{
|
||||
"type": s.Labels[types.LabelConfigSubType],
|
||||
"id": s.Name,
|
||||
"name": s.Name,
|
||||
"config": data,
|
||||
}
|
||||
}
|
||||
|
||||
return connectors, nil
|
||||
}
|
||||
106
pkg/apiserver/rest/utils/dex_test.go
Normal file
106
pkg/apiserver/rest/utils/dex_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
func TestGetDexConnectors(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
type args struct {
|
||||
k8sClient client.Client
|
||||
}
|
||||
type want struct {
|
||||
connectors []map[string]interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
ldap := map[string]interface{}{
|
||||
"clientID": "clientID",
|
||||
"clientSecret": "clientSecret",
|
||||
"callbackURL": "redirectURL",
|
||||
"xxx": map[string]interface{}{"aaa": "bbb", "ccc": "ddd"},
|
||||
}
|
||||
data, err := json.Marshal(ldap)
|
||||
assert.NoError(t, err)
|
||||
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
Namespace: "vela-system",
|
||||
Labels: map[string]string{
|
||||
"app.oam.dev/source-of-truth": "from-inner-system",
|
||||
"config.oam.dev/catalog": "velacore-config",
|
||||
"config.oam.dev/type": "config-dex-connector",
|
||||
"config.oam.dev/sub-type": "ldap",
|
||||
"project": "abc",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"ldap": data,
|
||||
},
|
||||
Type: v1.SecretTypeOpaque,
|
||||
}
|
||||
|
||||
k8sClient := fake.NewClientBuilder().WithObjects(secret).Build()
|
||||
|
||||
testcaes := map[string]struct {
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
|
||||
"test": {args: args{
|
||||
k8sClient: k8sClient,
|
||||
},
|
||||
want: want{
|
||||
connectors: []map[string]interface{}{{
|
||||
"id": "a",
|
||||
"name": "a",
|
||||
"type": "ldap",
|
||||
"config": map[string]interface{}{
|
||||
"clientID": "clientID",
|
||||
"clientSecret": "clientSecret",
|
||||
"callbackURL": "redirectURL",
|
||||
"xxx": map[string]interface{}{"aaa": "bbb", "ccc": "ddd"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testcaes {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, err := GetDexConnectors(ctx, tc.args.k8sClient)
|
||||
if err != tc.want.err {
|
||||
t.Errorf("%s: GetDexConnectors() error = %v, wantErr %v", name, err, tc.want.err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tc.want.connectors) {
|
||||
t.Errorf("%s: GetDexConnectors() = %v, want %v", name, got, tc.want.connectors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
192
pkg/apiserver/rest/webservice/config.go
Normal file
192
pkg/apiserver/rest/webservice/config.go
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package webservice
|
||||
|
||||
import (
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
// ConfigWebService returns config web service
|
||||
func ConfigWebService(u usecase.ConfigHandler, rbacUseCase usecase.RBACUsecase) WebService {
|
||||
return &configWebService{
|
||||
handler: u,
|
||||
rbacUseCase: rbacUseCase,
|
||||
}
|
||||
}
|
||||
|
||||
type configWebService struct {
|
||||
handler usecase.ConfigHandler
|
||||
rbacUseCase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
func (s *configWebService) GetWebService() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix+"/config_types").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML).
|
||||
Doc("api for configuration management")
|
||||
|
||||
tags := []string{"config"}
|
||||
|
||||
ws.Route(ws.GET("/").To(s.listConfigTypes).
|
||||
Doc("list all config types").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUseCase.CheckPerm("configType", "list")).
|
||||
Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")).
|
||||
Returns(200, "OK", []apis.ConfigType{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]apis.ConfigType{}))
|
||||
|
||||
ws.Route(ws.GET("/{configType}").To(s.getConfigType).
|
||||
Doc("get a config type").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUseCase.CheckPerm("configType", "get")).
|
||||
Param(ws.PathParameter("configType", "identifier of the config type").DataType("string")).
|
||||
Returns(200, "OK", apis.ConfigType{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ConfigType{}))
|
||||
|
||||
ws.Route(ws.POST("/{configType}").To(s.createConfig).
|
||||
Doc("create or update a config").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUseCase.CheckPerm("configType", "create")).
|
||||
Param(ws.PathParameter("configType", "identifier of the config type").DataType("string")).
|
||||
Reads(apis.CreateConfigRequest{}).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Returns(404, "Not Found", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{configType}/configs").To(s.getConfigs).
|
||||
Doc("get configs from a config type").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUseCase.CheckPerm("config", "list")).
|
||||
Param(ws.PathParameter("configType", "identifier of the config").DataType("string")).
|
||||
Returns(200, "OK", []*apis.Config{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ConfigType{}))
|
||||
|
||||
ws.Route(ws.GET("/{configType}/configs/{name}").To(s.getConfig).
|
||||
Doc("get a config from a config type").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUseCase.CheckPerm("config", "get")).
|
||||
Param(ws.PathParameter("configType", "identifier of the config type").DataType("string")).
|
||||
Param(ws.PathParameter("name", "identifier of the config").DataType("string")).
|
||||
Returns(200, "OK", []*apis.Config{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ConfigType{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{configType}/configs/{name}").To(s.deleteConfig).
|
||||
Doc("delete a config").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUseCase.CheckPerm("config", "delete")).
|
||||
Param(ws.PathParameter("configType", "identifier of the config type").DataType("string")).
|
||||
Param(ws.PathParameter("name", "identifier of the config").DataType("string")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Returns(404, "Not Found", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Filter(authCheckFilter)
|
||||
return ws
|
||||
}
|
||||
|
||||
func (s *configWebService) listConfigTypes(req *restful.Request, res *restful.Response) {
|
||||
types, err := s.handler.ListConfigTypes(req.Request.Context(), req.QueryParameter("query"))
|
||||
if len(types) == 0 && err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(types)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *configWebService) getConfigType(req *restful.Request, res *restful.Response) {
|
||||
t, err := s.handler.GetConfigType(req.Request.Context(), req.PathParameter("configType"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(t)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *configWebService) createConfig(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var createReq apis.CreateConfigRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := s.handler.CreateConfig(req.Request.Context(), createReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("failed to create config: %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *configWebService) getConfigs(req *restful.Request, res *restful.Response) {
|
||||
configs, err := s.handler.GetConfigs(req.Request.Context(), req.PathParameter("configType"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(configs)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *configWebService) getConfig(req *restful.Request, res *restful.Response) {
|
||||
t, err := s.handler.GetConfig(req.Request.Context(), req.PathParameter("configType"), req.PathParameter("name"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(t)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *configWebService) deleteConfig(req *restful.Request, res *restful.Response) {
|
||||
err := s.handler.DeleteConfig(req.Request.Context(), req.PathParameter("configType"), req.PathParameter("name"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -46,11 +46,21 @@ func (h helmWebService) GetWebService() *restful.WebService {
|
||||
|
||||
tags := []string{"repository", "helm"}
|
||||
|
||||
// List charts
|
||||
ws.Route(ws.GET("/chart_repos").To(h.listRepo).
|
||||
Doc("list chart repo").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("project", "the config project").DataType("string")).
|
||||
Returns(200, "OK", []string{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
|
||||
// List charts
|
||||
ws.Route(ws.GET("/charts").To(h.listCharts).
|
||||
Doc("list charts").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("repoUrl", "helm repository url").DataType("string")).
|
||||
Param(ws.QueryParameter("secretName", "secret of the repo").DataType("string")).
|
||||
Returns(200, "OK", []string{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
@@ -60,6 +70,7 @@ func (h helmWebService) GetWebService() *restful.WebService {
|
||||
Doc("list versions").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("repoUrl", "helm repository url").DataType("string")).
|
||||
Param(ws.QueryParameter("secretName", "secret of the repo").DataType("string")).
|
||||
Returns(200, "OK", v1.ChartVersionListResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
@@ -69,6 +80,7 @@ func (h helmWebService) GetWebService() *restful.WebService {
|
||||
Doc("get chart value").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("repoUrl", "helm repository url").DataType("string")).
|
||||
Param(ws.QueryParameter("secretName", "secret of the repo").DataType("string")).
|
||||
Returns(200, "OK", map[string]interface{}{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
@@ -79,12 +91,13 @@ func (h helmWebService) GetWebService() *restful.WebService {
|
||||
|
||||
func (h helmWebService) listCharts(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
secName := req.QueryParameter("secretName")
|
||||
skipCache, err := isSkipCache(req)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, bcode.ErrSkipCacheParameter)
|
||||
return
|
||||
}
|
||||
charts, err := h.usecase.ListChartNames(context.Background(), url, skipCache)
|
||||
charts, err := h.usecase.ListChartNames(context.Background(), url, secName, skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -99,13 +112,14 @@ func (h helmWebService) listCharts(req *restful.Request, res *restful.Response)
|
||||
func (h helmWebService) listVersions(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
chartName := req.PathParameter("chart")
|
||||
secName := req.QueryParameter("secretName")
|
||||
skipCache, err := isSkipCache(req)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, bcode.ErrSkipCacheParameter)
|
||||
return
|
||||
}
|
||||
|
||||
versions, err := h.usecase.ListChartVersions(context.Background(), url, chartName, skipCache)
|
||||
versions, err := h.usecase.ListChartVersions(context.Background(), url, chartName, secName, skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -119,6 +133,7 @@ func (h helmWebService) listVersions(req *restful.Request, res *restful.Response
|
||||
|
||||
func (h helmWebService) chartValues(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
secName := req.QueryParameter("secretName")
|
||||
chartName := req.PathParameter("chart")
|
||||
version := req.PathParameter("version")
|
||||
skipCache, err := isSkipCache(req)
|
||||
@@ -127,7 +142,7 @@ func (h helmWebService) chartValues(req *restful.Request, res *restful.Response)
|
||||
return
|
||||
}
|
||||
|
||||
versions, err := h.usecase.GetChartValues(context.Background(), url, chartName, version, skipCache)
|
||||
versions, err := h.usecase.GetChartValues(context.Background(), url, chartName, version, secName, skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -139,6 +154,20 @@ func (h helmWebService) chartValues(req *restful.Request, res *restful.Response)
|
||||
}
|
||||
}
|
||||
|
||||
func (h helmWebService) listRepo(req *restful.Request, res *restful.Response) {
|
||||
project := req.QueryParameter("project")
|
||||
repos, err := h.usecase.ListChartRepo(context.Background(), project)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(repos)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func isSkipCache(req *restful.Request) (bool, error) {
|
||||
skipStr := req.QueryParameter("skipCache")
|
||||
skipCache := false
|
||||
|
||||
@@ -73,15 +73,16 @@ func Init(ctx context.Context, ds datastore.DataStore, addonCacheTime time.Durat
|
||||
definitionUsecase := usecase.NewDefinitionUsecase()
|
||||
addonUsecase := usecase.NewAddonUsecase(addonCacheTime)
|
||||
envBindingUsecase := usecase.NewEnvBindingUsecase(ds, workflowUsecase, definitionUsecase, envUsecase)
|
||||
applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase, envBindingUsecase, envUsecase, targetUsecase, definitionUsecase, projectUsecase)
|
||||
webhookUsecase := usecase.NewWebhookUsecase(ds, applicationUsecase)
|
||||
systemInfoUsecase := usecase.NewSystemInfoUsecase(ds)
|
||||
helmUsecase := usecase.NewHelmUsecase()
|
||||
userUsecase := usecase.NewUserUsecase(ds, projectUsecase, systemInfoUsecase, rbacUsecase)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(ds, systemInfoUsecase, userUsecase)
|
||||
configUseCase := usecase.NewConfigUseCase(authenticationUsecase)
|
||||
applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase, envBindingUsecase, envUsecase, targetUsecase, definitionUsecase, projectUsecase)
|
||||
webhookUsecase := usecase.NewWebhookUsecase(ds, applicationUsecase)
|
||||
// Modules that require default data initialization, Call it here in order
|
||||
if initDatabase {
|
||||
initData(ctx, userUsecase, rbacUsecase, projectUsecase, targetUsecase)
|
||||
initData(ctx, userUsecase, rbacUsecase, projectUsecase, targetUsecase, systemInfoUsecase)
|
||||
}
|
||||
|
||||
// Application
|
||||
@@ -95,6 +96,9 @@ func Init(ctx context.Context, ds datastore.DataStore, addonCacheTime time.Durat
|
||||
RegisterWebService(NewEnabledAddonWebService(addonUsecase, rbacUsecase))
|
||||
RegisterWebService(NewAddonRegistryWebService(addonUsecase, rbacUsecase))
|
||||
|
||||
// Config management
|
||||
RegisterWebService(ConfigWebService(configUseCase, rbacUsecase))
|
||||
|
||||
// Resources
|
||||
RegisterWebService(NewClusterWebService(clusterUsecase, rbacUsecase))
|
||||
RegisterWebService(NewOAMApplication(oamApplicationUsecase, rbacUsecase))
|
||||
|
||||
@@ -90,6 +90,12 @@ const (
|
||||
// LabelRuntimeNamespaceUsage mark the usage of the namespace in runtime cluster.
|
||||
// A control plane cluster can also be used as runtime cluster
|
||||
LabelRuntimeNamespaceUsage = "usage.oam.dev/runtime"
|
||||
|
||||
// LabelConfigType means the config type
|
||||
LabelConfigType = "config.oam.dev/type"
|
||||
|
||||
// LabelProject recorde the project the resource belong to
|
||||
LabelProject = "core.oam.dev/project"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -279,13 +279,13 @@ func RealtimePrintCommandOutput(cmd *exec.Cmd, logFile string) error {
|
||||
|
||||
// ClusterObject2Map convert ClusterObjectReference to a readable map
|
||||
func ClusterObject2Map(refs []common.ClusterObjectReference) map[string]string {
|
||||
clusterResourceRefTmpl := "Cluster: %s | Namespace: %s | Component: %s | Kind: %s"
|
||||
clusterResourceRefTmpl := "Cluster: %s | Namespace: %s | Kind: %s | Name: %s"
|
||||
objs := make(map[string]string, len(refs))
|
||||
for _, r := range refs {
|
||||
if r.Cluster == "" {
|
||||
r.Cluster = "local"
|
||||
}
|
||||
objs[r.Cluster+"/"+r.Namespace+"/"+r.Name+"/"+r.Kind] = fmt.Sprintf(clusterResourceRefTmpl, r.Cluster, r.Namespace, r.Name, r.Kind)
|
||||
objs[r.Cluster+"/"+r.Namespace+"/"+r.Name+"/"+r.Kind] = fmt.Sprintf(clusterResourceRefTmpl, r.Cluster, r.Namespace, r.Kind, r.Name)
|
||||
}
|
||||
return objs
|
||||
}
|
||||
@@ -312,6 +312,11 @@ func clusterObjectReferenceTypeFilterGenerator(allowedKinds ...string) clusterOb
|
||||
var isWorkloadClusterObjectReferenceFilter = clusterObjectReferenceTypeFilterGenerator("Deployment", "StatefulSet", "CloneSet", "Job", "Configuration")
|
||||
var isPortForwardEndpointClusterObjectReferenceFilter = clusterObjectReferenceTypeFilterGenerator("Deployment",
|
||||
"StatefulSet", "CloneSet", "Job", "Service", "HelmRelease")
|
||||
var resourceNameClusterObjectReferenceFilter = func(resourceName string) clusterObjectReferenceFilter {
|
||||
return func(reference common.ClusterObjectReference) bool {
|
||||
return resourceName == reference.Name
|
||||
}
|
||||
}
|
||||
|
||||
func filterResource(inputs []common.ClusterObjectReference, filters ...clusterObjectReferenceFilter) (outputs []common.ClusterObjectReference) {
|
||||
for _, item := range inputs {
|
||||
@@ -419,13 +424,22 @@ func filterClusterObjectRefFromAddonObservability(resources []common.ClusterObje
|
||||
// AskToChooseOneEnvResource will ask users to select one applied resource of the application if more than one
|
||||
// resource is a map for component to applied resources
|
||||
// return the selected ClusterObjectReference
|
||||
func AskToChooseOneEnvResource(app *v1beta1.Application) (*common.ClusterObjectReference, error) {
|
||||
return askToChooseOneResource(app, isWorkloadClusterObjectReferenceFilter)
|
||||
func AskToChooseOneEnvResource(app *v1beta1.Application, resourceName ...string) (*common.ClusterObjectReference, error) {
|
||||
filters := []clusterObjectReferenceFilter{isWorkloadClusterObjectReferenceFilter}
|
||||
for _, n := range resourceName {
|
||||
filters = append(filters, resourceNameClusterObjectReferenceFilter(n))
|
||||
}
|
||||
return askToChooseOneResource(app, filters...)
|
||||
}
|
||||
|
||||
// AskToChooseOnePortForwardEndpoint will ask user to select one applied resource as port forward endpoint
|
||||
func AskToChooseOnePortForwardEndpoint(app *v1beta1.Application) (*common.ClusterObjectReference, error) {
|
||||
return askToChooseOneResource(app, isPortForwardEndpointClusterObjectReferenceFilter)
|
||||
func AskToChooseOnePortForwardEndpoint(app *v1beta1.Application, resourceName ...string) (*common.ClusterObjectReference, error) {
|
||||
filters := []clusterObjectReferenceFilter{isPortForwardEndpointClusterObjectReferenceFilter}
|
||||
for _, n := range resourceName {
|
||||
filters = append(filters, resourceNameClusterObjectReferenceFilter(n))
|
||||
}
|
||||
|
||||
return askToChooseOneResource(app, filters...)
|
||||
}
|
||||
|
||||
func askToChooseOneInApplication(category string, options []string) (decision string, err error) {
|
||||
|
||||
@@ -180,10 +180,7 @@ func (p *provider) ExpandTopology(ctx wfContext.Context, v *value.Value, act wfT
|
||||
return errors.Wrapf(e, "failed to get cluster %s", cluster)
|
||||
}
|
||||
}
|
||||
if ns == "" {
|
||||
ns = p.app.GetNamespace()
|
||||
}
|
||||
if !resourcekeeper.AllowCrossNamespaceResource && ns != p.app.GetNamespace() {
|
||||
if !resourcekeeper.AllowCrossNamespaceResource && (ns != p.app.GetNamespace() && ns != "") {
|
||||
return errors.Errorf("cannot cross namespace")
|
||||
}
|
||||
placement := v1alpha1.PlacementDecision{Cluster: cluster, Namespace: ns}
|
||||
|
||||
@@ -576,7 +576,7 @@ func TestExpandTopology(t *testing.T) {
|
||||
},
|
||||
"topology-by-clusters": {
|
||||
Input: `{inputs:{policies:[{name:"topology-policy",type:"topology",properties:{clusters:["cluster-a"]}}]}}`,
|
||||
Outputs: []v1alpha1.PlacementDecision{{Cluster: "cluster-a", Namespace: "test"}},
|
||||
Outputs: []v1alpha1.PlacementDecision{{Cluster: "cluster-a", Namespace: ""}},
|
||||
},
|
||||
"topology-by-cluster-selector-404": {
|
||||
Input: `{inputs:{policies:[{name:"topology-policy",type:"topology",properties:{clusterSelector:{"key":"bad-value"}}}]}}`,
|
||||
@@ -584,11 +584,11 @@ func TestExpandTopology(t *testing.T) {
|
||||
},
|
||||
"topology-by-cluster-selector": {
|
||||
Input: `{inputs:{policies:[{name:"topology-policy",type:"topology",properties:{clusterSelector:{"key":"value"}}}]}}`,
|
||||
Outputs: []v1alpha1.PlacementDecision{{Cluster: "cluster-a", Namespace: "test"}, {Cluster: "cluster-b", Namespace: "test"}},
|
||||
Outputs: []v1alpha1.PlacementDecision{{Cluster: "cluster-a", Namespace: ""}, {Cluster: "cluster-b", Namespace: ""}},
|
||||
},
|
||||
"topology-by-cluster-label-selector": {
|
||||
Input: `{inputs:{policies:[{name:"topology-policy",type:"topology",properties:{clusterLabelSelector:{"key":"value"}}}]}}`,
|
||||
Outputs: []v1alpha1.PlacementDecision{{Cluster: "cluster-a", Namespace: "test"}, {Cluster: "cluster-b", Namespace: "test"}},
|
||||
Outputs: []v1alpha1.PlacementDecision{{Cluster: "cluster-a", Namespace: ""}, {Cluster: "cluster-b", Namespace: ""}},
|
||||
},
|
||||
"topology-by-cluster-selector-and-namespace-invalid": {
|
||||
Input: `{inputs:{policies:[{name:"topology-policy",type:"topology",properties:{clusterSelector:{"key":"value"},namespace:"override"}}]}}`,
|
||||
|
||||
@@ -68,6 +68,7 @@ const (
|
||||
const (
|
||||
statusEnabled = "enabled"
|
||||
statusDisabled = "disabled"
|
||||
statusSuspend = "suspend"
|
||||
)
|
||||
|
||||
var forceDisable bool
|
||||
@@ -204,7 +205,12 @@ func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient cli
|
||||
return
|
||||
}
|
||||
if name == "velaux" {
|
||||
fmt.Println(`Please use command: "vela port-forward -n vela-system addon-velaux 9082:80" and Select "Cluster: local | Namespace: vela-system | Component: velaux | Kind: Service" to check the dashboard.`)
|
||||
fmt.Println(`To check the initialized admin user name and password by:`)
|
||||
fmt.Println(` vela logs -n vela-system --name apiserver addon-velaux | grep "initialized admin username"`)
|
||||
fmt.Println(`To open the dashboard directly by port-forward:`)
|
||||
fmt.Println(` vela port-forward -n vela-system addon-velaux 9082:80`)
|
||||
fmt.Println(`Select "Cluster: local | Namespace: vela-system | Component: velaux | Kind: Service" from the prompt.`)
|
||||
fmt.Println(`Please refer to https://kubevela.io/docs/reference/addons/velaux for more VelaUX addon installation and visiting method.`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +258,7 @@ Upgrade addon for specific clusters, (local means control plane):
|
||||
}
|
||||
ioStream.Infof("enable addon by local dir: %s \n", addonOrDir)
|
||||
// args[0] is a local path install with local dir
|
||||
name := filepath.Base(addonOrDir)
|
||||
name = filepath.Base(addonOrDir)
|
||||
_, err = pkgaddon.FetchAddonRelatedApp(context.Background(), k8sClient, name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot fetch addon related addon %s", name)
|
||||
@@ -265,7 +271,7 @@ Upgrade addon for specific clusters, (local means control plane):
|
||||
if filepath.IsAbs(addonOrDir) || strings.HasPrefix(addonOrDir, ".") || strings.HasSuffix(addonOrDir, "/") {
|
||||
return fmt.Errorf("addon directory %s not found in local", addonOrDir)
|
||||
}
|
||||
|
||||
name = addonOrDir
|
||||
_, err = pkgaddon.FetchAddonRelatedApp(context.Background(), k8sClient, addonOrDir)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot fetch addon related addon %s", addonOrDir)
|
||||
@@ -399,7 +405,9 @@ func statusAddon(name string, ioStreams cmdutil.IOStreams, cmd *cobra.Command, c
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("addon %s status is %s \n", name, status.AddonPhase)
|
||||
|
||||
fmt.Print(generateAddonInfo(name, status))
|
||||
|
||||
if status.AddonPhase != statusEnabled && status.AddonPhase != statusDisabled {
|
||||
fmt.Printf("diagnose addon info from application %s", pkgaddon.Convert2AppName(name))
|
||||
err := printAppStatus(context.Background(), k8sClient, ioStreams, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, cmd, c)
|
||||
@@ -410,6 +418,35 @@ func statusAddon(name string, ioStreams cmdutil.IOStreams, cmd *cobra.Command, c
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateAddonInfo(name string, status pkgaddon.Status) string {
|
||||
var res string
|
||||
var phase string
|
||||
|
||||
switch status.AddonPhase {
|
||||
case statusEnabled:
|
||||
c := color.New(color.FgGreen)
|
||||
phase = c.Sprintf("%s", status.AddonPhase)
|
||||
case statusSuspend:
|
||||
c := color.New(color.FgRed)
|
||||
phase = c.Sprintf("%s", status.AddonPhase)
|
||||
default:
|
||||
phase = status.AddonPhase
|
||||
}
|
||||
res += fmt.Sprintf("addon %s status is %s \n", name, phase)
|
||||
if len(status.InstalledVersion) != 0 {
|
||||
res += fmt.Sprintf("installedVersion: %s \n", status.InstalledVersion)
|
||||
}
|
||||
|
||||
if len(status.Clusters) != 0 {
|
||||
var ic []string
|
||||
for c := range status.Clusters {
|
||||
ic = append(ic, c)
|
||||
}
|
||||
res += fmt.Sprintf("installedClusters: %s \n", ic)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func listAddons(ctx context.Context, clt client.Client, registry string) error {
|
||||
var addons []*pkgaddon.UIData
|
||||
var err error
|
||||
@@ -504,9 +541,31 @@ func waitApplicationRunning(k8sClient client.Client, addonName string) error {
|
||||
|
||||
}
|
||||
|
||||
// generate the available version
|
||||
// this func put the installed version as the first version and keep the origin order
|
||||
// print ... if available version too much
|
||||
func genAvailableVersionInfo(versions []string, status pkgaddon.Status) string {
|
||||
var v []string
|
||||
|
||||
// put installed-version as the first version and keep the origin order
|
||||
if len(status.InstalledVersion) != 0 {
|
||||
for i, version := range versions {
|
||||
if version == status.InstalledVersion {
|
||||
v = append(v, version)
|
||||
versions = append(versions[:i], versions[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
v = append(v, versions...)
|
||||
|
||||
res := "["
|
||||
for _, version := range versions {
|
||||
var count int
|
||||
for _, version := range v {
|
||||
if count == 3 {
|
||||
// just show newest 3 versions
|
||||
res += "..."
|
||||
break
|
||||
}
|
||||
if version == status.InstalledVersion {
|
||||
col := color.New(color.Bold, color.FgGreen)
|
||||
res += col.Sprintf("%s", version)
|
||||
@@ -514,6 +573,7 @@ func genAvailableVersionInfo(versions []string, status pkgaddon.Status) string {
|
||||
res += version
|
||||
}
|
||||
res += ", "
|
||||
count++
|
||||
}
|
||||
res = strings.TrimSuffix(res, ", ")
|
||||
res += "]"
|
||||
|
||||
@@ -18,8 +18,13 @@ package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
||||
pkgaddon "github.com/oam-dev/kubevela/pkg/addon"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
|
||||
@@ -151,3 +156,62 @@ func TestTransCluster(t *testing.T) {
|
||||
assert.DeepEqual(t, transClusters(s.str), s.res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateStatusIn(t *testing.T) {
|
||||
testcases := []struct {
|
||||
c pkgaddon.Status
|
||||
res []string
|
||||
}{
|
||||
{
|
||||
c: pkgaddon.Status{InstalledVersion: "1.2.1", Clusters: map[string]map[string]interface{}{"cluster1": nil, "cluster2": nil}, AddonPhase: statusEnabled},
|
||||
res: []string{"installedVersion: 1.2.1", "installedClusters: [cluster1 cluster2]", fmt.Sprintf("status is %s", color.New(color.FgGreen).Sprintf(statusEnabled))},
|
||||
},
|
||||
{
|
||||
c: pkgaddon.Status{InstalledVersion: "1.2.3", AddonPhase: statusSuspend},
|
||||
res: []string{"installedVersion: 1.2.3", fmt.Sprintf("status is %s", color.New(color.FgRed).Sprintf(statusSuspend))},
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
res := generateAddonInfo("test", testcase.c)
|
||||
for _, re := range testcase.res {
|
||||
assert.Equal(t, strings.Contains(res, re), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateAvailableVersions(t *testing.T) {
|
||||
type testcase struct {
|
||||
inVersion string
|
||||
versions []string
|
||||
}
|
||||
testcases := []struct {
|
||||
c testcase
|
||||
res string
|
||||
}{
|
||||
{
|
||||
c: testcase{
|
||||
inVersion: "1.2.1",
|
||||
versions: []string{"1.2.1"},
|
||||
},
|
||||
res: fmt.Sprintf("[%s]", color.New(color.Bold, color.FgGreen).Sprintf("1.2.1")),
|
||||
},
|
||||
{
|
||||
c: testcase{
|
||||
inVersion: "1.2.1",
|
||||
versions: []string{"1.2.3", "1.2.2", "1.2.1"},
|
||||
},
|
||||
res: fmt.Sprintf("[%s, 1.2.3, 1.2.2]", color.New(color.Bold, color.FgGreen).Sprintf("1.2.1")),
|
||||
},
|
||||
{
|
||||
c: testcase{
|
||||
inVersion: "1.2.1",
|
||||
versions: []string{"1.2.3", "1.2.2", "1.2.1", "1.2.0"},
|
||||
},
|
||||
res: fmt.Sprintf("[%s, 1.2.3, 1.2.2, ...]", color.New(color.Bold, color.FgGreen).Sprintf("1.2.1")),
|
||||
},
|
||||
}
|
||||
for _, s := range testcases {
|
||||
re := genAvailableVersionInfo(s.c.versions, pkgaddon.Status{InstalledVersion: s.c.inVersion})
|
||||
assert.Equal(t, re, s.res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ import (
|
||||
)
|
||||
|
||||
// defaultConstraint
|
||||
const defaultConstraint = ">= 1.19, <= 1.21"
|
||||
const defaultConstraint = ">= 1.19, <= 1.22"
|
||||
|
||||
const kubevelaInstallerHelmRepoURL = "https://charts.kubevela.net/core/"
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ func NewLogsCommand(c common.Args, order string, ioStreams util.IOStreams) *cobr
|
||||
|
||||
cmd.Flags().StringVarP(&largs.Output, "output", "o", "default", "output format for logs, support: [default, raw, json]")
|
||||
cmd.Flags().StringVarP(&largs.Container, "container", "c", "", "specify container name for output")
|
||||
cmd.Flags().StringVar(&largs.Name, "name", "", "specify resource name for output")
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
return cmd
|
||||
}
|
||||
@@ -96,6 +97,7 @@ type Args struct {
|
||||
Args common.Args
|
||||
Namespace string
|
||||
Container string
|
||||
Name string
|
||||
App *v1beta1.Application
|
||||
}
|
||||
|
||||
@@ -111,7 +113,7 @@ func (l *Args) Run(ctx context.Context, ioStreams util.IOStreams) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selectedRes, err := common.AskToChooseOneEnvResource(l.App)
|
||||
selectedRes, err := common.AskToChooseOneEnvResource(l.App, l.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
"config-dex-connector": {
|
||||
type: "component"
|
||||
annotations: {
|
||||
"alias.config.oam.dev": "Dex Connector"
|
||||
}
|
||||
labels: {
|
||||
"catalog.config.oam.dev": "velacore-config"
|
||||
"type.config.oam.dev": "dex-connector"
|
||||
"multi-cluster.config.oam.dev": "false"
|
||||
}
|
||||
description: "Config information to authenticate Dex connectors"
|
||||
attributes: workload: type: "autodetects.core.oam.dev"
|
||||
}
|
||||
|
||||
template: {
|
||||
output: {
|
||||
apiVersion: "v1"
|
||||
kind: "Secret"
|
||||
metadata: {
|
||||
name: parameter.name
|
||||
namespace: context.namespace
|
||||
labels: {
|
||||
"config.oam.dev/catalog": "velacore-config"
|
||||
"config.oam.dev/type": "dex-connector"
|
||||
"config.oam.dev/multi-cluster": "false"
|
||||
"config.oam.dev/identifier": parameter.name
|
||||
"config.oam.dev/sub-type": parameter.type
|
||||
}
|
||||
}
|
||||
type: "Opaque"
|
||||
|
||||
if parameter.type == "github" {
|
||||
stringData: parameter.github
|
||||
}
|
||||
if parameter.type == "ldap" {
|
||||
stringData: parameter.ldap
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Config type
|
||||
type: "github" | "ldap"
|
||||
github?: {
|
||||
// +usage=GitHub client ID
|
||||
clientID: string
|
||||
// +usage=GitHub client secret
|
||||
clientSecret: string
|
||||
// +usage=GitHub call back URL
|
||||
callbackURL: string
|
||||
}
|
||||
ldap?: {
|
||||
host: string
|
||||
insecureNoSSL: *true | bool
|
||||
insecureSkipVerify: bool
|
||||
startTLS: bool
|
||||
usernamePrompt: string
|
||||
userSearch: {
|
||||
baseDN: string
|
||||
username: string
|
||||
idAttr: string
|
||||
emailAttr: string
|
||||
nameAttr: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
"config-image-registry": {
|
||||
type: "component"
|
||||
annotations: {
|
||||
"alias.config.oam.dev": "Image Registry"
|
||||
}
|
||||
labels: {
|
||||
"catalog.config.oam.dev": "velacore-config"
|
||||
"type.config.oam.dev": "image-registry"
|
||||
"multi-cluster.config.oam.dev": "true"
|
||||
}
|
||||
description: "Config information to authenticate image registry"
|
||||
attributes: workload: type: "autodetects.core.oam.dev"
|
||||
}
|
||||
|
||||
template: {
|
||||
output: {
|
||||
apiVersion: "v1"
|
||||
kind: "Secret"
|
||||
metadata: {
|
||||
name: context.name
|
||||
namespace: context.namespace
|
||||
labels: {
|
||||
"config.oam.dev/catalog": "velacore-config"
|
||||
"config.oam.dev/type": "image-registry"
|
||||
"config.oam.dev/multi-cluster": "true"
|
||||
"config.oam.dev/identifier": parameter.registry
|
||||
"config.oam.dev/sub-type": "auth"
|
||||
}
|
||||
}
|
||||
type: "kubernetes.io/dockerconfigjson"
|
||||
stringData: {
|
||||
if parameter.auth != _|_ {
|
||||
".dockerconfigjson": json.Marshal({
|
||||
"auths": "\(parameter.registry)": {
|
||||
"username": parameter.auth.username
|
||||
"password": parameter.auth.password
|
||||
if parameter.auth.email != _|_ {
|
||||
"email": parameter.auth.email
|
||||
}
|
||||
"auth": base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Image registry FQDN
|
||||
registry: string
|
||||
// +usage=Authenticate the image registry
|
||||
auth?: {
|
||||
// +usage=Private Image registry username
|
||||
username: string
|
||||
// +usage=Private Image registry password
|
||||
password: string
|
||||
// +usage=Private Image registry email
|
||||
email?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
306
vela-templates/definitions/internal/component/cron-task.cue
Normal file
306
vela-templates/definitions/internal/component/cron-task.cue
Normal file
@@ -0,0 +1,306 @@
|
||||
"cron-task": {
|
||||
type: "component"
|
||||
annotations: {}
|
||||
labels: {}
|
||||
description: "Describes cron jobs that run code or a script to completion."
|
||||
attributes: workload: {
|
||||
definition: {
|
||||
apiVersion: "batch/v1beta1"
|
||||
kind: "CronJob"
|
||||
}
|
||||
type: "cronjobs.batch"
|
||||
}
|
||||
}
|
||||
template: {
|
||||
output: {
|
||||
apiVersion: "batch/v1beta1"
|
||||
kind: "CronJob"
|
||||
spec: {
|
||||
schedule: parameter.schedule
|
||||
concurrencyPolicy: parameter.concurrencyPolicy
|
||||
suspend: parameter.suspend
|
||||
successfulJobsHistoryLimit: parameter.successfulJobsHistoryLimit
|
||||
failedJobsHistoryLimit: parameter.failedJobsHistoryLimit
|
||||
if parameter.startingDeadlineSeconds != _|_ {
|
||||
startingDeadlineSeconds: parameter.startingDeadlineSeconds
|
||||
}
|
||||
jobTemplate: {
|
||||
if parameter.labels != _|_ {
|
||||
metadata: labels: parameter.labels
|
||||
}
|
||||
if parameter.annotations != _|_ {
|
||||
metadata: annotations: parameter.annotations
|
||||
}
|
||||
spec: {
|
||||
parallelism: parameter.count
|
||||
completions: parameter.count
|
||||
if parameter.ttlSecondsAfterFinished != _|_ {
|
||||
ttlSecondsAfterFinished: parameter.ttlSecondsAfterFinished
|
||||
}
|
||||
if parameter.activeDeadlineSeconds != _|_ {
|
||||
activeDeadlineSeconds: parameter.activeDeadlineSeconds
|
||||
}
|
||||
backoffLimit: parameter.backoffLimit
|
||||
template: {
|
||||
if parameter.labels != _|_ {
|
||||
metadata: labels: parameter.labels
|
||||
}
|
||||
if parameter.annotations != _|_ {
|
||||
metadata: annotations: parameter.annotations
|
||||
}
|
||||
spec: {
|
||||
restartPolicy: parameter.restart
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
if parameter["imagePullPolicy"] != _|_ {
|
||||
imagePullPolicy: parameter.imagePullPolicy
|
||||
}
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
if parameter["env"] != _|_ {
|
||||
env: parameter.env
|
||||
}
|
||||
if parameter["cpu"] != _|_ {
|
||||
resources: {
|
||||
limits: cpu: parameter.cpu
|
||||
requests: cpu: parameter.cpu
|
||||
}
|
||||
}
|
||||
if parameter["memory"] != _|_ {
|
||||
resources: {
|
||||
limits: memory: parameter.memory
|
||||
requests: memory: parameter.memory
|
||||
}
|
||||
}
|
||||
if parameter["volumes"] != _|_ {
|
||||
volumeMounts: [ for v in parameter.volumes {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
}}]
|
||||
}
|
||||
}]
|
||||
if parameter["volumes"] != _|_ {
|
||||
volumes: [ for v in parameter.volumes {
|
||||
{
|
||||
name: v.name
|
||||
if v.type == "pvc" {
|
||||
persistentVolumeClaim: claimName: v.claimName
|
||||
}
|
||||
if v.type == "configMap" {
|
||||
configMap: {
|
||||
defaultMode: v.defaultMode
|
||||
name: v.cmName
|
||||
if v.items != _|_ {
|
||||
items: v.items
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.type == "secret" {
|
||||
secret: {
|
||||
defaultMode: v.defaultMode
|
||||
secretName: v.secretName
|
||||
if v.items != _|_ {
|
||||
items: v.items
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.type == "emptyDir" {
|
||||
emptyDir: medium: v.medium
|
||||
}
|
||||
}}]
|
||||
}
|
||||
if parameter["imagePullSecrets"] != _|_ {
|
||||
imagePullSecrets: [ for v in parameter.imagePullSecrets {
|
||||
name: v
|
||||
},
|
||||
]
|
||||
}
|
||||
if parameter.hostAliases != _|_ {
|
||||
hostAliases: [ for v in parameter.hostAliases {
|
||||
ip: v.ip
|
||||
hostnames: v.hostnames
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Specify the labels in the workload
|
||||
labels?: [string]: string
|
||||
|
||||
// +usage=Specify the annotations in the workload
|
||||
annotations?: [string]: string
|
||||
|
||||
// +usage=Specify the schedule in Cron format, see https://en.wikipedia.org/wiki/Cron
|
||||
schedule: string
|
||||
|
||||
// +usage=Specify deadline in seconds for starting the job if it misses scheduled
|
||||
startingDeadlineSeconds?: int
|
||||
|
||||
// +usage=suspend subsequent executions
|
||||
suspend: *false | bool
|
||||
|
||||
// +usage=Specifies how to treat concurrent executions of a Job
|
||||
concurrencyPolicy: *"Allow" | "Allow" | "Forbid" | "Replace"
|
||||
|
||||
// +usage=The number of successful finished jobs to retain
|
||||
successfulJobsHistoryLimit: *3 | int
|
||||
|
||||
// +usage=The number of failed finished jobs to retain
|
||||
failedJobsHistoryLimit: *1 | int
|
||||
|
||||
// +usage=Specify number of tasks to run in parallel
|
||||
// +short=c
|
||||
count: *1 | int
|
||||
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
|
||||
// +usage=Specify image pull policy for your service
|
||||
imagePullPolicy?: "Always" | "Never" | "IfNotPresent"
|
||||
|
||||
// +usage=Specify image pull secrets for your service
|
||||
imagePullSecrets?: [...string]
|
||||
|
||||
// +usage=Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never.
|
||||
restart: *"Never" | string
|
||||
|
||||
// +usage=Commands to run in the container
|
||||
cmd?: [...string]
|
||||
|
||||
// +usage=Define arguments by using environment variables
|
||||
env?: [...{
|
||||
// +usage=Environment variable name
|
||||
name: string
|
||||
// +usage=The value of the environment variable
|
||||
value?: string
|
||||
// +usage=Specifies a source the value of this var should come from
|
||||
valueFrom?: {
|
||||
// +usage=Selects a key of a secret in the pod's namespace
|
||||
secretKeyRef: {
|
||||
// +usage=The name of the secret in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the secret to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=Number of CPU units for the service, like `0.5` (0.5 CPU core), `1` (1 CPU core)
|
||||
cpu?: string
|
||||
|
||||
// +usage=Specifies the attributes of the memory resource required for the container.
|
||||
memory?: string
|
||||
|
||||
// +usage=Declare volumes and volumeMounts
|
||||
volumes?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
// +usage=Specify volume type, options: "pvc","configMap","secret","emptyDir"
|
||||
type: "pvc" | "configMap" | "secret" | "emptyDir"
|
||||
if type == "pvc" {
|
||||
claimName: string
|
||||
}
|
||||
if type == "configMap" {
|
||||
defaultMode: *420 | int
|
||||
cmName: string
|
||||
items?: [...{
|
||||
key: string
|
||||
path: string
|
||||
mode: *511 | int
|
||||
}]
|
||||
}
|
||||
if type == "secret" {
|
||||
defaultMode: *420 | int
|
||||
secretName: string
|
||||
items?: [...{
|
||||
key: string
|
||||
path: string
|
||||
mode: *511 | int
|
||||
}]
|
||||
}
|
||||
if type == "emptyDir" {
|
||||
medium: *"" | "Memory"
|
||||
}
|
||||
}]
|
||||
|
||||
// +usage=An optional list of hosts and IPs that will be injected into the pod's hosts file
|
||||
hostAliases?: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
|
||||
// +usage=Limits the lifetime of a Job that has finished
|
||||
ttlSecondsAfterFinished?: int
|
||||
|
||||
// +usage=The duration in seconds relative to the startTime that the job may be continuously active before the system tries to terminate it
|
||||
activeDeadlineSeconds?: int
|
||||
|
||||
// +usage=The number of retries before marking this job failed
|
||||
backoffLimit: *6 | int
|
||||
|
||||
// +usage=Instructions for assessing whether the container is alive.
|
||||
livenessProbe?: #HealthProbe
|
||||
|
||||
// +usage=Instructions for assessing whether the container is in a suitable state to serve traffic.
|
||||
readinessProbe?: #HealthProbe
|
||||
}
|
||||
|
||||
#HealthProbe: {
|
||||
|
||||
// +usage=Instructions for assessing container health by executing a command. Either this attribute or the httpGet attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the httpGet attribute and the tcpSocket attribute.
|
||||
exec?: {
|
||||
// +usage=A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures.
|
||||
command: [...string]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by executing an HTTP GET request. Either this attribute or the exec attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the tcpSocket attribute.
|
||||
httpGet?: {
|
||||
// +usage=The endpoint, relative to the port, to which the HTTP GET request should be directed.
|
||||
path: string
|
||||
// +usage=The TCP socket within the container to which the HTTP GET request should be directed.
|
||||
port: int
|
||||
httpHeaders?: [...{
|
||||
name: string
|
||||
value: string
|
||||
}]
|
||||
}
|
||||
|
||||
// +usage=Instructions for assessing container health by probing a TCP socket. Either this attribute or the exec attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with both the exec attribute and the httpGet attribute.
|
||||
tcpSocket?: {
|
||||
// +usage=The TCP socket within the container that should be probed to assess container health.
|
||||
port: int
|
||||
}
|
||||
|
||||
// +usage=Number of seconds after the container is started before the first probe is initiated.
|
||||
initialDelaySeconds: *0 | int
|
||||
|
||||
// +usage=How often, in seconds, to execute the probe.
|
||||
periodSeconds: *10 | int
|
||||
|
||||
// +usage=Number of seconds after which the probe times out.
|
||||
timeoutSeconds: *1 | int
|
||||
|
||||
// +usage=Minimum consecutive successes for the probe to be considered successful after having failed.
|
||||
successThreshold: *1 | int
|
||||
|
||||
// +usage=Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).
|
||||
failureThreshold: *3 | int
|
||||
}
|
||||
}
|
||||
@@ -499,6 +499,12 @@ template: {
|
||||
|
||||
// +usage=Instructions for assessing whether the container is in a suitable state to serve traffic.
|
||||
readinessProbe?: #HealthProbe
|
||||
|
||||
// +usage=Specify the hostAliases to add
|
||||
hostAliases: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
}
|
||||
|
||||
#HealthProbe: {
|
||||
@@ -541,11 +547,5 @@ template: {
|
||||
|
||||
// +usage=Number of consecutive failures required to determine the container is not alive (liveness probe) or not ready (readiness probe).
|
||||
failureThreshold: *3 | int
|
||||
|
||||
// +usage=Specify the hostAliases to add
|
||||
hostAliases: [...{
|
||||
ip: string
|
||||
hostnames: [...string]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user