Skip to content

Commit 84a2fe6

Browse files
committed
Able to use project components as golang library
1 parent ac4634a commit 84a2fe6

File tree

7 files changed

+251
-245
lines changed

7 files changed

+251
-245
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ release/
66
.idea
77
docker-run-release-cache/
88
.vscode/
9+
/cover.out

cmd/helpers.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ import (
1111
"k8s.io/client-go/util/homedir"
1212
)
1313

14-
const (
15-
helm2TestSuccessHook = "test-success"
16-
helm3TestHook = "test"
17-
)
18-
1914
var (
2015
// DefaultHelmHome to hold default home path of .helm dir
2116
DefaultHelmHome = filepath.Join(homedir.HomeDir(), ".helm")

cmd/release.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func releaseCmd() *cobra.Command {
7171
}
7272

7373
func (d *release) differentiateHelm3() error {
74-
excludes := []string{helm3TestHook, helm2TestSuccessHook}
74+
excludes := []string{manifest.Helm3TestHook, manifest.Helm2TestSuccessHook}
7575
if d.includeTests {
7676
excludes = []string{}
7777
}

cmd/revision.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func revisionCmd() *cobra.Command {
7979

8080
func (d *revision) differentiateHelm3() error {
8181
namespace := os.Getenv("HELM_NAMESPACE")
82-
excludes := []string{helm3TestHook, helm2TestSuccessHook}
82+
excludes := []string{manifest.Helm3TestHook, manifest.Helm2TestSuccessHook}
8383
if d.includeTests {
8484
excludes = []string{}
8585
}

cmd/rollback.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func rollbackCmd() *cobra.Command {
6969

7070
func (d *rollback) backcastHelm3() error {
7171
namespace := os.Getenv("HELM_NAMESPACE")
72-
excludes := []string{helm3TestHook, helm2TestSuccessHook}
72+
excludes := []string{manifest.Helm3TestHook, manifest.Helm2TestSuccessHook}
7373
if d.includeTests {
7474
excludes = []string{}
7575
}

cmd/upgrade.go

Lines changed: 3 additions & 237 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package cmd
22

33
import (
4-
"bytes"
5-
"encoding/json"
64
"errors"
75
"fmt"
86
"log"
@@ -11,19 +9,9 @@ import (
119
"strconv"
1210
"strings"
1311

14-
jsonpatch "github.com/evanphx/json-patch/v5"
15-
jsoniterator "github.com/json-iterator/go"
1612
"github.com/spf13/cobra"
1713
"helm.sh/helm/v3/pkg/action"
1814
"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"
2715

2816
"github.com/databus23/helm-diff/v3/diff"
2917
"github.com/databus23/helm-diff/v3/manifest"
@@ -117,7 +105,6 @@ perform.
117105
`
118106

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

122109
func newChartCommand() *cobra.Command {
123110
diff := diffCmd{
@@ -308,15 +295,7 @@ func (d *diffCmd) runHelm3() error {
308295
if err := actionConfig.KubeClient.IsReachable(); err != nil {
309296
return err
310297
}
311-
original, err := actionConfig.KubeClient.Build(bytes.NewBuffer(releaseManifest), false)
312-
if err != nil {
313-
return fmt.Errorf("unable to build kubernetes objects from original release manifest: %w", err)
314-
}
315-
target, err := actionConfig.KubeClient.Build(bytes.NewBuffer(installManifest), false)
316-
if err != nil {
317-
return fmt.Errorf("unable to build kubernetes objects from new release manifest: %w", err)
318-
}
319-
releaseManifest, installManifest, err = genManifest(original, target)
298+
releaseManifest, installManifest, err = manifest.Generate(actionConfig, releaseManifest, installManifest)
320299
if err != nil {
321300
return fmt.Errorf("unable to generate manifests: %w", err)
322301
}
@@ -334,14 +313,14 @@ func (d *diffCmd) runHelm3() error {
334313
if d.includeTests {
335314
currentSpecs = manifest.Parse(string(releaseManifest), d.namespace, d.normalizeManifests)
336315
} else {
337-
currentSpecs = manifest.Parse(string(releaseManifest), d.namespace, d.normalizeManifests, helm3TestHook, helm2TestSuccessHook)
316+
currentSpecs = manifest.Parse(string(releaseManifest), d.namespace, d.normalizeManifests, manifest.Helm3TestHook, manifest.Helm2TestSuccessHook)
338317
}
339318
}
340319
var newSpecs map[string]*manifest.MappingResult
341320
if d.includeTests {
342321
newSpecs = manifest.Parse(string(installManifest), d.namespace, d.normalizeManifests)
343322
} else {
344-
newSpecs = manifest.Parse(string(installManifest), d.namespace, d.normalizeManifests, helm3TestHook, helm2TestSuccessHook)
323+
newSpecs = manifest.Parse(string(installManifest), d.namespace, d.normalizeManifests, manifest.Helm3TestHook, manifest.Helm2TestSuccessHook)
345324
}
346325
seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, &d.Options, os.Stdout)
347326

@@ -354,216 +333,3 @@ func (d *diffCmd) runHelm3() error {
354333

355334
return nil
356335
}
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)