mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-19 04:26:39 +00:00
Feat(lang): The policy controller generate the OpenAPI schema (#3703)
* Fix: The policy controller generate the OpenAPI schema (#3683) Signed-off-by: DESKTOP-FV8IFEC\10690 <1069029381@qq.com> * Feat(lang): The policy controller generate the OpenAPI schema (#3683) The policy controller does not generate the OpenAPI schema #3683 add CapabilityPolicyDefinition struct comment add StoreOpenAPISchema of CapabilityPolicyDefinition comment Signed-off-by: fengkang <fengkangb@digitalchina.com> * Feat(lang): The policy controller generate the OpenAPI schema (#3683) The policy controller does not generate the OpenAPI schema #3683 fix unit-test Signed-off-by: fengkang <fengkangb@digitalchina.com> * Feat(lang): The policy controller generate the OpenAPI schema (#3683) fix unit-test about policydefinition_controller_test.go The policy controller does not generate the OpenAPI schema #3683 fix unit-test Signed-off-by: fengkang <fengkangb@digitalchina.com> * Feat(lang): The policy controller generate the OpenAPI schema (#3683) fix unit-test about policydefinition_controller_test.go Signed-off-by: fengkang <fengkangb@digitalchina.com> * Feat(lang): The policy controller generate the OpenAPI schema (#3683) gen crd Signed-off-by: fengkang <fengkangb@digitalchina.com> * Feat(lang): The policy controller generate the OpenAPI schema (#3683) make reviewable Signed-off-by: fengkang01 <fengkangb@digitalchina.com> Co-authored-by: DESKTOP-FV8IFEC\10690 <1069029381@qq.com>
This commit is contained in:
@@ -43,6 +43,9 @@ type PolicyDefinitionStatus struct {
|
||||
// ConditionedStatus reflects the observed status of a resource
|
||||
condition.ConditionedStatus `json:",inline"`
|
||||
|
||||
// ConfigMapRef refer to a ConfigMap which contains OpenAPI V3 JSON schema of Component parameters.
|
||||
ConfigMapRef string `json:"configMapRef,omitempty"`
|
||||
|
||||
// LatestRevision of the component definition
|
||||
// +optional
|
||||
LatestRevision *common.Revision `json:"latestRevision,omitempty"`
|
||||
|
||||
@@ -3390,6 +3390,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -636,6 +636,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -244,6 +244,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
|
||||
V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -3390,6 +3390,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -636,6 +636,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -244,6 +244,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
|
||||
V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -3390,6 +3390,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -636,6 +636,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains
|
||||
OpenAPI V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -244,6 +244,10 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
configMapRef:
|
||||
description: ConfigMapRef refer to a ConfigMap which contains OpenAPI
|
||||
V3 JSON schema of Component parameters.
|
||||
type: string
|
||||
latestRevision:
|
||||
description: LatestRevision of the component definition
|
||||
properties:
|
||||
|
||||
@@ -190,7 +190,7 @@ func (d *definitionUsecaseImpl) listDefinitions(ctx context.Context, list *unstr
|
||||
|
||||
// DetailDefinition get definition detail
|
||||
func (d *definitionUsecaseImpl) DetailDefinition(ctx context.Context, name, defType string) (*apisv1.DetailDefinitionResponse, error) {
|
||||
if !utils.StringsContain([]string{"component", "trait", "workflowstep"}, defType) {
|
||||
if !utils.StringsContain([]string{"component", "trait", "workflowstep", "policy"}, defType) {
|
||||
return nil, bcode.ErrDefinitionTypeNotSupport
|
||||
}
|
||||
var cm v1.ConfigMap
|
||||
|
||||
+23
@@ -124,6 +124,29 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
r.record.Event(&policydefinition, event.Warning("failed to garbage collect DefinitionRevision of type PolicyDefinition", err))
|
||||
}
|
||||
|
||||
def := utils.NewCapabilityPolicyDef(&policydefinition)
|
||||
def.Name = req.NamespacedName.Name
|
||||
// Store the parameter of policyDefinition to configMap
|
||||
cmName, err := def.StoreOpenAPISchema(ctx, r.Client, r.pd, req.Namespace, req.Name, defRev.Name)
|
||||
if err != nil {
|
||||
klog.InfoS("Could not capability in ConfigMap", "err", err)
|
||||
r.record.Event(&(policydefinition), event.Warning("Could not store capability in ConfigMap", err))
|
||||
return ctrl.Result{}, util.PatchCondition(ctx, r, &(policydefinition),
|
||||
condition.ReconcileError(fmt.Errorf(util.ErrStoreCapabilityInConfigMap, def.Name, err)))
|
||||
}
|
||||
|
||||
if policydefinition.Status.ConfigMapRef != cmName {
|
||||
policydefinition.Status.ConfigMapRef = cmName
|
||||
if err := r.UpdateStatus(ctx, &policydefinition); err != nil {
|
||||
klog.InfoS("Could not update policyDefinition Status", "err", err)
|
||||
r.record.Event(&policydefinition, event.Warning("cannot update PolicyDefinition Status", err))
|
||||
return ctrl.Result{}, util.PatchCondition(ctx, r, &policydefinition,
|
||||
condition.ReconcileError(fmt.Errorf(util.ErrUpdatePolicyDefinition, policydefinition.Name, err)))
|
||||
}
|
||||
klog.InfoS("Successfully updated the status.configMapRef of the PolicyDefinition", "policyDefinition",
|
||||
klog.KRef(req.Namespace, req.Name), "status.configMapRef", cmName)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
|
||||
Copyright 2021 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 policydefinition
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
corev1 "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/reconcile"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/testutil"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Apply PolicyDefinition to store its schema to ConfigMap Test", func() {
|
||||
ctx := context.Background()
|
||||
var ns corev1.Namespace
|
||||
|
||||
Context("When the PolicyDefinition is valid, but the namespace doesn't exist, should occur errors", func() {
|
||||
It("Apply PolicyDefinition", func() {
|
||||
By("Apply PolicyDefinition")
|
||||
var validPolicyDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply raw kubernetes objects for your policy
|
||||
name: apply-object
|
||||
namespace: not-exist
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1alpha1"
|
||||
kind: "EnvBinding"
|
||||
spec: {
|
||||
engine: parameter.engine
|
||||
appTemplate: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: context.appName
|
||||
namespace: context.namespace
|
||||
}
|
||||
spec: {
|
||||
components: context.components
|
||||
}
|
||||
}
|
||||
envs: parameter.envs
|
||||
}
|
||||
}
|
||||
|
||||
#Env: {
|
||||
name: string
|
||||
patch: components: [...{
|
||||
name: string
|
||||
type: string
|
||||
properties: {...}
|
||||
}]
|
||||
placement: clusterSelector: {
|
||||
labels?: [string]: string
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
engine: *"ocm" | string
|
||||
envs: [...#Env]
|
||||
}
|
||||
`
|
||||
|
||||
var def v1beta1.PolicyDefinition
|
||||
Expect(yaml.Unmarshal([]byte(validPolicyDefinition), &def)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &def)).Should(Not(Succeed()))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the PolicyDefinition is valid, should create a ConfigMap", func() {
|
||||
var PolicyDefinitionName = "policy-obj"
|
||||
var namespace = "ns-plc-def-1"
|
||||
req := reconcile.Request{NamespacedName: client.ObjectKey{Name: PolicyDefinitionName, Namespace: namespace}}
|
||||
|
||||
It("Apply PolicyDefinition", func() {
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
By("Create a namespace")
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Apply PolicyDefinition")
|
||||
var validPolicyDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply raw kubernetes objects for your Policy
|
||||
name: policy-obj
|
||||
namespace: ns-plc-def-1
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1alpha1"
|
||||
kind: "EnvBinding"
|
||||
spec: {
|
||||
engine: parameter.engine
|
||||
appTemplate: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: context.appName
|
||||
namespace: context.namespace
|
||||
}
|
||||
spec: {
|
||||
components: context.components
|
||||
}
|
||||
}
|
||||
envs: parameter.envs
|
||||
}
|
||||
}
|
||||
|
||||
#Env: {
|
||||
name: string
|
||||
patch: components: [...{
|
||||
name: string
|
||||
type: string
|
||||
properties: {...}
|
||||
}]
|
||||
placement: clusterSelector: {
|
||||
labels?: [string]: string
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
engine: *"ocm" | string
|
||||
envs: [...#Env]
|
||||
}
|
||||
`
|
||||
|
||||
var def v1beta1.PolicyDefinition
|
||||
Expect(yaml.Unmarshal([]byte(validPolicyDefinition), &def)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &def)).Should(Succeed())
|
||||
testutil.ReconcileRetry(&r, req)
|
||||
|
||||
By("Check whether ConfigMap is created")
|
||||
var cm corev1.ConfigMap
|
||||
name := fmt.Sprintf("policy-%s%s", types.CapabilityConfigMapNamePrefix, PolicyDefinitionName)
|
||||
Eventually(func() bool {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &cm)
|
||||
return err == nil
|
||||
}, 30*time.Second, time.Second).Should(BeTrue())
|
||||
Expect(cm.Data[types.OpenapiV3JSONSchema]).Should(Not(Equal("")))
|
||||
Expect(cm.Labels["definition.oam.dev/name"]).Should(Equal(PolicyDefinitionName))
|
||||
|
||||
By("Check whether ConfigMapRef refer to right")
|
||||
Eventually(func() string {
|
||||
_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: def.Namespace, Name: def.Name}, &def)
|
||||
return def.Status.ConfigMapRef
|
||||
}, 30*time.Second, time.Second).Should(Equal(name))
|
||||
|
||||
By("Delete the policy")
|
||||
Expect(k8sClient.Delete(ctx, &def)).Should(Succeed())
|
||||
testutil.ReconcileRetry(&r, req)
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the PolicyDefinition is invalid, should report issues", func() {
|
||||
var invalidPolicyDefinitionName = "invalid-plc1"
|
||||
var namespace = "ns-plc-def2"
|
||||
BeforeEach(func() {
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
By("Create a namespace")
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
})
|
||||
|
||||
It("Applying invalid PolicyDefinition", func() {
|
||||
By("Apply the PolicyDefinition")
|
||||
var invalidPolicyDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Apply raw kubernetes objects for your policy
|
||||
name: invalid-plc1
|
||||
namespace: ns-plc-def2
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "core.oam.dev/v1alpha1"
|
||||
kind: "EnvBinding"
|
||||
spec: {
|
||||
engine: parameter.engine
|
||||
appTemplate: {
|
||||
apiVersion: "core.oam.dev/v1beta1"
|
||||
kind: "Application"
|
||||
metadata: {
|
||||
name: context.appName
|
||||
namespace: context.namespace
|
||||
}
|
||||
spec: {
|
||||
components: context.components
|
||||
}
|
||||
}
|
||||
envs: parameter.envs
|
||||
}
|
||||
}
|
||||
|
||||
#Env: {
|
||||
name: string
|
||||
patch: components: [...{
|
||||
name: string
|
||||
type: string
|
||||
properties: {...}
|
||||
}]
|
||||
placement: clusterSelector: {
|
||||
labels?: [string]: string
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
`
|
||||
|
||||
var invalidDef v1beta1.PolicyDefinition
|
||||
Expect(yaml.Unmarshal([]byte(invalidPolicyDefinition), &invalidDef)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &invalidDef)).Should(Succeed())
|
||||
gotPolicyDefinition := &v1beta1.PolicyDefinition{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: invalidPolicyDefinitionName, Namespace: namespace}, gotPolicyDefinition)).Should(BeNil())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -72,6 +72,7 @@ const (
|
||||
typeTraitDefinition = "trait"
|
||||
typeComponentDefinition = "component"
|
||||
typeWorkflowStepDefinition = "workflowstep"
|
||||
typePolicyStepDefinition = "policy"
|
||||
)
|
||||
|
||||
// ErrNoSectionParameterInCue means there is not parameter section in Cue template of a workload
|
||||
@@ -567,6 +568,76 @@ func (def *CapabilityStepDefinition) StoreOpenAPISchema(ctx context.Context, k8s
|
||||
return cmName, nil
|
||||
}
|
||||
|
||||
// CapabilityPolicyDefinition is the Capability struct for PolicyDefinition
|
||||
type CapabilityPolicyDefinition struct {
|
||||
Name string `json:"name"`
|
||||
PolicyDefinition v1beta1.PolicyDefinition `json:"policyDefinition"`
|
||||
|
||||
CapabilityBaseDefinition
|
||||
}
|
||||
|
||||
// NewCapabilityPolicyDef will create a CapabilityPolicyDefinition
|
||||
func NewCapabilityPolicyDef(policydefinition *v1beta1.PolicyDefinition) CapabilityPolicyDefinition {
|
||||
var def CapabilityPolicyDefinition
|
||||
def.Name = policydefinition.Name
|
||||
def.PolicyDefinition = *policydefinition.DeepCopy()
|
||||
return def
|
||||
}
|
||||
|
||||
// GetOpenAPISchema gets OpenAPI v3 schema by StepDefinition name
|
||||
func (def *CapabilityPolicyDefinition) GetOpenAPISchema(pd *packages.PackageDiscover, name string) ([]byte, error) {
|
||||
capability, err := appfile.ConvertTemplateJSON2Object(name, nil, def.PolicyDefinition.Spec.Schematic)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert WorkflowStepDefinition to Capability Object")
|
||||
}
|
||||
return getOpenAPISchema(capability, pd)
|
||||
}
|
||||
|
||||
// StoreOpenAPISchema stores OpenAPI v3 schema from StepDefinition in ConfigMap
|
||||
func (def *CapabilityPolicyDefinition) StoreOpenAPISchema(ctx context.Context, k8sClient client.Client,
|
||||
pd *packages.PackageDiscover, namespace, name, revName string) (string, error) {
|
||||
var jsonSchema []byte
|
||||
var err error
|
||||
|
||||
jsonSchema, err = def.GetOpenAPISchema(pd, name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate OpenAPI v3 JSON schema for capability %s: %w", def.Name, err)
|
||||
}
|
||||
|
||||
policyDefinition := def.PolicyDefinition
|
||||
ownerReference := []metav1.OwnerReference{{
|
||||
APIVersion: policyDefinition.APIVersion,
|
||||
Kind: policyDefinition.Kind,
|
||||
Name: policyDefinition.Name,
|
||||
UID: policyDefinition.GetUID(),
|
||||
Controller: pointer.BoolPtr(true),
|
||||
BlockOwnerDeletion: pointer.BoolPtr(true),
|
||||
}}
|
||||
cmName, err := def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, policyDefinition.Name, typePolicyStepDefinition, policyDefinition.Labels, nil, jsonSchema, ownerReference)
|
||||
if err != nil {
|
||||
return cmName, err
|
||||
}
|
||||
|
||||
// Create a configmap to store parameter for each definitionRevision
|
||||
defRev := new(v1beta1.DefinitionRevision)
|
||||
if err = k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revName}, defRev); err != nil {
|
||||
return "", err
|
||||
}
|
||||
ownerReference = []metav1.OwnerReference{{
|
||||
APIVersion: defRev.APIVersion,
|
||||
Kind: defRev.Kind,
|
||||
Name: defRev.Name,
|
||||
UID: defRev.GetUID(),
|
||||
Controller: pointer.BoolPtr(true),
|
||||
BlockOwnerDeletion: pointer.BoolPtr(true),
|
||||
}}
|
||||
_, err = def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, revName, typePolicyStepDefinition, defRev.Spec.PolicyDefinition.Labels, nil, jsonSchema, ownerReference)
|
||||
if err != nil {
|
||||
return cmName, err
|
||||
}
|
||||
return cmName, nil
|
||||
}
|
||||
|
||||
// CapabilityBaseDefinition is the base struct for CapabilityWorkloadDefinition and CapabilityTraitDefinition
|
||||
type CapabilityBaseDefinition struct {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user