diff --git a/examples/knative-serving/README.md b/examples/knative-serving/README.md new file mode 100644 index 00000000..e7c6442b --- /dev/null +++ b/examples/knative-serving/README.md @@ -0,0 +1,38 @@ +#### Install knative serving on host cluster +``` +# installing knative serving CRDs +kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.6.0/serving-crds.yaml +# installing knative serving core +kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.6.0/serving-core.yaml +# installing kourier networking layer +kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.6.0/kourier.yaml +# configure serving to use kourier by default +sleep 10 +kubectl patch configmap/config-network \ + --namespace knative-serving \ + --type merge \ + --patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}' +# configure serving to use sslip.io as default DNS +kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.6.0/serving-default-domain.yaml + +``` + +#### Deploy vcluster with knative plugin +``` +vcluster create vcluster -f https://raw.githubusercontent.com/loft-sh/vcluster-generic-crd-sync-plugin/main/examples/knative-serving/plugin.yaml +``` + +#### Create a knative service +``` +cat << EOF | kubectl apply -f - +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: hello +spec: + template: + spec: + containers: + - image: gcr.io/google-samples/hello-app:1.0 +EOF +``` \ No newline at end of file diff --git a/examples/knative-serving/plugin.yaml b/examples/knative-serving/plugin.yaml new file mode 100644 index 00000000..b22e7eec --- /dev/null +++ b/examples/knative-serving/plugin.yaml @@ -0,0 +1,139 @@ +plugin: + knative-serving: + image: ghcr.io/loft-sh/vcluster-generic-crd-plugin:latest + imagePullPolicy: Always + rbac: + role: + extraRules: + - apiGroups: ["serving.knative.dev"] + resources: ["services", "configurations", "revisions", "routes"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + clusterRole: + extraRules: + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["get", "list", "watch"] + env: + - name: CONFIG + value: |- + version: v1beta1 + mappings: + - fromVirtualCluster: + apiVersion: serving.knative.dev/v1 + kind: Service + patches: [] + reversePatches: + - op: copyFromObject + fromPath: status + path: status + - op: copyFromObject + fromPath: spec.template.spec.containerConcurrency + path: spec.template.spec.containerConcurrency + conditions: + - empty: true + path: spec.template.spec.containerConcurrency + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.containers[0].name + path: spec.template.spec.containers[0].name + conditions: + - empty: true + path: spec.template.spec.containers[0].name + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.containers[0].readinessProbe + path: spec.template.spec.containers[0].readinessProbe + conditions: + - empty: true + path: spec.template.spec.containers[0].readinessProbe + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.timeoutSeconds + path: spec.template.spec.timeoutSeconds + conditions: + - empty: true + path: spec.template.spec.timeoutSeconds + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.responseStartTimeoutSeconds + path: spec.template.spec.responseStartTimeoutSeconds + conditions: + - empty: true + path: spec.template.spec.responseStartTimeoutSeconds + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.idleTimeoutSeconds + path: spec.template.spec.idleTimeoutSeconds + conditions: + - empty: true + path: spec.template.spec.idleTimeoutSeconds + ignore: true + - op: copyFromObject + fromPath: spec.traffic + path: spec.traffic + conditions: + - empty: true + path: spec.traffic + ignore: true + - fromVirtualCluster: + apiVersion: serving.knative.dev/v1 + kind: Configuration + reversePatches: + - op: copyFromObject + fromPath: status + path: status + - op: copyFromObject + fromPath: spec.template.spec.containerConcurrency + path: spec.template.spec.containerConcurrency + conditions: + - empty: true + path: spec.template.spec.containerConcurrency + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.containers[0].name + path: spec.template.spec.containers[0].name + conditions: + - empty: true + path: spec.template.spec.containers[0].name + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.containers[0].readinessProbe + path: spec.template.spec.containers[0].readinessProbe + conditions: + - empty: true + path: spec.template.spec.containers[0].readinessProbe + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.timeoutSeconds + path: spec.template.spec.timeoutSeconds + conditions: + - empty: true + path: spec.template.spec.timeoutSeconds + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.responseStartTimeoutSeconds + path: spec.template.spec.responseStartTimeoutSeconds + conditions: + - empty: true + path: spec.template.spec.responseStartTimeoutSeconds + ignore: true + - op: copyFromObject + fromPath: spec.template.spec.idleTimeoutSeconds + path: spec.template.spec.idleTimeoutSeconds + conditions: + - empty: true + path: spec.template.spec.idleTimeoutSeconds + ignore: true + - fromVirtualCluster: + apiVersion: serving.knative.dev/v1 + kind: Route + reversePatches: + - op: copyFromObject + fromPath: status + path: status + - op: copyFromObject + fromPath: spec.traffic + path: spec.traffic + conditions: + - empty: true + path: spec.traffic \ No newline at end of file diff --git a/pkg/patches/patch.go b/pkg/patches/patch.go index 8d081bd3..b7457b33 100644 --- a/pkg/patches/patch.go +++ b/pkg/patches/patch.go @@ -23,15 +23,15 @@ type NameResolver interface { TranslateNamespaceRef(namespace string) (string, error) } -func ApplyPatches(obj1, obj2 client.Object, patchesConf []*config.Patch, reversePatchesConf []*config.Patch, nameResolver NameResolver) error { - node1, err := NewJSONNode(obj1) +func ApplyPatches(destObj, sourceObj client.Object, patchesConf []*config.Patch, reversePatchesConf []*config.Patch, nameResolver NameResolver) error { + node1, err := NewJSONNode(destObj) if err != nil { return errors.Wrap(err, "new json yaml node") } var node2 *yaml.Node - if obj2 != nil { - node2, err = NewJSONNode(obj2) + if sourceObj != nil { + node2, err = NewJSONNode(sourceObj) if err != nil { return errors.Wrap(err, "new json yaml node") } @@ -46,7 +46,7 @@ func ApplyPatches(obj1, obj2 client.Object, patchesConf []*config.Patch, reverse // remove ignore paths from patched object for _, p := range reversePatchesConf { - if p.Path == "" || (p.Ignore != nil && !*p.Ignore) { + if p.Path == "" || (p.Ignore != nil && *p.Ignore) { continue } @@ -64,7 +64,7 @@ func ApplyPatches(obj1, obj2 client.Object, patchesConf []*config.Patch, reverse return errors.Wrap(err, "marshal yaml") } - err = jsonyaml.Unmarshal(objYaml, obj1) + err = jsonyaml.Unmarshal(objYaml, destObj) if err != nil { return errors.Wrap(err, "convert object") } diff --git a/pkg/syncer/generic.go b/pkg/syncer/generic.go index 4ab8a07d..16b834a9 100644 --- a/pkg/syncer/generic.go +++ b/pkg/syncer/generic.go @@ -3,6 +3,7 @@ package syncer import ( "context" "fmt" + "github.com/loft-sh/vcluster-generic-crd-plugin/pkg/config" "github.com/loft-sh/vcluster-generic-crd-plugin/pkg/patches" "github.com/loft-sh/vcluster-sdk/log" @@ -40,8 +41,8 @@ func (s *patcher) ApplyPatches(ctx context.Context, fromObj, toObj client.Object } toObjCopied := toObjBase.DeepCopy() - // apply patches on from object - err = patches.ApplyPatches(toObjCopied, toObj, patchesConfig, reversePatchesConfig, nameResolver) + // apply patches on toObjCopied + err = patches.ApplyPatches(toObjCopied, fromObj, patchesConfig, reversePatchesConfig, nameResolver) if err != nil { return nil, fmt.Errorf("error applying patches: %v", err) } @@ -78,15 +79,15 @@ func (s *patcher) ApplyPatches(ctx context.Context, fromObj, toObj client.Object return outObject, nil } -func (s *patcher) ApplyReversePatches(ctx context.Context, fromObj, otherObj client.Object, reversePatchConfig []*config.Patch, nameResolver patches.NameResolver) (controllerutil.OperationResult, error) { - originalUnstructured, err := toUnstructured(fromObj) +func (s *patcher) ApplyReversePatches(ctx context.Context, destObj, sourceObj client.Object, reversePatchConfig []*config.Patch, nameResolver patches.NameResolver) (controllerutil.OperationResult, error) { + originalUnstructured, err := toUnstructured(destObj) if err != nil { return controllerutil.OperationResultNone, err } - fromCopied := originalUnstructured.DeepCopy() + destCopied := originalUnstructured.DeepCopy() - // apply patches on from object - err = patches.ApplyPatches(fromCopied, otherObj, reversePatchConfig, nil, nameResolver) + // apply patches on destCopied obj + err = patches.ApplyPatches(destCopied, sourceObj, reversePatchConfig, nil, nameResolver) if err != nil { return controllerutil.OperationResultNone, fmt.Errorf("error applying reverse patches: %v", err) } @@ -97,15 +98,15 @@ func (s *patcher) ApplyReversePatches(ctx context.Context, fromObj, otherObj cli if err != nil { return controllerutil.OperationResultNone, err } - afterStatus, hasAfterStatus, err := unstructured.NestedFieldCopy(fromCopied.Object, "status") + afterStatus, hasAfterStatus, err := unstructured.NestedFieldCopy(destCopied.Object, "status") if err != nil { return controllerutil.OperationResultNone, err } // update status if (hasBeforeStatus || hasAfterStatus) && !equality.Semantic.DeepEqual(beforeStatus, afterStatus) { - s.log.Infof("Update status of %s during reverse patching", fromCopied.GetName()) - err = s.fromClient.Status().Update(ctx, fromCopied) + s.log.Infof("Update status of %s during reverse patching", destCopied.GetName()) + err = s.fromClient.Status().Update(ctx, destCopied) if err != nil { return controllerutil.OperationResultNone, errors.Wrap(err, "update reverse status") } @@ -117,14 +118,14 @@ func (s *patcher) ApplyReversePatches(ctx context.Context, fromObj, otherObj cli unstructured.RemoveNestedField(originalUnstructured.Object, "status") } if hasAfterStatus { - unstructured.RemoveNestedField(fromCopied.Object, "status") + unstructured.RemoveNestedField(destCopied.Object, "status") } } // compare rest of the object - if !equality.Semantic.DeepEqual(originalUnstructured, fromCopied) { - s.log.Infof("Update %s during reverse patching", fromCopied.GetName()) - err = s.fromClient.Update(ctx, fromCopied) + if !equality.Semantic.DeepEqual(originalUnstructured, destCopied) { + s.log.Infof("Update %s during reverse patching", destCopied.GetName()) + err = s.fromClient.Update(ctx, destCopied) if err != nil { return controllerutil.OperationResultNone, errors.Wrap(err, "update reverse") }