Skip to content

Commit 479bd02

Browse files
committed
Sort manifests, options to create ns & use non-strict engine for helm addons
Signed-off-by: Tamal Saha <tamal@appscode.com>
1 parent 8c5a456 commit 479bd02

File tree

15 files changed

+1222
-30
lines changed

15 files changed

+1222
-30
lines changed

pkg/addonfactory/addonfactory.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ type AgentAddonFactory struct {
3434
getValuesFuncs []GetValuesFunc
3535
agentAddonOptions agent.AgentAddonOptions
3636
// trimCRDDescription flag is used to trim the description of CRDs in manifestWork. disabled by default.
37-
trimCRDDescription bool
38-
hostingCluster *clusterv1.ManagedCluster
39-
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
37+
trimCRDDescription bool
38+
hostingCluster *clusterv1.ManagedCluster
39+
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
40+
createAgentInstallNamespace bool
41+
helmEngineStrict bool
4042
}
4143

4244
// NewAgentAddonFactory builds an addonAgentFactory instance with addon name and fs.
@@ -57,8 +59,10 @@ func NewAgentAddonFactory(addonName string, fs embed.FS, dir string) *AgentAddon
5759
HealthProber: nil,
5860
SupportedConfigGVRs: []schema.GroupVersionResource{},
5961
},
60-
trimCRDDescription: false,
61-
scheme: s,
62+
trimCRDDescription: false,
63+
scheme: s,
64+
createAgentInstallNamespace: false,
65+
helmEngineStrict: false,
6266
}
6367
}
6468

@@ -115,6 +119,18 @@ func (f *AgentAddonFactory) WithTrimCRDDescription() *AgentAddonFactory {
115119
return f
116120
}
117121

122+
// WithCreateAgentInstallNamespace is to create the agent install namespace object in manifestWork.
123+
func (f *AgentAddonFactory) WithCreateAgentInstallNamespace() *AgentAddonFactory {
124+
f.createAgentInstallNamespace = true
125+
return f
126+
}
127+
128+
// WithHelmEngineStrict is to enable script go template rendering for Helm charts to generate manifestWork.
129+
func (f *AgentAddonFactory) WithHelmEngineStrict() *AgentAddonFactory {
130+
f.helmEngineStrict = true
131+
return f
132+
}
133+
118134
// WithConfigGVRs defines the addon supported configuration GroupVersionResource
119135
func (f *AgentAddonFactory) WithConfigGVRs(gvrs ...schema.GroupVersionResource) *AgentAddonFactory {
120136
f.agentAddonOptions.SupportedConfigGVRs = append(f.agentAddonOptions.SupportedConfigGVRs, gvrs...)

pkg/addonfactory/helm_agentaddon.go

Lines changed: 102 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import (
1010
"helm.sh/helm/v3/pkg/chart"
1111
"helm.sh/helm/v3/pkg/chartutil"
1212
"helm.sh/helm/v3/pkg/engine"
13+
"helm.sh/helm/v3/pkg/releaseutil"
14+
"k8s.io/apimachinery/pkg/api/meta"
15+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1316
"k8s.io/apimachinery/pkg/runtime"
1417
"k8s.io/apimachinery/pkg/runtime/serializer"
1518
"k8s.io/apimachinery/pkg/util/yaml"
@@ -42,28 +45,59 @@ type helmDefaultValues struct {
4245
}
4346

4447
type HelmAgentAddon struct {
45-
decoder runtime.Decoder
46-
chart *chart.Chart
47-
getValuesFuncs []GetValuesFunc
48-
agentAddonOptions agent.AgentAddonOptions
49-
trimCRDDescription bool
50-
hostingCluster *clusterv1.ManagedCluster
51-
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
48+
decoder runtime.Decoder
49+
chart *chart.Chart
50+
getValuesFuncs []GetValuesFunc
51+
agentAddonOptions agent.AgentAddonOptions
52+
trimCRDDescription bool
53+
hostingCluster *clusterv1.ManagedCluster
54+
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
55+
createAgentInstallNamespace bool
56+
helmEngineStrict bool
5257
}
5358

5459
func newHelmAgentAddon(factory *AgentAddonFactory, chart *chart.Chart) *HelmAgentAddon {
5560
return &HelmAgentAddon{
56-
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
57-
chart: chart,
58-
getValuesFuncs: factory.getValuesFuncs,
59-
agentAddonOptions: factory.agentAddonOptions,
60-
trimCRDDescription: factory.trimCRDDescription,
61-
hostingCluster: factory.hostingCluster,
62-
agentInstallNamespace: factory.agentInstallNamespace,
61+
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
62+
chart: chart,
63+
getValuesFuncs: factory.getValuesFuncs,
64+
agentAddonOptions: factory.agentAddonOptions,
65+
trimCRDDescription: factory.trimCRDDescription,
66+
hostingCluster: factory.hostingCluster,
67+
agentInstallNamespace: factory.agentInstallNamespace,
68+
createAgentInstallNamespace: factory.createAgentInstallNamespace,
69+
helmEngineStrict: factory.helmEngineStrict,
6370
}
6471
}
6572

6673
func (a *HelmAgentAddon) Manifests(
74+
cluster *clusterv1.ManagedCluster,
75+
addon *addonapiv1alpha1.ManagedClusterAddOn) ([]runtime.Object, error) {
76+
objects, err := a.renderManifests(cluster, addon)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
manifests := make([]Manifest, 0, len(objects))
82+
for _, obj := range objects {
83+
a, err := meta.TypeAccessor(obj)
84+
if err != nil {
85+
return nil, err
86+
}
87+
manifests = append(manifests, Manifest{
88+
Object: obj,
89+
Kind: a.GetKind(),
90+
})
91+
}
92+
sortManifestsByKind(manifests, releaseutil.InstallOrder)
93+
94+
for i, manifest := range manifests {
95+
objects[i] = manifest.Object
96+
}
97+
return objects, nil
98+
}
99+
100+
func (a *HelmAgentAddon) renderManifests(
67101
cluster *clusterv1.ManagedCluster,
68102
addon *addonapiv1alpha1.ManagedClusterAddOn) ([]runtime.Object, error) {
69103
var objects []runtime.Object
@@ -74,7 +108,7 @@ func (a *HelmAgentAddon) Manifests(
74108
}
75109

76110
helmEngine := engine.Engine{
77-
Strict: true,
111+
Strict: a.helmEngineStrict,
78112
LintMode: false,
79113
}
80114

@@ -93,16 +127,8 @@ func (a *HelmAgentAddon) Manifests(
93127
return objects, err
94128
}
95129

96-
// sort the filenames of the templates so the manifests are ordered consistently
97-
keys := make([]string, 0, len(templates))
98-
for k := range templates {
99-
keys = append(keys, k)
100-
}
101-
sort.Strings(keys)
102-
103-
for _, k := range keys {
104-
data := templates[k]
105-
130+
agentInstallNamespace := a.getValueAgentInstallNamespace(addon)
131+
for k, data := range templates {
106132
if len(data) == 0 {
107133
continue
108134
}
@@ -132,6 +158,13 @@ func (a *HelmAgentAddon) Manifests(
132158
}
133159
}
134160

161+
if agentInstallNamespace != "" && a.createAgentInstallNamespace {
162+
var ns unstructured.Unstructured
163+
ns.SetAPIVersion("v1")
164+
ns.SetKind("Namespace")
165+
ns.SetName(agentInstallNamespace)
166+
objects = append(objects, &ns)
167+
}
135168
}
136169

137170
if a.trimCRDDescription {
@@ -260,3 +293,47 @@ func (a *HelmAgentAddon) releaseOptions(
260293
Namespace: a.getValueAgentInstallNamespace(addon),
261294
}
262295
}
296+
297+
// Manifest represents a manifest file, which has a name and some content.
298+
type Manifest struct {
299+
Object runtime.Object
300+
Kind string
301+
}
302+
303+
// sort manifests by kind.
304+
//
305+
// Results are sorted by 'ordering', keeping order of items with equal kind/priority
306+
func sortManifestsByKind(manifests []Manifest, ordering releaseutil.KindSortOrder) []Manifest {
307+
sort.SliceStable(manifests, func(i, j int) bool {
308+
return lessByKind(manifests[i], manifests[j], manifests[i].Kind, manifests[j].Kind, ordering)
309+
})
310+
311+
return manifests
312+
}
313+
314+
func lessByKind(a interface{}, b interface{}, kindA string, kindB string, o releaseutil.KindSortOrder) bool {
315+
ordering := make(map[string]int, len(o))
316+
for v, k := range o {
317+
ordering[k] = v
318+
}
319+
320+
first, aok := ordering[kindA]
321+
second, bok := ordering[kindB]
322+
323+
if !aok && !bok {
324+
// if both are unknown then sort alphabetically by kind, keep original order if same kind
325+
if kindA != kindB {
326+
return kindA < kindB
327+
}
328+
return first < second
329+
}
330+
// unknown kind is last
331+
if !aok {
332+
return false
333+
}
334+
if !bok {
335+
return true
336+
}
337+
// sort different kinds, keep original order if same priority
338+
return first < second
339+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
Copyright The Helm Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package release
18+
19+
import (
20+
"helm.sh/helm/v3/pkg/time"
21+
)
22+
23+
// HookEvent specifies the hook event
24+
type HookEvent string
25+
26+
// Hook event types
27+
const (
28+
HookPreInstall HookEvent = "pre-install"
29+
HookPostInstall HookEvent = "post-install"
30+
HookPreDelete HookEvent = "pre-delete"
31+
HookPostDelete HookEvent = "post-delete"
32+
HookPreUpgrade HookEvent = "pre-upgrade"
33+
HookPostUpgrade HookEvent = "post-upgrade"
34+
HookPreRollback HookEvent = "pre-rollback"
35+
HookPostRollback HookEvent = "post-rollback"
36+
HookTest HookEvent = "test"
37+
)
38+
39+
func (x HookEvent) String() string { return string(x) }
40+
41+
// HookDeletePolicy specifies the hook delete policy
42+
type HookDeletePolicy string
43+
44+
// Hook delete policy types
45+
const (
46+
HookSucceeded HookDeletePolicy = "hook-succeeded"
47+
HookFailed HookDeletePolicy = "hook-failed"
48+
HookBeforeHookCreation HookDeletePolicy = "before-hook-creation"
49+
)
50+
51+
func (x HookDeletePolicy) String() string { return string(x) }
52+
53+
// HookAnnotation is the label name for a hook
54+
const HookAnnotation = "helm.sh/hook"
55+
56+
// HookWeightAnnotation is the label name for a hook weight
57+
const HookWeightAnnotation = "helm.sh/hook-weight"
58+
59+
// HookDeleteAnnotation is the label name for the delete policy for a hook
60+
const HookDeleteAnnotation = "helm.sh/hook-delete-policy"
61+
62+
// Hook defines a hook object.
63+
type Hook struct {
64+
Name string `json:"name,omitempty"`
65+
// Kind is the Kubernetes kind.
66+
Kind string `json:"kind,omitempty"`
67+
// Path is the chart-relative path to the template.
68+
Path string `json:"path,omitempty"`
69+
// Manifest is the manifest contents.
70+
Manifest string `json:"manifest,omitempty"`
71+
// Events are the events that this hook fires on.
72+
Events []HookEvent `json:"events,omitempty"`
73+
// LastRun indicates the date/time this was last run.
74+
LastRun HookExecution `json:"last_run,omitempty"`
75+
// Weight indicates the sort order for execution among similar Hook type
76+
Weight int `json:"weight,omitempty"`
77+
// DeletePolicies are the policies that indicate when to delete the hook
78+
DeletePolicies []HookDeletePolicy `json:"delete_policies,omitempty"`
79+
}
80+
81+
// A HookExecution records the result for the last execution of a hook for a given release.
82+
type HookExecution struct {
83+
// StartedAt indicates the date/time this hook was started
84+
StartedAt time.Time `json:"started_at,omitempty"`
85+
// CompletedAt indicates the date/time this hook was completed.
86+
CompletedAt time.Time `json:"completed_at,omitempty"`
87+
// Phase indicates whether the hook completed successfully
88+
Phase HookPhase `json:"phase"`
89+
}
90+
91+
// A HookPhase indicates the state of a hook execution
92+
type HookPhase string
93+
94+
const (
95+
// HookPhaseUnknown indicates that a hook is in an unknown state
96+
HookPhaseUnknown HookPhase = "Unknown"
97+
// HookPhaseRunning indicates that a hook is currently executing
98+
HookPhaseRunning HookPhase = "Running"
99+
// HookPhaseSucceeded indicates that hook execution succeeded
100+
HookPhaseSucceeded HookPhase = "Succeeded"
101+
// HookPhaseFailed indicates that hook execution failed
102+
HookPhaseFailed HookPhase = "Failed"
103+
)
104+
105+
// String converts a hook phase to a printable string
106+
func (x HookPhase) String() string { return string(x) }
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
Copyright The Helm Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package release
17+
18+
import (
19+
"k8s.io/apimachinery/pkg/runtime"
20+
21+
"helm.sh/helm/v3/pkg/time"
22+
)
23+
24+
// Info describes release information.
25+
type Info struct {
26+
// FirstDeployed is when the release was first deployed.
27+
FirstDeployed time.Time `json:"first_deployed,omitempty"`
28+
// LastDeployed is when the release was last deployed.
29+
LastDeployed time.Time `json:"last_deployed,omitempty"`
30+
// Deleted tracks when this object was deleted.
31+
Deleted time.Time `json:"deleted"`
32+
// Description is human-friendly "log entry" about this release.
33+
Description string `json:"description,omitempty"`
34+
// Status is the current state of the release
35+
Status Status `json:"status,omitempty"`
36+
// Contains the rendered templates/NOTES.txt if available
37+
Notes string `json:"notes,omitempty"`
38+
// Contains the deployed resources information
39+
Resources map[string][]runtime.Object `json:"resources,omitempty"`
40+
}

0 commit comments

Comments
 (0)