generated from kyma-project/template-repository
/
chart.go
144 lines (117 loc) · 3.72 KB
/
chart.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package chart
import (
"context"
"fmt"
"io"
"reflect"
"strings"
"go.uber.org/zap"
"gopkg.in/yaml.v3"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/kube"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage/driver"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type Config struct {
Ctx context.Context
Log *zap.SugaredLogger
Cache ManifestCache
CacheKey types.NamespacedName
ManagerUID string
Cluster Cluster
Release Release
}
type Release struct {
Flags map[string]interface{}
ChartPath string
Name string
Namespace string
}
type Cluster struct {
Client client.Client
Config *rest.Config
}
func parseManifest(manifest string) ([]unstructured.Unstructured, error) {
results := make([]unstructured.Unstructured, 0)
decoder := yaml.NewDecoder(strings.NewReader(manifest))
for {
var obj map[string]interface{}
err := decoder.Decode(&obj)
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
// no obj between separators
if len(obj) == 0 {
continue
}
u := unstructured.Unstructured{Object: obj}
// some resources need to be applied first (before workloads)
// if this statement gets bigger then extract it to the separated place
if u.GetObjectKind().GroupVersionKind().Kind == "CustomResourceDefinition" ||
u.GetObjectKind().GroupVersionKind().Kind == "PriorityClass" {
results = append([]unstructured.Unstructured{u}, results...)
continue
}
results = append(results, u)
}
return results, nil
}
func getCachedAndCurrentManifest(config *Config, renderChartFunc func(config *Config) (*release.Release, error)) (string, string, error) {
cachedSpecManifest, err := config.Cache.Get(config.Ctx, config.CacheKey)
if err != nil {
return "", "", fmt.Errorf("could not get manifest from cache : %s", err.Error())
}
if !shouldRenderAgain(cachedSpecManifest, config) {
return cachedSpecManifest.Manifest, cachedSpecManifest.Manifest, nil
}
currentRelease, err := renderChartFunc(config)
if err != nil {
return cachedSpecManifest.Manifest, "", fmt.Errorf("could not render manifest : %s", err.Error())
}
return cachedSpecManifest.Manifest, currentRelease.Manifest, nil
}
func shouldRenderAgain(spec ServerlessSpecManifest, config *Config) bool {
// spec is up-to-date only if flags used to render and manager is the same one who rendered it before
equalFlags := reflect.DeepEqual(spec.CustomFlags, config.Release.Flags)
return !(spec.ManagerUID == config.ManagerUID && equalFlags)
}
func renderChart(config *Config) (*release.Release, error) {
chart, err := loader.Load(config.Release.ChartPath)
if err != nil {
return nil, fmt.Errorf("while loading chart from path '%s': %s", config.Release.ChartPath, err.Error())
}
installAction := newInstallAction(config)
rel, err := installAction.Run(chart, config.Release.Flags)
if err != nil {
return nil, fmt.Errorf("while templating chart: %s", err.Error())
}
return rel, nil
}
func newInstallAction(config *Config) *action.Install {
helmRESTGetter := &clientGetter{
config: config.Cluster.Config,
}
helmClient := kube.New(helmRESTGetter)
helmClient.Log = config.Log.Debugf
actionConfig := new(action.Configuration)
actionConfig.KubeClient = helmClient
actionConfig.Log = helmClient.Log
actionConfig.Releases = storage.Init(driver.NewMemory())
actionConfig.RESTClientGetter = helmRESTGetter
action := action.NewInstall(actionConfig)
action.ReleaseName = config.Release.Name
action.Namespace = config.Release.Namespace
action.Replace = true
action.IsUpgrade = true
action.DryRun = true
return action
}