Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

Commit

Permalink
add mutating for appConfig to support name/properties fomart of trait
Browse files Browse the repository at this point in the history
Signed-off-by: 天元 <jianbo.sjb@alibaba-inc.com>
  • Loading branch information
wonderflow committed Oct 9, 2020
1 parent 763a9ef commit 27094af
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 7 deletions.
16 changes: 16 additions & 0 deletions charts/oam-kubernetes-runtime/templates/webhook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ metadata:
labels:
{{- include "oam-kubernetes-runtime.selectorLabels" . | nindent 4 }}
webhooks:
- name: "mutate.applicationconfigurations.core.oam.dev"
clientConfig:
service:
name: {{ template "oam-kubernetes-runtime.name" . }}-webhook
namespace: {{.Release.Namespace}}
path: /mutating-core-oam-dev-v1alpha2-applicationconfigurations
caBundle: "{{.Values.certificate.caBundle}}"
rules:
- apiGroups: ["core.oam.dev"]
apiVersions: ["v1alpha2"]
operations: ["CREATE", "UPDATE"]
resources: ["applicationconfigurations"]
scope: "Namespaced"
admissionReviewVersions: ["v1beta1"]
failurePolicy: Fail
timeoutSeconds: 5
- name: "mutate.component.core.oam.dev"
clientConfig:
service:
Expand Down
5 changes: 5 additions & 0 deletions pkg/oam/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const (
LabelAppComponentRevision = "app.oam.dev/revision"
// LabelOAMResourceType whether a CR is workload or trait
LabelOAMResourceType = "app.oam.dev/resourceType"

// WorkloadTypeLabel indicates the type of the workloadDefinition
WorkloadTypeLabel = "workload.oam.dev/type"
// TraitTypeLabel indicates the type of the traitDefinition
TraitTypeLabel = "trait.oam.dev/type"
)

const (
Expand Down
3 changes: 0 additions & 3 deletions pkg/oam/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ import (
runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
)

// WorkloadTypeLabel indicates the type of the workloadDefinition
const WorkloadTypeLabel = "workload.oam.dev/type"

// ScopeKind contains the type metadata for a kind of an OAM scope resource.
type ScopeKind schema.GroupVersionKind

Expand Down
7 changes: 6 additions & 1 deletion pkg/oam/util/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,16 @@ func PassLabelAndAnnotation(parentObj oam.Object, childObj labelAnnotationObject
// Now the definition name of a resource could also be defined as `definition.oam.dev/name` in `metadata.annotations`
func GetDefinitionName(u *unstructured.Unstructured) string {
if labels := u.GetLabels(); labels != nil {
if resourceType, ok := labels[oam.LabelOAMResourceType]; ok && resourceType == "WORKLOAD" {
if resourceType, ok := labels[oam.LabelOAMResourceType]; ok && resourceType == oam.ResourceTypeWorkload {
if definitionName, ok := labels[oam.WorkloadTypeLabel]; ok {
return definitionName
}
}
if resourceType, ok := labels[oam.LabelOAMResourceType]; ok && resourceType == oam.ResourceTypeTrait {
if definitionName, ok := labels[oam.TraitTypeLabel]; ok {
return definitionName
}
}
}
group, _ := APIVersion2GroupVersion(u.GetAPIVersion())
resources := []string{Kind2Resource(u.GetKind())}
Expand Down
3 changes: 2 additions & 1 deletion pkg/webhook/v1alpha2/admit.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (

// Add will be called in main and register all validation handlers
func Add(mgr manager.Manager) {
applicationconfiguration.Register(mgr)
applicationconfiguration.RegisterValidatingHandler(mgr)
applicationconfiguration.RegisterMutatingHandler(mgr)
component.RegisterMutatingHandler(mgr)
component.RegisterValidatingHandler(mgr)
}
174 changes: 174 additions & 0 deletions pkg/webhook/v1alpha2/applicationconfiguration/mutating_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package applicationconfiguration

import (
"context"
"encoding/json"
"fmt"
"net/http"
"reflect"

"github.com/davecgh/go-spew/spew"
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/crossplane/oam-kubernetes-runtime/pkg/oam"
"github.com/crossplane/oam-kubernetes-runtime/pkg/oam/util"
)

const (
// TraitTypeField is the special field indicate the type of the traitDefinition
TraitTypeField = "name"
// TraitSpecField indicate the spec of the trait in ApplicationConfiguration
TraitSpecField = "properties"
)

// MutatingHandler handles Component
type MutatingHandler struct {
Client client.Client

// Decoder decodes objects
Decoder *admission.Decoder
}

// log is for logging in this package.
var mutatelog = logf.Log.WithName("applicationconfiguration mutate webhook")

var _ admission.Handler = &MutatingHandler{}

// Handle handles admission requests.
func (h *MutatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
obj := &v1alpha2.ApplicationConfiguration{}

err := h.Decoder.Decode(req, obj)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
// mutate the object
if err := h.Mutate(obj); err != nil {
mutatelog.Error(err, "failed to mutate the applicationConfiguration", "name", obj.Name)
return admission.Errored(http.StatusBadRequest, err)
}
mutatelog.Info("Print the mutated obj", "obj name", obj.Name, "mutated obj", spew.Sdump(obj.Spec))

marshalled, err := json.Marshal(obj)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}

resp := admission.PatchResponseFromRaw(req.AdmissionRequest.Object.Raw, marshalled)
if len(resp.Patches) > 0 {
mutatelog.Info("admit ApplicationConfiguration",
"namespace", obj.Namespace, "name", obj.Name, "patches", util.JSONMarshal(resp.Patches))
}
return resp
}

// Mutate sets all the default value for the Component
func (h *MutatingHandler) Mutate(obj *v1alpha2.ApplicationConfiguration) error {
mutatelog.Info("mutate", "name", obj.Name)

for compIdx, comp := range obj.Spec.Components {
var updated bool
for idx, tr := range comp.Traits {
var content map[string]interface{}
if err := json.Unmarshal(tr.Trait.Raw, &content); err != nil {
return err
}
rawByte, mutated, err := h.mutateTrait(content, comp.ComponentName, obj.GetNamespace())
if err != nil {
return err
}
if !mutated {
continue
}
tr.Trait.Raw = rawByte
comp.Traits[idx] = tr
updated = true
}
if updated {
obj.Spec.Components[compIdx] = comp
}
}

return nil
}

func (h *MutatingHandler) mutateTrait(content map[string]interface{}, compName, namespace string) ([]byte, bool, error) {
if content[TraitTypeField] == nil {
return nil, false, nil
}
traitType, ok := content[TraitTypeField].(string)
if !ok {
return nil, false, fmt.Errorf("name of trait should be string instead of %s", reflect.TypeOf(content[TraitTypeField]))
}
mutatelog.Info("the trait refers to traitDefinition by name", "compName", compName, "trait name", traitType)
// Fetch the corresponding traitDefinition CR, the traitDefinition crd is cluster scoped
traitDefinition := &v1alpha2.TraitDefinition{}
if err := h.Client.Get(context.TODO(), types.NamespacedName{Name: traitType}, traitDefinition); err != nil {
return nil, false, err
}
// fetch the CRDs definition
customResourceDefinition := &crdv1.CustomResourceDefinition{}
if err := h.Client.Get(context.TODO(), types.NamespacedName{Name: traitDefinition.Spec.Reference.Name}, customResourceDefinition); err != nil {
return nil, false, err
}
// reconstruct the workload CR
delete(content, TraitTypeField)

if content[TraitSpecField] != nil {
content["spec"] = content[TraitSpecField]
delete(content, TraitSpecField)
}

trait := unstructured.Unstructured{
Object: content,
}
// find out the GVK from the CRD definition and set
apiVersion := metav1.GroupVersion{
Group: customResourceDefinition.Spec.Group,
Version: customResourceDefinition.Spec.Versions[0].Name,
}.String()
trait.SetAPIVersion(apiVersion)
trait.SetKind(customResourceDefinition.Spec.Names.Kind)
mutatelog.Info("Set the trait GVK", "trait api version", trait.GetAPIVersion(), "trait Kind", trait.GetKind())
// copy namespace/label/annotation to the workload and add traitType label
trait.SetNamespace(namespace)
trait.SetLabels(map[string]string{oam.TraitTypeLabel: traitType})
// copy back the object
rawBye, err := json.Marshal(trait.Object)
if err != nil {
return nil, false, err
}
return rawBye, true, nil
}

var _ inject.Client = &MutatingHandler{}

// InjectClient injects the client into the ComponentMutatingHandler
func (h *MutatingHandler) InjectClient(c client.Client) error {
h.Client = c
return nil
}

var _ admission.DecoderInjector = &MutatingHandler{}

// InjectDecoder injects the decoder into the ComponentMutatingHandler
func (h *MutatingHandler) InjectDecoder(d *admission.Decoder) error {
h.Decoder = d
return nil
}

// RegisterMutatingHandler will register component mutation handler to the webhook
func RegisterMutatingHandler(mgr manager.Manager) {
server := mgr.GetWebhookServer()
server.Register("/mutating-core-oam-dev-v1alpha2-applicationconfigurations", &webhook.Admission{Handler: &MutatingHandler{}})
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ func (h *ValidatingHandler) InjectDecoder(d *admission.Decoder) error {
return nil
}

// Register will register application configuration validation to webhook
func Register(mgr manager.Manager) {
// RegisterValidatingHandler will register application configuration validation to webhook
func RegisterValidatingHandler(mgr manager.Manager) {
server := mgr.GetWebhookServer()
server.Register("/validating-core-oam-dev-v1alpha2-applicationconfigurations", &webhook.Admission{Handler: &ValidatingHandler{}})
}

0 comments on commit 27094af

Please sign in to comment.