github.com/oam-dev/kubevela@v1.9.11/pkg/appfile/dryrun/diff.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package dryrun
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"strings"
    24  
    25  	"github.com/aryann/difflib"
    26  	"github.com/pkg/errors"
    27  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    28  	"k8s.io/client-go/rest"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  	"sigs.k8s.io/yaml"
    31  
    32  	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
    33  	"github.com/kubevela/workflow/pkg/cue/packages"
    34  
    35  	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    36  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
    37  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    38  	"github.com/oam-dev/kubevela/apis/types"
    39  	"github.com/oam-dev/kubevela/pkg/appfile"
    40  	"github.com/oam-dev/kubevela/pkg/oam"
    41  )
    42  
    43  // NewLiveDiffOption creates a live-diff option
    44  func NewLiveDiffOption(c client.Client, cfg *rest.Config, pd *packages.PackageDiscover, as []*unstructured.Unstructured) *LiveDiffOption {
    45  	parser := appfile.NewApplicationParser(c, pd)
    46  	return &LiveDiffOption{DryRun: NewDryRunOption(c, cfg, pd, as, false), Parser: parser}
    47  }
    48  
    49  // ManifestKind enums the kind of OAM objects
    50  type ManifestKind string
    51  
    52  // enum kinds of manifest objects
    53  const (
    54  	AppKind           ManifestKind = "Application"
    55  	AppConfigCompKind ManifestKind = "AppConfigComponent"
    56  	RawCompKind       ManifestKind = "Component"
    57  	TraitKind         ManifestKind = "Trait"
    58  	PolicyKind        ManifestKind = "Policy"
    59  	WorkflowKind      ManifestKind = "Workflow"
    60  	ReferredObject    ManifestKind = "ReferredObject"
    61  )
    62  
    63  // DiffEntry records diff info of OAM object
    64  type DiffEntry struct {
    65  	Name     string               `json:"name"`
    66  	Kind     ManifestKind         `json:"kind"`
    67  	DiffType DiffType             `json:"diffType,omitempty"`
    68  	Diffs    []difflib.DiffRecord `json:"diffs,omitempty"`
    69  	Subs     []*DiffEntry         `json:"subs,omitempty"`
    70  }
    71  
    72  // DiffType enums the type of diff
    73  type DiffType string
    74  
    75  // enum types of diff
    76  const (
    77  	AddDiff    DiffType = "ADD"
    78  	ModifyDiff DiffType = "MODIFY"
    79  	RemoveDiff DiffType = "REMOVE"
    80  	NoDiff     DiffType = ""
    81  )
    82  
    83  // manifest is a helper struct used to calculate diff on applications and
    84  // sub-resources.
    85  type manifest struct {
    86  	Name string
    87  	Kind ManifestKind
    88  	// Data is unmarshalled object in YAML
    89  	Data string
    90  	// application's subs means appConfigComponents
    91  	// appConfigComponent's subs means rawComponent and traits
    92  	Subs []*manifest
    93  }
    94  
    95  func (m *manifest) key() string {
    96  	return string(m.Kind) + "/" + m.Name
    97  }
    98  
    99  // LiveDiffOption contains options for comparing an application with a
   100  // living AppRevision in the cluster
   101  type LiveDiffOption struct {
   102  	DryRun
   103  	Parser *appfile.Parser
   104  }
   105  
   106  // LiveDiffObject wraps the objects for diff
   107  type LiveDiffObject struct {
   108  	*v1beta1.Application
   109  	*v1beta1.ApplicationRevision
   110  }
   111  
   112  // RenderlessDiff will not compare the rendered component results but only compare the application spec and
   113  // original external dependency objects such as external workflow/policies
   114  func (l *LiveDiffOption) RenderlessDiff(ctx context.Context, base, comparor LiveDiffObject) (*DiffEntry, error) {
   115  	genManifest := func(obj LiveDiffObject) (*manifest, error) {
   116  		var af *appfile.Appfile
   117  		var err error
   118  		var app *v1beta1.Application
   119  		switch {
   120  		case obj.Application != nil:
   121  			app = obj.Application.DeepCopy()
   122  			af, err = l.Parser.GenerateAppFileFromApp(ctx, obj.Application)
   123  		case obj.ApplicationRevision != nil:
   124  			app = obj.ApplicationRevision.Spec.Application.DeepCopy()
   125  			af, err = l.Parser.GenerateAppFileFromRevision(obj.ApplicationRevision)
   126  		default:
   127  			err = errors.Errorf("either application or application revision should be set for LiveDiffObject")
   128  		}
   129  		var appfileError error
   130  		if err != nil {
   131  			appfileError = err
   132  		}
   133  		bs, err := marshalObject(app)
   134  		if err != nil {
   135  			return nil, errors.Wrapf(err, "failed to marshal application")
   136  		}
   137  		m := &manifest{Name: app.Name, Kind: AppKind, Data: string(bs)}
   138  		if appfileError != nil {
   139  			m.Data += "Error: " + appfileError.Error() + "\n"
   140  			return m, nil // nolint
   141  		}
   142  		for _, policy := range af.ExternalPolicies {
   143  			if bs, err = marshalObject(policy); err == nil {
   144  				m.Subs = append(m.Subs, &manifest{Name: policy.Name, Kind: PolicyKind, Data: string(bs)})
   145  			} else {
   146  				m.Subs = append(m.Subs, &manifest{Name: policy.Name, Kind: PolicyKind, Data: "Error: " + errors.Wrapf(err, "failed to marshal external policy %s", policy.Name).Error()})
   147  			}
   148  		}
   149  		if af.ExternalWorkflow != nil {
   150  			if bs, err = marshalObject(af.ExternalWorkflow); err == nil {
   151  				m.Subs = append(m.Subs, &manifest{Name: af.ExternalWorkflow.Name, Kind: WorkflowKind, Data: string(bs)})
   152  			} else {
   153  				m.Subs = append(m.Subs, &manifest{Name: af.ExternalWorkflow.Name, Kind: WorkflowKind, Data: "Error: " + errors.Wrapf(err, "failed to marshal external workflow %s", af.ExternalWorkflow.Name).Error()})
   154  			}
   155  		}
   156  		if af.ReferredObjects != nil {
   157  			for _, refObj := range af.ReferredObjects {
   158  				manifestName := fmt.Sprintf("%s %s %s", refObj.GetAPIVersion(), refObj.GetKind(), client.ObjectKeyFromObject(refObj).String())
   159  				if bs, err = marshalObject(refObj); err == nil {
   160  					m.Subs = append(m.Subs, &manifest{Name: manifestName, Kind: ReferredObject, Data: string(bs)})
   161  				} else {
   162  					m.Subs = append(m.Subs, &manifest{Name: manifestName, Kind: ReferredObject, Data: "Error: " + errors.Wrapf(err, "failed to marshal referred object").Error()})
   163  				}
   164  			}
   165  		}
   166  		return m, nil
   167  	}
   168  	baseManifest, err := genManifest(base)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	comparorManifest, err := genManifest(comparor)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	diffResult := l.diffManifest(baseManifest, comparorManifest)
   177  	return diffResult, nil
   178  }
   179  
   180  func calDiffType(diffs []difflib.DiffRecord) DiffType {
   181  	hasAdd, hasRemove := false, false
   182  	for _, d := range diffs {
   183  		switch d.Delta {
   184  		case difflib.LeftOnly:
   185  			hasRemove = true
   186  		case difflib.RightOnly:
   187  			hasAdd = true
   188  		default:
   189  		}
   190  	}
   191  	switch {
   192  	case hasAdd && hasRemove:
   193  		return ModifyDiff
   194  	case hasAdd && !hasRemove:
   195  		return AddDiff
   196  	case !hasAdd && hasRemove:
   197  		return RemoveDiff
   198  	default:
   199  		return NoDiff
   200  	}
   201  }
   202  
   203  func (l *LiveDiffOption) diffManifest(base, comparor *manifest) *DiffEntry {
   204  	if base == nil {
   205  		base = &manifest{}
   206  	}
   207  	if comparor == nil {
   208  		comparor = &manifest{}
   209  	}
   210  	entry := &DiffEntry{Name: base.Name, Kind: base.Kind}
   211  	if base.Name == "" {
   212  		entry = &DiffEntry{Name: comparor.Name, Kind: comparor.Kind}
   213  	}
   214  	const sep = "\n"
   215  	entry.Diffs = difflib.Diff(strings.Split(comparor.Data, sep), strings.Split(base.Data, sep))
   216  	entry.DiffType = calDiffType(entry.Diffs)
   217  	baseManifestMap, comparorManifestMap := make(map[string]*manifest), make(map[string]*manifest)
   218  	var keys []string
   219  	for _, _base := range base.Subs {
   220  		baseManifestMap[_base.key()] = _base
   221  		keys = append(keys, _base.key())
   222  	}
   223  	for _, _comparor := range comparor.Subs {
   224  		comparorManifestMap[_comparor.key()] = _comparor
   225  		if _, found := baseManifestMap[_comparor.key()]; !found {
   226  			keys = append(keys, _comparor.key())
   227  		}
   228  	}
   229  	for _, key := range keys {
   230  		entry.Subs = append(entry.Subs, l.diffManifest(baseManifestMap[key], comparorManifestMap[key]))
   231  	}
   232  	return entry
   233  }
   234  
   235  // Diff does three phases, dry-run on input app, preparing manifest for diff, and
   236  // calculating diff on manifests.
   237  // TODO(wonderflow): vela live-diff don't diff for policies now.
   238  func (l *LiveDiffOption) Diff(ctx context.Context, app *v1beta1.Application, appRevision *v1beta1.ApplicationRevision) (*DiffEntry, error) {
   239  	comps, _, err := l.ExecuteDryRun(ctx, app)
   240  	if err != nil {
   241  		return nil, errors.WithMessagef(err, "cannot dry-run for app %q", app.Name)
   242  	}
   243  	// new refers to the app as input to dry-run
   244  	newManifest, err := generateManifest(app, comps)
   245  	if err != nil {
   246  		return nil, errors.WithMessagef(err, "cannot generate diff manifest for app %q", app.Name)
   247  	}
   248  
   249  	// old refers to the living app revision
   250  	oldManifest, err := generateManifestFromAppRevision(l.Parser, appRevision)
   251  	if err != nil {
   252  		return nil, errors.WithMessagef(err, "cannot generate diff manifest for AppRevision %q", appRevision.Name)
   253  	}
   254  	diffResult := l.calculateDiff(oldManifest, newManifest)
   255  	return diffResult, nil
   256  }
   257  
   258  // DiffApps does three phases, dry-run on input app, preparing manifest for diff, and
   259  // calculating diff on manifests.
   260  // TODO(wonderflow): vela live-diff don't diff for policies now.
   261  func (l *LiveDiffOption) DiffApps(ctx context.Context, app *v1beta1.Application, oldApp *v1beta1.Application) (*DiffEntry, error) {
   262  	comps, _, err := l.ExecuteDryRun(ctx, app)
   263  	if err != nil {
   264  		return nil, errors.WithMessagef(err, "cannot dry-run for app %q", app.Name)
   265  	}
   266  	// new refers to the app as input to dry-run
   267  	newManifest, err := generateManifest(app, comps)
   268  	if err != nil {
   269  		return nil, errors.WithMessagef(err, "cannot generate diff manifest for app %q", app.Name)
   270  	}
   271  
   272  	oldComps, _, err := l.ExecuteDryRun(ctx, oldApp)
   273  	if err != nil {
   274  		return nil, errors.WithMessagef(err, "cannot dry-run for app %q", oldApp.Name)
   275  	}
   276  	// new refers to the app as input to dry-run
   277  	oldManifest, err := generateManifest(oldApp, oldComps)
   278  	if err != nil {
   279  		return nil, errors.WithMessagef(err, "cannot generate diff manifest for app %q", oldApp.Name)
   280  	}
   281  
   282  	diffResult := l.calculateDiff(oldManifest, newManifest)
   283  	return diffResult, nil
   284  }
   285  
   286  // calculateDiff calculate diff between two application and their sub-resources
   287  func (l *LiveDiffOption) calculateDiff(oldApp, newApp *manifest) *DiffEntry {
   288  	emptyManifest := &manifest{}
   289  	r := &DiffEntry{
   290  		Name: oldApp.Name,
   291  		Kind: oldApp.Kind,
   292  	}
   293  
   294  	appDiffs := diffManifest(oldApp, newApp)
   295  	if hasChanges(appDiffs) {
   296  		r.DiffType = ModifyDiff
   297  		r.Diffs = appDiffs
   298  	}
   299  
   300  	// check modified and removed components
   301  	for _, oldAcc := range oldApp.Subs {
   302  		accDiffEntry := &DiffEntry{
   303  			Name: oldAcc.Name,
   304  			Kind: oldAcc.Kind,
   305  			Subs: make([]*DiffEntry, 0),
   306  		}
   307  
   308  		var newAcc *manifest
   309  		// check whether component is removed
   310  		for _, acc := range newApp.Subs {
   311  			if oldAcc.Name == acc.Name {
   312  				newAcc = acc
   313  				break
   314  			}
   315  		}
   316  		if newAcc != nil {
   317  			// component is not removed
   318  			// check modified and removed ACC subs (rawComponent and traits)
   319  			for _, oldAccSub := range oldAcc.Subs {
   320  				accSubDiffEntry := &DiffEntry{
   321  					Name: oldAccSub.Name,
   322  					Kind: oldAccSub.Kind,
   323  				}
   324  				var newAccSub *manifest
   325  				for _, accSub := range newAcc.Subs {
   326  					if accSub.Kind == oldAccSub.Kind &&
   327  						accSub.Name == oldAccSub.Name {
   328  						newAccSub = accSub
   329  						break
   330  					}
   331  				}
   332  				var diffs []difflib.DiffRecord
   333  				if newAccSub != nil {
   334  					// accSub is not removed, then check modification
   335  					diffs = diffManifest(oldAccSub, newAccSub)
   336  					if hasChanges(diffs) {
   337  						accSubDiffEntry.DiffType = ModifyDiff
   338  					} else {
   339  						accSubDiffEntry.DiffType = NoDiff
   340  					}
   341  				} else {
   342  					// accSub is removed
   343  					diffs = diffManifest(oldAccSub, emptyManifest)
   344  					accSubDiffEntry.DiffType = RemoveDiff
   345  				}
   346  				accSubDiffEntry.Diffs = diffs
   347  				accDiffEntry.Subs = append(accDiffEntry.Subs, accSubDiffEntry)
   348  			}
   349  
   350  			// check added ACC subs (traits)
   351  			for _, newAccSub := range newAcc.Subs {
   352  				isAdded := true
   353  				for _, oldAccSub := range oldAcc.Subs {
   354  					if oldAccSub.Kind == newAccSub.Kind &&
   355  						oldAccSub.Name == newAccSub.Name {
   356  						isAdded = false
   357  						break
   358  					}
   359  				}
   360  				if isAdded {
   361  					accSubDiffEntry := &DiffEntry{
   362  						Name:     newAccSub.Name,
   363  						Kind:     newAccSub.Kind,
   364  						DiffType: AddDiff,
   365  					}
   366  					diffs := diffManifest(emptyManifest, newAccSub)
   367  					accSubDiffEntry.Diffs = diffs
   368  					accDiffEntry.Subs = append(accDiffEntry.Subs, accSubDiffEntry)
   369  				}
   370  			}
   371  		} else {
   372  			// component is removed as well as its subs
   373  			accDiffEntry.DiffType = RemoveDiff
   374  			for _, oldAccSub := range oldAcc.Subs {
   375  				diffs := diffManifest(oldAccSub, emptyManifest)
   376  				accSubDiffEntry := &DiffEntry{
   377  					Name:     oldAccSub.Name,
   378  					Kind:     oldAccSub.Kind,
   379  					DiffType: RemoveDiff,
   380  					Diffs:    diffs,
   381  				}
   382  				accDiffEntry.Subs = append(accDiffEntry.Subs, accSubDiffEntry)
   383  			}
   384  		}
   385  		r.Subs = append(r.Subs, accDiffEntry)
   386  	}
   387  
   388  	// check added component
   389  	for _, newAcc := range newApp.Subs {
   390  		isAdded := true
   391  		for _, oldAcc := range oldApp.Subs {
   392  			if oldAcc.Kind == newAcc.Kind &&
   393  				oldAcc.Name == newAcc.Name {
   394  				isAdded = false
   395  				break
   396  			}
   397  		}
   398  		if isAdded {
   399  			accDiffEntry := &DiffEntry{
   400  				Name:     newAcc.Name,
   401  				Kind:     newAcc.Kind,
   402  				DiffType: AddDiff,
   403  				Subs:     make([]*DiffEntry, 0),
   404  			}
   405  			// added component's subs are all added
   406  			for _, newAccSub := range newAcc.Subs {
   407  				diffs := diffManifest(emptyManifest, newAccSub)
   408  				accSubDiffEntry := &DiffEntry{
   409  					Name:     newAccSub.Name,
   410  					Kind:     newAccSub.Kind,
   411  					DiffType: AddDiff,
   412  					Diffs:    diffs,
   413  				}
   414  				accDiffEntry.Subs = append(accDiffEntry.Subs, accSubDiffEntry)
   415  			}
   416  			r.Subs = append(r.Subs, accDiffEntry)
   417  		}
   418  	}
   419  
   420  	return r
   421  }
   422  
   423  // generateManifest generates a manifest whose top-level is an application
   424  func generateManifest(app *v1beta1.Application, comps []*types.ComponentManifest) (*manifest, error) {
   425  	r := &manifest{
   426  		Name: app.Name,
   427  		Kind: AppKind,
   428  	}
   429  	removeRevisionRelatedLabelAndAnnotation(app)
   430  	b, err := yaml.Marshal(app)
   431  	if err != nil {
   432  		return nil, errors.Wrapf(err, "cannot marshal application %q", app.Name)
   433  	}
   434  	r.Data = string(b)
   435  	appSubs := make([]*manifest, 0, len(app.Spec.Components))
   436  
   437  	// a helper map recording all rawComponents with compName as key
   438  	rawCompManifests := map[string]*manifest{}
   439  	for _, comp := range comps {
   440  		cM := &manifest{
   441  			Name: comp.Name,
   442  			Kind: RawCompKind,
   443  		}
   444  		removeRevisionRelatedLabelAndAnnotation(comp.ComponentOutput)
   445  		b, err := yaml.Marshal(comp.ComponentOutput)
   446  		if err != nil {
   447  			return nil, errors.Wrapf(err, "cannot marshal component %q", comp.Name)
   448  		}
   449  		cM.Data = string(b)
   450  		rawCompManifests[comp.Name] = cM
   451  	}
   452  
   453  	// generate appConfigComponent manifests
   454  	for _, comp := range comps {
   455  		compM := &manifest{
   456  			Name: comp.Name,
   457  			Kind: AppConfigCompKind,
   458  		}
   459  		comp.RevisionHash = ""
   460  		comp.RevisionName = ""
   461  		// get matched raw component and add it into appConfigComponent's subs
   462  		subs := []*manifest{rawCompManifests[comp.Name]}
   463  		for _, t := range comp.ComponentOutputsAndTraits {
   464  			removeRevisionRelatedLabelAndAnnotation(t)
   465  
   466  			tType := t.GetLabels()[oam.TraitTypeLabel]
   467  			tResource := t.GetLabels()[oam.TraitResource]
   468  			// dry-run cannot generate name for a trait
   469  			// a join of trait type&resource is unique in a component
   470  			// we use it to identify a trait
   471  			tUnique := fmt.Sprintf("%s/%s", tType, tResource)
   472  
   473  			b, err := yaml.Marshal(t)
   474  			if err != nil {
   475  				return nil, errors.Wrapf(err, "cannot parse trait %q raw to YAML", tUnique)
   476  			}
   477  			subs = append(subs, &manifest{
   478  				Name: tUnique,
   479  				Kind: TraitKind,
   480  				Data: string(b),
   481  			})
   482  		}
   483  		compM.Subs = subs
   484  		appSubs = append(appSubs, compM)
   485  	}
   486  	r.Subs = appSubs
   487  	return r, nil
   488  }
   489  
   490  // generateManifestFromAppRevision generates manifest from an AppRevision
   491  func generateManifestFromAppRevision(parser *appfile.Parser, appRevision *v1beta1.ApplicationRevision) (*manifest, error) {
   492  	af, err := parser.GenerateAppFileFromRevision(appRevision)
   493  	if err != nil {
   494  		return nil, err
   495  	}
   496  	comps, err := af.GenerateComponentManifests()
   497  	if err != nil {
   498  		return nil, err
   499  	}
   500  	app := appRevision.Spec.Application
   501  	// app in appRevision has no name & namespace
   502  	// we should extract/get them from appRappRevision
   503  	app.Name = extractNameFromRevisionName(appRevision.Name)
   504  	app.Namespace = appRevision.Namespace
   505  	return generateManifest(&app, comps)
   506  }
   507  
   508  // diffManifest calculates diff between data of two manifest line by line
   509  func diffManifest(old, new *manifest) []difflib.DiffRecord {
   510  	const sep = "\n"
   511  	return difflib.Diff(strings.Split(old.Data, sep), strings.Split(new.Data, sep))
   512  }
   513  
   514  func extractNameFromRevisionName(r string) string {
   515  	s := strings.Split(r, "-")
   516  	return strings.Join(s[0:len(s)-1], "-")
   517  }
   518  
   519  func clearedLabels(labels map[string]string) map[string]string {
   520  	newLabels := map[string]string{}
   521  	for k, v := range labels {
   522  		if k == oam.LabelAppRevision {
   523  			continue
   524  		}
   525  		newLabels[k] = v
   526  	}
   527  	if len(newLabels) == 0 {
   528  		return nil
   529  	}
   530  	return newLabels
   531  }
   532  
   533  func clearedAnnotations(annotations map[string]string) map[string]string {
   534  	newAnnotations := map[string]string{}
   535  	for k, v := range annotations {
   536  		if k == oam.AnnotationKubeVelaVersion || k == oam.AnnotationAppRevision || k == "kubectl.kubernetes.io/last-applied-configuration" {
   537  			continue
   538  		}
   539  		newAnnotations[k] = v
   540  	}
   541  	if len(newAnnotations) == 0 {
   542  		return nil
   543  	}
   544  	return newAnnotations
   545  }
   546  
   547  // removeRevisionRelatedLabelAndAnnotation will set label oam.LabelAppRevision to empty
   548  // because dry-run cannot set value to this label
   549  func removeRevisionRelatedLabelAndAnnotation(o client.Object) {
   550  	o.SetLabels(clearedLabels(o.GetLabels()))
   551  	o.SetAnnotations(clearedAnnotations(o.GetAnnotations()))
   552  }
   553  
   554  // hasChanges checks whether existing change in diff records
   555  func hasChanges(diffs []difflib.DiffRecord) bool {
   556  	for _, d := range diffs {
   557  		// difflib.Common means no change between two sides
   558  		if d.Delta != difflib.Common {
   559  			return true
   560  		}
   561  	}
   562  	return false
   563  }
   564  
   565  func marshalObject(o client.Object) ([]byte, error) {
   566  	switch obj := o.(type) {
   567  	case *v1beta1.Application:
   568  		obj.SetGroupVersionKind(v1beta1.ApplicationKindVersionKind)
   569  		obj.Status = common.AppStatus{}
   570  	case *v1alpha1.Policy:
   571  		obj.SetGroupVersionKind(v1alpha1.PolicyGroupVersionKind)
   572  	case *workflowv1alpha1.Workflow:
   573  		obj.SetGroupVersionKind(v1alpha1.WorkflowGroupVersionKind)
   574  	}
   575  	o.SetLabels(clearedLabels(o.GetLabels()))
   576  	o.SetAnnotations(clearedAnnotations(o.GetAnnotations()))
   577  	bs, err := json.Marshal(o)
   578  	if err != nil {
   579  		return bs, err
   580  	}
   581  	m := make(map[string]interface{})
   582  	if err = json.Unmarshal(bs, &m); err != nil {
   583  		return bs, err
   584  	}
   585  	if metadata, found := m["metadata"]; found {
   586  		if md, ok := metadata.(map[string]interface{}); ok {
   587  			_m := make(map[string]interface{})
   588  			for k, v := range md {
   589  				if k == "name" || k == "namespace" || k == "labels" || k == "annotations" {
   590  					_m[k] = v
   591  				}
   592  			}
   593  			m["metadata"] = _m
   594  		}
   595  	}
   596  	return yaml.Marshal(m)
   597  }