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:
fengkang01
2022-04-22 15:53:20 +08:00
committed by GitHub
co-authored by lnx01
parent c126a5b272
commit 54e333148f
14 changed files with 402 additions and 1 deletions
@@ -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:
+1 -1
View File
@@ -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
@@ -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
}
@@ -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())
})
})
})
+71
View File
@@ -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 {
}