Skip to content

Commit fadaf7a

Browse files
committed
Able to use this project components as golang library
1 parent ac4634a commit fadaf7a

File tree

2 files changed

+233
-226
lines changed

2 files changed

+233
-226
lines changed

cmd/upgrade.go

Lines changed: 1 addition & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package cmd
22

33
import (
44
"bytes"
5-
"encoding/json"
65
"errors"
76
"fmt"
87
"log"
@@ -11,19 +10,9 @@ import (
1110
"strconv"
1211
"strings"
1312

14-
jsonpatch "github.com/evanphx/json-patch/v5"
15-
jsoniterator "github.com/json-iterator/go"
1613
"github.com/spf13/cobra"
1714
"helm.sh/helm/v3/pkg/action"
1815
"helm.sh/helm/v3/pkg/cli"
19-
"helm.sh/helm/v3/pkg/kube"
20-
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
21-
apierrors "k8s.io/apimachinery/pkg/api/errors"
22-
"k8s.io/apimachinery/pkg/runtime"
23-
"k8s.io/apimachinery/pkg/types"
24-
"k8s.io/apimachinery/pkg/util/strategicpatch"
25-
"k8s.io/cli-runtime/pkg/resource"
26-
"sigs.k8s.io/yaml"
2716

2817
"github.com/databus23/helm-diff/v3/diff"
2918
"github.com/databus23/helm-diff/v3/manifest"
@@ -117,7 +106,6 @@ perform.
117106
`
118107

119108
var envSettings = cli.New()
120-
var yamlSeperator = []byte("\n---\n")
121109

122110
func newChartCommand() *cobra.Command {
123111
diff := diffCmd{
@@ -316,7 +304,7 @@ func (d *diffCmd) runHelm3() error {
316304
if err != nil {
317305
return fmt.Errorf("unable to build kubernetes objects from new release manifest: %w", err)
318306
}
319-
releaseManifest, installManifest, err = genManifest(original, target)
307+
releaseManifest, installManifest, err = manifest.Generate(original, target)
320308
if err != nil {
321309
return fmt.Errorf("unable to generate manifests: %w", err)
322310
}
@@ -354,216 +342,3 @@ func (d *diffCmd) runHelm3() error {
354342

355343
return nil
356344
}
357-
358-
func genManifest(original, target kube.ResourceList) ([]byte, []byte, error) {
359-
var err error
360-
releaseManifest, installManifest := make([]byte, 0), make([]byte, 0)
361-
362-
// to be deleted
363-
targetResources := make(map[string]bool)
364-
for _, r := range target {
365-
targetResources[objectKey(r)] = true
366-
}
367-
for _, r := range original {
368-
if !targetResources[objectKey(r)] {
369-
out, _ := yaml.Marshal(r.Object)
370-
releaseManifest = append(releaseManifest, yamlSeperator...)
371-
releaseManifest = append(releaseManifest, out...)
372-
}
373-
}
374-
375-
existingResources := make(map[string]bool)
376-
for _, r := range original {
377-
existingResources[objectKey(r)] = true
378-
}
379-
380-
var toBeCreated kube.ResourceList
381-
for _, r := range target {
382-
if !existingResources[objectKey(r)] {
383-
toBeCreated = append(toBeCreated, r)
384-
}
385-
}
386-
387-
toBeUpdated, err := existingResourceConflict(toBeCreated)
388-
if err != nil {
389-
return nil, nil, fmt.Errorf("rendered manifests contain a resource that already exists. Unable to continue with update: %w", err)
390-
}
391-
392-
_ = toBeUpdated.Visit(func(r *resource.Info, err error) error {
393-
if err != nil {
394-
return err
395-
}
396-
original.Append(r)
397-
return nil
398-
})
399-
400-
err = target.Visit(func(info *resource.Info, err error) error {
401-
if err != nil {
402-
return err
403-
}
404-
kind := info.Mapping.GroupVersionKind.Kind
405-
406-
// Fetch the current object for the three way merge
407-
helper := resource.NewHelper(info.Client, info.Mapping)
408-
currentObj, err := helper.Get(info.Namespace, info.Name)
409-
if err != nil {
410-
if !apierrors.IsNotFound(err) {
411-
return fmt.Errorf("could not get information about the resource: %w", err)
412-
}
413-
// to be created
414-
out, _ := yaml.Marshal(info.Object)
415-
installManifest = append(installManifest, yamlSeperator...)
416-
installManifest = append(installManifest, out...)
417-
return nil
418-
}
419-
// to be updated
420-
out, _ := jsoniterator.ConfigCompatibleWithStandardLibrary.Marshal(currentObj)
421-
pruneObj, err := deleteStatusAndTidyMetadata(out)
422-
if err != nil {
423-
return fmt.Errorf("prune current obj %q with kind %s: %w", info.Name, kind, err)
424-
}
425-
pruneOut, err := yaml.Marshal(pruneObj)
426-
if err != nil {
427-
return fmt.Errorf("prune current out %q with kind %s: %w", info.Name, kind, err)
428-
}
429-
releaseManifest = append(releaseManifest, yamlSeperator...)
430-
releaseManifest = append(releaseManifest, pruneOut...)
431-
432-
originalInfo := original.Get(info)
433-
if originalInfo == nil {
434-
return fmt.Errorf("could not find %q", info.Name)
435-
}
436-
437-
patch, patchType, err := createPatch(originalInfo.Object, currentObj, info)
438-
if err != nil {
439-
return err
440-
}
441-
442-
helper.ServerDryRun = true
443-
targetObj, err := helper.Patch(info.Namespace, info.Name, patchType, patch, nil)
444-
if err != nil {
445-
return fmt.Errorf("cannot patch %q with kind %s: %w", info.Name, kind, err)
446-
}
447-
out, _ = jsoniterator.ConfigCompatibleWithStandardLibrary.Marshal(targetObj)
448-
pruneObj, err = deleteStatusAndTidyMetadata(out)
449-
if err != nil {
450-
return fmt.Errorf("prune current obj %q with kind %s: %w", info.Name, kind, err)
451-
}
452-
pruneOut, err = yaml.Marshal(pruneObj)
453-
if err != nil {
454-
return fmt.Errorf("prune current out %q with kind %s: %w", info.Name, kind, err)
455-
}
456-
installManifest = append(installManifest, yamlSeperator...)
457-
installManifest = append(installManifest, pruneOut...)
458-
return nil
459-
})
460-
461-
return releaseManifest, installManifest, err
462-
}
463-
464-
func createPatch(originalObj, currentObj runtime.Object, target *resource.Info) ([]byte, types.PatchType, error) {
465-
oldData, err := json.Marshal(originalObj)
466-
if err != nil {
467-
return nil, types.StrategicMergePatchType, fmt.Errorf("serializing current configuration: %w", err)
468-
}
469-
newData, err := json.Marshal(target.Object)
470-
if err != nil {
471-
return nil, types.StrategicMergePatchType, fmt.Errorf("serializing target configuration: %w", err)
472-
}
473-
474-
// Even if currentObj is nil (because it was not found), it will marshal just fine
475-
currentData, err := json.Marshal(currentObj)
476-
if err != nil {
477-
return nil, types.StrategicMergePatchType, fmt.Errorf("serializing live configuration: %w", err)
478-
}
479-
// kind := target.Mapping.GroupVersionKind.Kind
480-
// if kind == "Deployment" {
481-
// curr, _ := yaml.Marshal(currentObj)
482-
// fmt.Println(string(curr))
483-
// }
484-
485-
// Get a versioned object
486-
versionedObject := kube.AsVersioned(target)
487-
488-
// Unstructured objects, such as CRDs, may not have an not registered error
489-
// returned from ConvertToVersion. Anything that's unstructured should
490-
// use the jsonpatch.CreateMergePatch. Strategic Merge Patch is not supported
491-
// on objects like CRDs.
492-
_, isUnstructured := versionedObject.(runtime.Unstructured)
493-
494-
// On newer K8s versions, CRDs aren't unstructured but has this dedicated type
495-
_, isCRD := versionedObject.(*apiextv1.CustomResourceDefinition)
496-
497-
if isUnstructured || isCRD {
498-
// fall back to generic JSON merge patch
499-
patch, err := jsonpatch.CreateMergePatch(oldData, newData)
500-
return patch, types.MergePatchType, err
501-
}
502-
503-
patchMeta, err := strategicpatch.NewPatchMetaFromStruct(versionedObject)
504-
if err != nil {
505-
return nil, types.StrategicMergePatchType, fmt.Errorf("unable to create patch metadata from object: %w", err)
506-
}
507-
508-
patch, err := strategicpatch.CreateThreeWayMergePatch(oldData, newData, currentData, patchMeta, true)
509-
return patch, types.StrategicMergePatchType, err
510-
}
511-
512-
func objectKey(r *resource.Info) string {
513-
gvk := r.Object.GetObjectKind().GroupVersionKind()
514-
return fmt.Sprintf("%s/%s/%s/%s", gvk.GroupVersion().String(), gvk.Kind, r.Namespace, r.Name)
515-
}
516-
517-
func existingResourceConflict(resources kube.ResourceList) (kube.ResourceList, error) {
518-
var requireUpdate kube.ResourceList
519-
520-
err := resources.Visit(func(info *resource.Info, err error) error {
521-
if err != nil {
522-
return err
523-
}
524-
525-
helper := resource.NewHelper(info.Client, info.Mapping)
526-
_, err = helper.Get(info.Namespace, info.Name)
527-
if err != nil {
528-
if apierrors.IsNotFound(err) {
529-
return nil
530-
}
531-
return fmt.Errorf("could not get information about the resource: %w", err)
532-
}
533-
534-
requireUpdate.Append(info)
535-
return nil
536-
})
537-
538-
return requireUpdate, err
539-
}
540-
541-
func deleteStatusAndTidyMetadata(obj []byte) (map[string]interface{}, error) {
542-
var objectMap map[string]interface{}
543-
err := jsoniterator.Unmarshal(obj, &objectMap)
544-
if err != nil {
545-
return nil, fmt.Errorf("could not unmarshal byte sequence: %w", err)
546-
}
547-
548-
delete(objectMap, "status")
549-
550-
metadata := objectMap["metadata"].(map[string]interface{})
551-
552-
delete(metadata, "managedFields")
553-
delete(metadata, "generation")
554-
555-
// See the below for the goal of this metadata tidy logic.
556-
// https://github.com/databus23/helm-diff/issues/326#issuecomment-1008253274
557-
if a := metadata["annotations"]; a != nil {
558-
annotations := a.(map[string]interface{})
559-
delete(annotations, "meta.helm.sh/release-name")
560-
delete(annotations, "meta.helm.sh/release-namespace")
561-
delete(annotations, "deployment.kubernetes.io/revision")
562-
563-
if len(annotations) == 0 {
564-
delete(metadata, "annotations")
565-
}
566-
}
567-
568-
return objectMap, nil
569-
}

0 commit comments

Comments
 (0)