Skip to content

Commit

Permalink
fix: allow separate namespaces for helm chart resources (#1969)
Browse files Browse the repository at this point in the history
  • Loading branch information
morningvera committed Jul 21, 2021
1 parent 0537317 commit 480418f
Show file tree
Hide file tree
Showing 9 changed files with 443 additions and 23 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ require (
k8s.io/client-go v0.21.0
k8s.io/cluster-bootstrap v0.20.5
k8s.io/helm v2.14.3+incompatible
k8s.io/kube-openapi v0.0.0-20210323165736-1a6458611d18 // indirect
k8s.io/kubelet v0.0.0
k8s.io/kubernetes v1.20.5
k8s.io/utils v0.0.0-20210305010621-2afb4311ab10
Expand Down Expand Up @@ -140,4 +139,5 @@ replace (
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.5
k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.20.5
k8s.io/sample-controller => k8s.io/sample-controller v0.20.5
sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.3.3
)
54 changes: 43 additions & 11 deletions go.sum

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pkg/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,19 @@ func init() {
troubleshootscheme.AddToScheme(scheme.Scheme)
}

func GetGVKWithNameAndNs(content []byte, baseNS string) string {
func GetGVKWithNameAndNs(content []byte, baseNS string) (string, OverlySimpleGVK) {
o := OverlySimpleGVK{}

if err := yaml.Unmarshal(content, &o); err != nil {
return ""
return "", o
}

namespace := baseNS
if o.Metadata.Namespace != "" {
namespace = o.Metadata.Namespace
}

return fmt.Sprintf("%s-%s-%s-%s", o.APIVersion, o.Kind, o.Metadata.Name, namespace)
return fmt.Sprintf("%s-%s-%s-%s", o.APIVersion, o.Kind, o.Metadata.Name, namespace), o
}

func (f *BaseFile) transpileHelmHooksToKotsHooks() error {
Expand Down
106 changes: 106 additions & 0 deletions pkg/base/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import (
upstreamtypes "github.com/replicatedhq/kots/pkg/upstream/types"
"github.com/replicatedhq/kots/pkg/util"
"helm.sh/helm/v3/pkg/strvals"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/krusty"
kustomizetypes "sigs.k8s.io/kustomize/api/types"
)

func RenderHelm(u *upstreamtypes.Upstream, renderOptions *RenderOptions) (*Base, error) {
Expand Down Expand Up @@ -271,3 +274,106 @@ func checkChartForVersion(file *upstreamtypes.UpstreamFile) (string, error) {
// if no determination is made, assume v2
return "v2", nil
}

// insert namespace if it's defined in the spec and not already present in the manifests
func kustomizeHelmNamespace(baseFiles []BaseFile, renderOptions *RenderOptions) ([]BaseFile, error) {
if renderOptions.Namespace == "" {
return baseFiles, nil
}

chartsPath, err := ioutil.TempDir("", "charts")
if err != nil {
return nil, errors.Wrap(err, "failed to create temp dir")
}
defer os.RemoveAll(chartsPath)

var updatedBaseFiles []BaseFile
var kustomizeResources []string
var kustomizePatches []kustomizetypes.PatchStrategicMerge
resources := map[string]BaseFile{}
foundGVKNamesMap := map[string]bool{}
for _, baseFile := range baseFiles {
// write temp files for manifests that need a namespace
gvk, manifest := GetGVKWithNameAndNs(baseFile.Content, renderOptions.Namespace)
if manifest.APIVersion == "" || manifest.Kind == "" || manifest.Metadata.Name == "" {
updatedBaseFiles = append(updatedBaseFiles, baseFile)
continue // ignore invalid resources
}

if manifest.Metadata.Namespace == "" {
name := filepath.Base(baseFile.Path)
tmpFile, err := ioutil.TempFile(chartsPath, name)
if err != nil {
return nil, errors.Wrapf(err, "failed to write temp file %v", tmpFile.Name())
}
defer tmpFile.Close()

if _, err := tmpFile.Write(baseFile.Content); err != nil {
return nil, errors.Wrapf(err, "failed to write temp file %v content", tmpFile.Name())
}

if err := tmpFile.Close(); err != nil {
return nil, errors.Wrapf(err, "failed to close temp file %v", tmpFile.Name())
}

if !foundGVKNamesMap[gvk] || gvk == "" {
resources[gvk] = baseFile
kustomizeResources = append(kustomizeResources, tmpFile.Name())
foundGVKNamesMap[gvk] = true
} else {
kustomizePatches = append(kustomizePatches, kustomizetypes.PatchStrategicMerge(tmpFile.Name()))
}
} else {
updatedBaseFiles = append(updatedBaseFiles, baseFile)
continue // don't bother kustomizing the yaml if namespace already exists
}
}

// write kustomization
kustomization := kustomizetypes.Kustomization{
TypeMeta: kustomizetypes.TypeMeta{
APIVersion: "kustomize.config.k8s.io/v1beta1",
Kind: "Kustomization",
},
Namespace: renderOptions.Namespace,
Resources: kustomizeResources,
PatchesStrategicMerge: kustomizePatches,
}
b, err := yaml.Marshal(kustomization)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal kustomization")
}
err = ioutil.WriteFile(filepath.Join(chartsPath, "kustomization.yaml"), b, 0644)
if err != nil {
return nil, errors.Wrap(err, "failed to write kustomization file")
}

fSys := filesys.MakeFsOnDisk()
k := krusty.MakeKustomizer(fSys, krusty.MakeDefaultOptions())
m, err := k.Run(chartsPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to kustomize %s", chartsPath)
}
updatedManifests, err := m.AsYaml()
if err != nil {
return nil, errors.Wrap(err, "failed to convert kustomize output to yaml")
}

splitManifests := splitManifests(string(updatedManifests))
for _, manifest := range splitManifests {
if len(manifest) == 0 {
continue
}

gvk, _ := GetGVKWithNameAndNs([]byte(manifest), renderOptions.Namespace)
if _, ok := resources[gvk]; !ok {
return nil, errors.Wrapf(err, "failed to replace base %v", gvk)
}

baseFile := resources[gvk]
baseFile.Content = []byte(manifest)
updatedBaseFiles = append(updatedBaseFiles, baseFile)
}

return updatedBaseFiles, nil
}

0 comments on commit 480418f

Please sign in to comment.