Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App post renderer #255

Merged
merged 2 commits into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions internal/chart/application_chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,11 @@ appVersion: {{ .AppVersion }}

// ChartConfig contains data used to render the helm chart's "Chart.yaml" file.
type ChartConfig struct {
Version string
Description string
AppName string
AppVersion string
Version string
Description string
AppName string
AppVersion string
DeploymentVersions []int
}

// NewChartConfig returns a ChartConfig instance based on the given application.
Expand All @@ -247,13 +248,26 @@ func NewChartConfig(app ketchv1.App) ChartConfig {
version = *app.Spec.Version
}
return ChartConfig{
Version: chartVersion,
Description: app.Spec.Description,
AppName: app.Name,
AppVersion: version,
Version: chartVersion,
Description: app.Spec.Description,
AppName: app.Name,
AppVersion: version,
DeploymentVersions: deploymentVersions(app),
}
}

// targetDeploymentVersion is all deployment version in given app
func deploymentVersions(appCR ketchv1.App) []int {
if len(appCR.Spec.Deployments) == 0 {
return []int{}
}
versions := make([]int, len(appCR.Spec.Deployments))
for i, d := range appCR.Spec.Deployments {
versions[i] = int(d.Version)
}
return versions
}

func (config ChartConfig) render() ([]byte, error) {
buf := bytes.Buffer{}
t := template.Must(template.New("chart.yaml").Parse(chartYaml))
Expand Down
5 changes: 3 additions & 2 deletions internal/chart/application_chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,9 @@ func TestNewApplicationChart(t *testing.T) {
actualFilename := filepath.Join(chartDirectory, fmt.Sprintf("%s.output.yaml", tt.wantYamlsFilename))

chartConfig := ChartConfig{
Version: "0.0.1",
AppName: tt.application.Name,
Version: "0.0.1",
AppName: tt.application.Name,
DeploymentVersions: deploymentVersions(*tt.application),
}

client := HelmClient{cfg: &action.Configuration{KubeClient: &fake.PrintingKubeClient{}, Releases: storage.Init(driver.NewMemory())}, namespace: tt.framework.Spec.NamespaceName, c: clientfake.NewClientBuilder().Build()}
Expand Down
9 changes: 5 additions & 4 deletions internal/chart/chartutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (

func TestBufferedFiles(t *testing.T) {
chartConfig := ChartConfig{
Version: "v0.0.1",
Description: "test config",
AppName: "test app",
AppVersion: "v1",
Version: "v0.0.1",
Description: "test config",
AppName: "test app",
AppVersion: "v1",
DeploymentVersions: []int{1},
}
templates := map[string]string{
"test.yaml": "name: {{ App.spec.name }}",
Expand Down
14 changes: 10 additions & 4 deletions internal/chart/helm_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,11 @@ func (c HelmClient) UpdateChart(tv TemplateValuer, config ChartConfig, opts ...I
clientInstall.ReleaseName = appName
clientInstall.Namespace = c.namespace
clientInstall.PostRenderer = &postRender{
namespace: c.namespace,
cli: c.c,
log: c.log,
cli: c.c,
namespace: c.namespace,
appName: config.AppName,
deploymentVersions: config.DeploymentVersions,
}
for _, opt := range opts {
opt(clientInstall)
Expand All @@ -119,8 +122,11 @@ func (c HelmClient) UpdateChart(tv TemplateValuer, config ChartConfig, opts ...I
// Let's set it to minimal to disable "helm rollback".
updateClient.MaxHistory = 1
updateClient.PostRenderer = &postRender{
namespace: c.namespace,
cli: c.c,
cli: c.c,
log: c.log,
namespace: c.namespace,
appName: config.AppName,
deploymentVersions: config.DeploymentVersions,
}
shouldUpdate, err := c.isHelmChartStatusActionable(c.statusFunc, appName, helmStatusActionMapUpdate)
if err != nil || !shouldUpdate {
Expand Down
9 changes: 5 additions & 4 deletions internal/chart/job_chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ func NewJobChartConfig(job ketchv1.Job) ChartConfig {
version = job.Spec.Version
}
return ChartConfig{
Version: chartVersion,
Description: job.Spec.Description,
AppName: job.Spec.Name,
AppVersion: version,
Version: chartVersion,
Description: job.Spec.Description,
AppName: job.Spec.Name,
AppVersion: version,
DeploymentVersions: []int{int(job.ObjectMeta.Generation)},
}
}

Expand Down
9 changes: 5 additions & 4 deletions internal/chart/job_chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,11 @@ func TestNewJobChart(t *testing.T) {

func TestNewJobChartConfig(t *testing.T) {
expected := ChartConfig{
Version: fmt.Sprintf("v0.0.%v", testJob.ObjectMeta.Generation),
Description: testJob.Spec.Description,
AppName: testJob.Spec.Name,
AppVersion: testJob.Spec.Version,
Version: fmt.Sprintf("v0.0.%v", testJob.ObjectMeta.Generation),
Description: testJob.Spec.Description,
AppName: testJob.Spec.Name,
AppVersion: testJob.Spec.Version,
DeploymentVersions: []int{int(testJob.ObjectMeta.Generation)},
}
chartConfig := NewJobChartConfig(*testJob)
require.Equal(t, expected, chartConfig)
Expand Down
93 changes: 58 additions & 35 deletions internal/chart/post_render.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package chart
import (
"bytes"
"context"
"fmt"
"strings"

"github.com/go-logr/logr"
"helm.sh/helm/v3/pkg/postrender"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -16,8 +18,12 @@ import (
var _ postrender.PostRenderer = &postRender{}

type postRender struct {
cli client.Client
namespace string
log logr.Logger
cli client.Client

appName string
deploymentVersions []int
namespace string
}

func (p *postRender) Run(renderedManifests *bytes.Buffer) (modifiedManifests *bytes.Buffer, err error) {
Expand All @@ -27,51 +33,68 @@ func (p *postRender) Run(renderedManifests *bytes.Buffer) (modifiedManifests *by
return nil, err
}

fs := filesys.MakeFsInMemory()
if err := fs.Mkdir(p.namespace); err != nil {
return nil, err
}

var postrenderFound bool
finalBuffer := renderedManifests
for _, cm := range configMapList.Items {
if !strings.HasSuffix(cm.Name, "-postrender") {
fwPatch := strings.HasSuffix(cm.Name, "-postrender")
var appPatch bool
for _, dv := range p.deploymentVersions {
koncar marked this conversation as resolved.
Show resolved Hide resolved
appPatch = strings.HasPrefix(cm.Name, fmt.Sprintf("%s-%d-app-post-render", p.appName, dv))
if appPatch {
break
}
}

if !fwPatch && !appPatch {
continue
}
postrenderFound = true
p.log.Info(fmt.Sprintf("including post renderer patch: appPatch: %t, fwPatch %t, %s ", appPatch, fwPatch, cm.Name))

fs := filesys.MakeFsInMemory()
localPath := p.localPath(fwPatch, p.appName)
if !fs.Exists(localPath) {
if err := fs.Mkdir(localPath); err != nil {
return nil, err
}
}

for k, v := range cm.Data {
fileName := p.namespace + "/" + k
fileName := localPath + "/" + k
if err := fs.WriteFile(fileName, []byte(v)); err != nil {
return nil, err
}
}
}

// return original manifests, otherwise begin postrender
if !postrenderFound {
return renderedManifests, nil
}
if err := fs.WriteFile(localPath+"/app.yaml", finalBuffer.Bytes()); err != nil {
return nil, err
}
kustomizer := krusty.MakeKustomizer(&krusty.Options{
PluginConfig: &kTypes.PluginConfig{
HelmConfig: kTypes.HelmConfig{
Enabled: true,
Command: "helm",
},
},
})

if err := fs.WriteFile(p.namespace+"/app.yaml", renderedManifests.Bytes()); err != nil {
return nil, err
result, err := kustomizer.Run(fs, localPath)
if err != nil {
return nil, err
}
y, err := result.AsYaml()
if err != nil {
return nil, err
}
finalBuffer = bytes.NewBuffer(y)
if err := fs.WriteFile(localPath+"/app.yaml", finalBuffer.Bytes()); err != nil {
return nil, err
}
}

kustomizer := krusty.MakeKustomizer(&krusty.Options{
PluginConfig: &kTypes.PluginConfig{
HelmConfig: kTypes.HelmConfig{
Enabled: true,
Command: "helm",
},
},
})
return finalBuffer, nil
}

result, err := kustomizer.Run(fs, p.namespace)
if err != nil {
return nil, err
}
y, err := result.AsYaml()
if err != nil {
return nil, err
func (p postRender) localPath(fwPatch bool, name string) string {
if fwPatch {
return p.namespace
}
return bytes.NewBuffer(y), nil
return p.appName
}
32 changes: 28 additions & 4 deletions internal/chart/post_render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,30 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/log"
)

//go:embed testdata/render_yamls/prerendered-manifests.yaml
var prerender []byte

//go:embed testdata/render_yamls/kustomization.yaml
var kustomizationYaml string
//go:embed testdata/render_yamls/nodeaffinity-kustomization.yaml
var kustomizationNodeAffinityYaml string

//go:embed testdata/render_yamls/nodeaffinity-patch.yaml
var nodeaffinityPatchYaml string

//go:embed testdata/render_yamls/nodeaffinity-postrender.yaml
var nodeaffinityPostrender string

//go:embed testdata/render_yamls/annotations-kustomization.yaml
var kustomizationAnnotationsYaml string

//go:embed testdata/render_yamls/annotations-patch.yaml
var annotationsPatchYaml string

//go:embed testdata/render_yamls/annotations-postrender.yaml
var annotationsPostrender string

func TestPostRenderRun(t *testing.T) {
tc := []struct {
name string
Expand All @@ -48,19 +58,33 @@ func TestPostRenderRun(t *testing.T) {
Namespace: "fake",
},
Data: map[string]string{
"kustomization.yaml": kustomizationYaml,
"kustomization.yaml": kustomizationNodeAffinityYaml,
"patch.yaml": nodeaffinityPatchYaml,
},
},
expected: nodeaffinityPostrender,
},
{
name: "postrender found, patch annotations",
configmap: corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "nodeaffinity-postrender",
Namespace: "fake",
},
Data: map[string]string{
"kustomization.yaml": kustomizationAnnotationsYaml,
"patch-annotations-Deployment.yaml": annotationsPatchYaml,
},
},
expected: annotationsPostrender,
},
}
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
client := fake.NewClientBuilder()
client.WithObjects(&tt.configmap)

pr := postRender{
log: log.NullLogger{},
namespace: "fake",
cli: client.Build(),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resources:
- app.yaml
patches:
- path: patch-annotations-Deployment.yaml
target:
kind: Deployment
name: ".*"
6 changes: 6 additions & 0 deletions internal/chart/testdata/render_yamls/annotations-patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- op: add
path: /metadata/annotations/app
value: "app1"
- op: add
path: /metadata/annotations/shipa.io~1is-shipa
value: "true"
Loading