@@ -19,6 +19,7 @@ import (
1919 "fmt"
2020
2121 "helm.sh/helm/v3/pkg/action"
22+ "helm.sh/helm/v3/pkg/chart"
2223 "helm.sh/helm/v3/pkg/kube"
2324 "helm.sh/helm/v3/pkg/storage/driver"
2425 v1 "k8s.io/api/core/v1"
@@ -29,40 +30,44 @@ import (
2930 "github.com/kinvolk/lokomotive/pkg/k8sutil"
3031)
3132
32- // InstallComponent installs given component using given kubeconfig.
33- func InstallComponent (name string , c components.Component , kubeconfig string ) error {
34- return InstallAsRelease (name , c , kubeconfig )
35- }
36-
37- // InstallAsRelease installs a component as a Helm release using a Helm client.
38- func InstallAsRelease (name string , c components.Component , kubeconfig string ) error {
39- cs , err := k8sutil .NewClientset (kubeconfig )
33+ func ensureNamespaceExists (name string , kubeconfigPath string ) error {
34+ cs , err := k8sutil .NewClientset (kubeconfigPath )
4035 if err != nil {
41- return err
36+ return fmt . Errorf ( "creating clientset: %w" , err )
4237 }
4338
44- // Get the namespace in which the component should be created.
45- ns := c .Metadata ().Namespace
46- if ns == "" {
47- return fmt .Errorf ("component %s namespace is empty" , name )
39+ if name == "" {
40+ return fmt .Errorf ("namespace name can't be empty" )
4841 }
4942
5043 // Ensure the namespace in which we create release and resources exists.
5144 _ , err = cs .CoreV1 ().Namespaces ().Create (context .TODO (), & v1.Namespace {
5245 ObjectMeta : metav1.ObjectMeta {
53- Name : ns ,
46+ Name : name ,
5447 },
5548 }, metav1.CreateOptions {})
5649 if err != nil && ! errors .IsAlreadyExists (err ) {
5750 return err
5851 }
5952
53+ return nil
54+ }
55+
56+ // InstallComponent installs given component using given kubeconfig as a Helm release using a Helm client.
57+ func InstallComponent (c components.Component , kubeconfig string ) error {
58+ name := c .Metadata ().Name
59+ ns := c .Metadata ().Namespace
60+
61+ if err := ensureNamespaceExists (ns , kubeconfig ); err != nil {
62+ return fmt .Errorf ("failed ensuring that namespace %q for component %q exists: %w" , ns , name , err )
63+ }
64+
6065 actionConfig , err := HelmActionConfig (ns , kubeconfig )
6166 if err != nil {
6267 return fmt .Errorf ("failed preparing helm client: %w" , err )
6368 }
6469
65- chart , err := chartFromComponent (name , c )
70+ chart , err := chartFromComponent (c )
6671 if err != nil {
6772 return err
6873 }
@@ -78,37 +83,59 @@ func InstallAsRelease(name string, c components.Component, kubeconfig string) er
7883
7984 wait := c .Metadata ().Helm .Wait
8085
86+ helmAction := & helmAction {
87+ releaseName : name ,
88+ chart : chart ,
89+ actionConfig : actionConfig ,
90+ wait : wait ,
91+ }
92+
8193 if ! exists {
82- install := action .NewInstall (actionConfig )
83- install .ReleaseName = name
84- install .Namespace = ns
85-
86- // Currently, we install components one-by-one, in the order how they are
87- // defined in the configuration and we do not support any dependencies between
88- // the components.
89- //
90- // If it is critical for component to have it's dependencies ready before it is
91- // installed, all dependencies should set Wait field to 'true' in components.HelmMetadata
92- // struct.
93- //
94- // The example of such dependency is between prometheus-operator and openebs-storage-class, where
95- // both openebs-operator and openebs-storage-class components must be fully functional, before
96- // prometheus-operator is deployed, otherwise it won't pick the default storage class.
97- install .Wait = wait
98-
99- if _ , err := install .Run (chart , map [string ]interface {}{}); err != nil {
100- return fmt .Errorf ("installing component '%s' as chart failed: %w" , name , err )
101- }
102-
103- return nil
94+ return install (helmAction , ns )
10495 }
10596
106- upgrade := action .NewUpgrade (actionConfig )
107- upgrade .Wait = wait
97+ return upgrade (helmAction )
98+ }
99+
100+ type helmAction struct {
101+ releaseName string
102+ chart * chart.Chart
103+ actionConfig * action.Configuration
104+ wait bool
105+ }
106+
107+ func install (helmAction * helmAction , namespace string ) error {
108+ install := action .NewInstall (helmAction .actionConfig )
109+ install .ReleaseName = helmAction .releaseName
110+ install .Namespace = namespace
111+
112+ // Currently, we install components one-by-one, in the order how they are
113+ // defined in the configuration and we do not support any dependencies between
114+ // the components.
115+ //
116+ // If it is critical for component to have it's dependencies ready before it is
117+ // installed, all dependencies should set Wait field to 'true' in components.HelmMetadata
118+ // struct.
119+ //
120+ // The example of such dependency is between prometheus-operator and openebs-storage-class, where
121+ // both openebs-operator and openebs-storage-class components must be fully functional, before
122+ // prometheus-operator is deployed, otherwise it won't pick the default storage class.
123+ install .Wait = helmAction .wait
124+
125+ if _ , err := install .Run (helmAction .chart , map [string ]interface {}{}); err != nil {
126+ return fmt .Errorf ("installing release failed: %w" , err )
127+ }
128+
129+ return nil
130+ }
131+
132+ func upgrade (helmAction * helmAction ) error {
133+ upgrade := action .NewUpgrade (helmAction .actionConfig )
134+ upgrade .Wait = helmAction .wait
108135 upgrade .RecreateResources = true
109136
110- if _ , err := upgrade .Run (name , chart , map [string ]interface {}{}); err != nil {
111- return fmt .Errorf ("updating chart failed: %w" , err )
137+ if _ , err := upgrade .Run (helmAction . releaseName , helmAction . chart , map [string ]interface {}{}); err != nil {
138+ return fmt .Errorf ("upgrading release failed: %w" , err )
112139 }
113140
114141 return nil
0 commit comments