From 6469d538be657bb37220ad2cfd0ccf5e4cb251af Mon Sep 17 00:00:00 2001 From: yangsoon Date: Sat, 27 Mar 2021 10:59:23 +0800 Subject: [PATCH] update docs & fix vela cap uninstall. (#1319) --- docs/en/developers/cap-center.md | 90 +++++++++---------- docs/en/helm/component.md | 2 +- docs/en/helm/trait.md | 4 +- references/common/capability.go | 6 +- references/plugins/cluster.go | 41 ++++++++- references/plugins/cluster_test.go | 10 +-- references/plugins/local.go | 12 ++- references/plugins/suit_test.go | 18 ++++ references/plugins/testdata/componentDef.yaml | 42 +++++++++ .../plugins/testdata/websvcComponentDef.yaml | 51 +++++++++++ 10 files changed, 218 insertions(+), 58 deletions(-) create mode 100644 references/plugins/testdata/componentDef.yaml create mode 100644 references/plugins/testdata/websvcComponentDef.yaml diff --git a/docs/en/developers/cap-center.md b/docs/en/developers/cap-center.md index 537e1ca3c..c34a64c00 100644 --- a/docs/en/developers/cap-center.md +++ b/docs/en/developers/cap-center.md @@ -1,6 +1,6 @@ # Managing Capabilities -In KubeVela, developers can install more capabilities (i.e. new workload types and traits) from any GitHub repo that contains OAM definition files. We call these GitHub repos as _Capability Centers_. +In KubeVela, developers can install more capabilities (i.e. new component types and traits) from any GitHub repo that contains OAM definition files. We call these GitHub repos as _Capability Centers_. KubeVela is able to discover OAM definition files in this repo automatically and sync them to your own KubeVela platform. @@ -44,88 +44,88 @@ Or, list all available capabilities in certain center. ```bash $ vela cap ls my-center -NAME CENTER TYPE DEFINITION STATUS APPLIES-TO -kubewatch my-center trait kubewatches.labs.bitnami.com uninstalled [] +NAME CENTER TYPE DEFINITION STATUS APPLIES-TO +clonesetservice my-center componentDefinition clonesets.apps.kruise.io uninstalled [] ``` ## Install a capability from capability center -Now let's try to install the new trait named `kubewatch` from `my-center` to your own KubeVela platform. +Now let's try to install the new component named `clonesetservice` from `my-center` to your own KubeVela platform. -> [KubeWatch](https://github.com/bitnami-labs/kubewatch) is a Kubernetes plugin that watches events and publishes notifications to Slack channel etc. We can use it as a trait to watch important changes of your app and notify the platform administrators via Slack. +You need to install OpenKruise first. -Install `kubewatch` trait from `my-center`. +```shell +helm install kruise https://github.com/openkruise/kruise/releases/download/v0.7.0/kruise-chart.tgz +``` + +Install `clonesetservice` component from `my-center`. ```bash -$ vela cap install my-center/kubewatch -Installing trait capability kubewatch -"my-repo" has been added to your repositories -2020/11/06 16:19:30 [debug] creating 1 resource(s) -2020/11/06 16:19:30 [debug] CRD kubewatches.labs.bitnami.com is already present. Skipping. -2020/11/06 16:19:37 [debug] creating 3 resource(s) -Successfully installed chart (kubewatch) with release name (kubewatch) -Successfully installed capability kubewatch from my-center +$ vela cap install my-center/clonesetservice +Installing component capability clonesetservice +Successfully installed capability clonesetservice from my-center ``` ## Use the newly installed capability -Let's check the `kubewatch` trait appears in your platform firstly: +Let's check the `clonesetservice` appears in your platform firstly: ```bash -$ vela traits -Synchronizing capabilities from clusterβŒ› ... -Sync capabilities successfully βœ… (no changes) -TYPE CATEGORY DESCRIPTION -kubewatch trait Add a watch for resource -... +$ vela components +NAME NAMESPACE WORKLOAD DESCRIPTION +clonesetservice vela-system clonesets.apps.kruise.io Describes long-running, scalable, containerized services + that have a stable network endpoint to receive external + network traffic from customers. If workload type is skipped + for any service defined in Appfile, it will be defaulted to + `webservice` type. ``` Great! Now let's deploy an app via Appfile. - ```bash $ cat << EOF > vela.yaml name: testapp services: testsvc: - type: webservice + type: clonesetservice image: crccheck/hello-world port: 8000 - route: - domain: testsvc.example.com EOF ``` ```bash $ vela up +Parsing vela appfile ... +Load Template ... + +Rendering configs for service (testsvc)... +Writing deploy config to (.vela/deploy.yaml) + +Applying application ... +Checking if app has been deployed... +App has not been deployed, creating a new deployment... +Updating: core.oam.dev/v1alpha2, Kind=HealthScope in default +βœ… App has been deployed πŸš€πŸš€πŸš€ + Port forward: vela port-forward testapp + SSH: vela exec testapp + Logging: vela logs testapp + App status: vela status testapp + Service status: vela status testapp --svc testsvc ``` -Then let's add `kubewatch` as a trait in this Appfile. +then you can Get a cloneset in your environment. -```bash -$ cat << EOF >> vela.yaml - kubewatch: - webhook: https://hooks.slack.com/ -EOF +```shell +$ kubectl get clonesets.apps.kruise.io +NAME DESIRED UPDATED UPDATED_READY READY TOTAL AGE +testsvc 1 1 1 1 1 46s ``` -> The `https://hooks.slack.com/` is the Slack channel that your platform administrators are keeping an eye on. - -Update the deployment: - -``` -$ vela up -``` - -Now, your platform administrators should receive notifications whenever important changes happen to your app. For example, a fresh new deployment. - -![Image of Kubewatch](../../resources/kubewatch-notif.jpg) - ## Uninstall a capability > NOTE: make sure no apps are using the capability before uninstalling. ```bash -$ vela cap uninstall my-center/kubewatch -Successfully removed chart (kubewatch) with release name (kubewatch) +$ vela cap uninstall my-center/clonesetservice +Successfully uninstalled capability clonesetservice ``` diff --git a/docs/en/helm/component.md b/docs/en/helm/component.md index d585d5e97..a65417324 100644 --- a/docs/en/helm/component.md +++ b/docs/en/helm/component.md @@ -50,7 +50,7 @@ In a word, just like the fields shown in the sample, the helm schematic module d Here is an example `Application`. ```yaml -apiVersion: core.oam.dev/v1alpha2 +apiVersion: core.oam.dev/v1beta1 kind: Application metadata: name: myapp diff --git a/docs/en/helm/trait.md b/docs/en/helm/trait.md index d3f692d2e..0ad57c85d 100644 --- a/docs/en/helm/trait.md +++ b/docs/en/helm/trait.md @@ -5,7 +5,7 @@ and [virtualgroup](https://github.com/oam-dev/kubevela/blob/master/docs/examples to a Helm based component. ```yaml -apiVersion: core.oam.dev/v1alpha2 +apiVersion: core.oam.dev/v1beta1 kind: Application metadata: name: myapp @@ -65,7 +65,7 @@ workload. Let's make several changes on the configuration of the sample application. ```yaml -apiVersion: core.oam.dev/v1alpha2 +apiVersion: core.oam.dev/v1beta1 kind: Application metadata: name: myapp diff --git a/references/common/capability.go b/references/common/capability.go index d0576a74f..2d7f672cb 100644 --- a/references/common/capability.go +++ b/references/common/capability.go @@ -314,7 +314,7 @@ func uninstallCap(client client.Client, cap types.Capability, ioStreams cmdutil. case types.TypeScope: return fmt.Errorf("uninstall scope capability was not supported yet") case types.TypeComponentDefinition: - // TODO(yangsoon): support uninstall componentDefinition here + obj = &v1beta1.ComponentDefinition{ObjectMeta: v1.ObjectMeta{Name: cap.Name, Namespace: types.DefaultKubeVelaNS}} } if err := client.Delete(ctx, obj); err != nil { return err @@ -344,7 +344,9 @@ func uninstallCap(client client.Client, cap types.Capability, ioStreams cmdutil. case types.TypeScope: // TODO(wonderflow): add scope remove here. case types.TypeComponentDefinition: - // TODO(yangsoon): add componenetDefinition remove here + if err := os.Remove(filepath.Join(capdir, "", cap.Name)); err != nil { + return err + } } ioStreams.Infof("Successfully uninstalled capability %s", cap.Name) return nil diff --git a/references/plugins/cluster.go b/references/plugins/cluster.go index f14bdc9d8..117e27a0d 100644 --- a/references/plugins/cluster.go +++ b/references/plugins/cluster.go @@ -43,7 +43,7 @@ const DescriptionUndefined = "description not defined" // GetCapabilitiesFromCluster will get capability from K8s cluster func GetCapabilitiesFromCluster(ctx context.Context, namespace string, c common.Args, selector labels.Selector) ([]types.Capability, error) { - workloads, _, err := GetWorkloadsFromCluster(ctx, namespace, c, selector) + workloads, _, err := GetComponentsFromCluster(ctx, namespace, c, selector) if err != nil { return nil, err } @@ -89,6 +89,45 @@ func GetWorkloadsFromCluster(ctx context.Context, namespace string, c common.Arg return templates, templateErrors, nil } +// GetComponentsFromCluster will get capability from K8s cluster +func GetComponentsFromCluster(ctx context.Context, namespace string, c common.Args, selector labels.Selector) ([]types.Capability, []error, error) { + newClient, err := c.GetClient() + if err != nil { + return nil, nil, err + } + dm, err := discoverymapper.New(c.Config) + if err != nil { + return nil, nil, err + } + + var templates []types.Capability + var componentsDefs v1beta1.ComponentDefinitionList + err = newClient.List(ctx, &componentsDefs, &client.ListOptions{Namespace: namespace, LabelSelector: selector}) + if err != nil { + return nil, nil, fmt.Errorf("list ComponentDefinition err: %w", err) + } + + var templateErrors []error + for _, cd := range componentsDefs.Items { + ref, err := util.ConvertWorkloadGVK2Definition(dm, cd.Spec.Workload.Definition) + if err != nil { + templateErrors = append(templateErrors, errors.Wrapf(err, "convert workload definition `%s` failed", cd.Name)) + continue + } + tmp, err := HandleDefinition(cd.Name, ref.Name, cd.Annotations, cd.Spec.Extension, types.TypeComponentDefinition, nil, cd.Spec.Schematic) + if err != nil { + templateErrors = append(templateErrors, errors.Wrapf(err, "handle workload template `%s` failed", cd.Name)) + continue + } + tmp.Namespace = namespace + if tmp, err = validateCapabilities(tmp, dm, cd.Name, ref); err != nil { + return nil, nil, err + } + templates = append(templates, tmp) + } + return templates, templateErrors, nil +} + // GetTraitsFromCluster will get capability from K8s cluster func GetTraitsFromCluster(ctx context.Context, namespace string, c common.Args, selector labels.Selector) ([]types.Capability, []error, error) { newClient, err := c.GetClient() diff --git a/references/plugins/cluster_test.go b/references/plugins/cluster_test.go index ecd43013f..fff00c670 100644 --- a/references/plugins/cluster_test.go +++ b/references/plugins/cluster_test.go @@ -45,7 +45,7 @@ var _ = Describe("DefinitionFiles", func() { deployment := types.Capability{ Namespace: "testdef", Name: DeployName, - Type: types.TypeWorkload, + Type: types.TypeComponentDefinition, CrdName: "deployments.apps", Description: "description not defined", Parameters: []types.Parameter{ @@ -78,7 +78,7 @@ var _ = Describe("DefinitionFiles", func() { websvc := types.Capability{ Namespace: "testdef", Name: WebserviceName, - Type: types.TypeWorkload, + Type: types.TypeComponentDefinition, Description: "description not defined", Parameters: []types.Parameter{{ Name: "env", Type: cue.ListKind, @@ -108,10 +108,10 @@ var _ = Describe("DefinitionFiles", func() { // Notice!! DefinitionPath Object is Cluster Scope object // which means objects created in other DefinitionNamespace will also affect here. - It("getworkload", func() { - workloadDefs, _, err := GetWorkloadsFromCluster(context.Background(), DefinitionNamespace, common.Args{Config: cfg, Schema: scheme}, selector) + It("getcomponents", func() { + workloadDefs, _, err := GetComponentsFromCluster(context.Background(), DefinitionNamespace, common.Args{Config: cfg, Schema: scheme}, selector) Expect(err).Should(BeNil()) - logf.Log.Info(fmt.Sprintf("Getting workload definitions %v", workloadDefs)) + logf.Log.Info(fmt.Sprintf("Getting component definitions %v", workloadDefs)) for i := range workloadDefs { // CueTemplate should always be fulfilled, even those whose CueTemplateURI is assigend, By("check CueTemplate is fulfilled") diff --git a/references/plugins/local.go b/references/plugins/local.go index d258e94f7..99403dd69 100644 --- a/references/plugins/local.go +++ b/references/plugins/local.go @@ -87,9 +87,17 @@ func LoadInstalledCapabilityWithType(userNamespace string, c common.Args, capT t return caps, nil case types.TypeScope: case types.TypeComponentDefinition: - // TODO(yangsoon): support ComponentDefinition here + caps, _, err := GetComponentsFromCluster(context.TODO(), userNamespace, c, nil) + if err != nil { + return nil, err + } + systemCaps, _, err := GetComponentsFromCluster(context.TODO(), types.DefaultKubeVelaNS, c, nil) + if err != nil { + return nil, err + } + caps = append(caps, systemCaps...) + return caps, nil } - return nil, nil } diff --git a/references/plugins/suit_test.go b/references/plugins/suit_test.go index 739afaa83..91a24daee 100644 --- a/references/plugins/suit_test.go +++ b/references/plugins/suit_test.go @@ -55,6 +55,7 @@ var testEnv *envtest.Environment var definitionDir string var td corev1beta1.TraitDefinition var wd, websvcWD corev1beta1.WorkloadDefinition +var cd, websvcCD corev1beta1.ComponentDefinition func TestAPIs(t *testing.T) { RegisterFailHandler(Fail) @@ -101,6 +102,15 @@ var _ = BeforeSuite(func(done Done) { logf.Log.Info("Creating workload definition", "data", wd) Expect(k8sClient.Create(ctx, &wd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + componentdata, err := ioutil.ReadFile("testdata/componentDef.yaml") + Expect(err).Should(BeNil()) + + Expect(yaml.Unmarshal(componentdata, &cd)).Should(BeNil()) + + cd.Namespace = DefinitionNamespace + logf.Log.Info("Creating component definition", "data", cd) + Expect(k8sClient.Create(ctx, &cd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + websvcWorkloadData, err := ioutil.ReadFile("testdata/websvcWorkloadDef.yaml") Expect(err).Should(BeNil()) @@ -109,6 +119,14 @@ var _ = BeforeSuite(func(done Done) { logf.Log.Info("Creating workload definition whose CUE template from remote", "data", &websvcWD) Expect(k8sClient.Create(ctx, &websvcWD)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + websvcComponentDefData, err := ioutil.ReadFile("testdata/websvcComponentDef.yaml") + Expect(err).Should(BeNil()) + + Expect(yaml.Unmarshal(websvcComponentDefData, &websvcCD)).Should(BeNil()) + websvcCD.Namespace = DefinitionNamespace + logf.Log.Info("Creating component definition whose CUE template from remote", "data", &websvcCD) + Expect(k8sClient.Create(ctx, &websvcCD)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + close(done) }, 60) diff --git a/references/plugins/testdata/componentDef.yaml b/references/plugins/testdata/componentDef.yaml new file mode 100644 index 000000000..404f8a884 --- /dev/null +++ b/references/plugins/testdata/componentDef.yaml @@ -0,0 +1,42 @@ +apiVersion: core.oam.dev/v1beta1 +kind: ComponentDefinition +metadata: + name: deployments.testapps + labels: + usecase: forplugintest +spec: + workload: + definition: + apiVersion: apps/v1 + kind: Deployment + extension: + template: | + output: { + apiVersion: "apps/v1" + kind: "Deployment" + metadata: name: context.name + spec: { + containers: [{ + image: parameter.image + name: context.name + env: parameter.env + ports: [{ + containerPort: parameter.port + protocol: "TCP" + name: "default" + }] + }] + } + } + parameter: { + // +usage=Which image would you like to use for your service + // +short=i + image: string + // +usage=Which port do you want customer traffic sent to + // +short=p + port: *8080 | int + env: [...{ + name: string + value: string + }] + } \ No newline at end of file diff --git a/references/plugins/testdata/websvcComponentDef.yaml b/references/plugins/testdata/websvcComponentDef.yaml new file mode 100644 index 000000000..c5dd1a439 --- /dev/null +++ b/references/plugins/testdata/websvcComponentDef.yaml @@ -0,0 +1,51 @@ +apiVersion: core.oam.dev/v1beta1 +kind: ComponentDefinition +metadata: + name: webservice.testapps + labels: + usecase: forplugintest +spec: + workload: + definition: + apiVersion: apps/v1 + kind: Deployment + extension: + template: | + output: { + apiVersion: "apps/v1" + kind: "Deployment" + metadata: name: context.name + spec: { + replicas: 1 + podSpec: { + containers: [{ + name: context.name + image: parameter.image + env: parameter.env + ports: [{ + containerPort: parameter.port + }] + }] + } + } + } + parameter: { + // +usage=Which image would you like to use for your service + // +short=i + image: string + + // +usage=Which port do you want customer traffic sent to + // +short=p + port: *6379 | int + + env: [...{ + name: string + value?: string + valueFrom?: { + secretKeyRef: { + name: string + key: string + } + } + }] + } \ No newline at end of file