Skip to content
This repository has been archived by the owner on Jun 29, 2022. It is now read-only.

Commit

Permalink
Merge pull request #605 from kinvolk/invidian/improvements
Browse files Browse the repository at this point in the history
pkg/components/util: improvements
  • Loading branch information
invidian authored Jun 16, 2020
2 parents 3070ead + 5e69aac commit b7da7fd
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 89 deletions.
2 changes: 1 addition & 1 deletion cli/cmd/component-apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func applyComponents(lokoConfig *config.Config, kubeconfig string, componentName
return diags
}

if err := util.InstallComponent(componentName, component, kubeconfig); err != nil {
if err := util.InstallComponent(component, kubeconfig); err != nil {
return err
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/components/util/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,13 @@ func filterOutUnusedFiles(files map[string]string) map[string]string {

// chartFromComponent creates Helm chart object in memory for given component and makes
// sure it is valid.
func chartFromComponent(name string, c components.Component) (*chart.Chart, error) {
func chartFromComponent(c components.Component) (*chart.Chart, error) {
m, err := c.RenderManifests()
if err != nil {
return nil, fmt.Errorf("rendering manifests failed: %w", err)
}

ch, err := chartFromManifests(name, m)
ch, err := chartFromManifests(c.Metadata().Name, m)
if err != nil {
return nil, fmt.Errorf("creating chart from manifests failed: %w", err)
}
Expand Down
109 changes: 68 additions & 41 deletions pkg/components/util/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"

"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/kube"
"helm.sh/helm/v3/pkg/storage/driver"
v1 "k8s.io/api/core/v1"
Expand All @@ -29,40 +30,44 @@ import (
"github.com/kinvolk/lokomotive/pkg/k8sutil"
)

// InstallComponent installs given component using given kubeconfig.
func InstallComponent(name string, c components.Component, kubeconfig string) error {
return InstallAsRelease(name, c, kubeconfig)
}

// InstallAsRelease installs a component as a Helm release using a Helm client.
func InstallAsRelease(name string, c components.Component, kubeconfig string) error {
cs, err := k8sutil.NewClientset(kubeconfig)
func ensureNamespaceExists(name string, kubeconfigPath string) error {
cs, err := k8sutil.NewClientset(kubeconfigPath)
if err != nil {
return err
return fmt.Errorf("creating clientset: %w", err)
}

// Get the namespace in which the component should be created.
ns := c.Metadata().Namespace
if ns == "" {
return fmt.Errorf("component %s namespace is empty", name)
if name == "" {
return fmt.Errorf("namespace name can't be empty")
}

// Ensure the namespace in which we create release and resources exists.
_, err = cs.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: ns,
Name: name,
},
}, metav1.CreateOptions{})
if err != nil && !errors.IsAlreadyExists(err) {
return err
}

return nil
}

// InstallComponent installs given component using given kubeconfig as a Helm release using a Helm client.
func InstallComponent(c components.Component, kubeconfig string) error {
name := c.Metadata().Name
ns := c.Metadata().Namespace

if err := ensureNamespaceExists(ns, kubeconfig); err != nil {
return fmt.Errorf("failed ensuring that namespace %q for component %q exists: %w", ns, name, err)
}

actionConfig, err := HelmActionConfig(ns, kubeconfig)
if err != nil {
return fmt.Errorf("failed preparing helm client: %w", err)
}

chart, err := chartFromComponent(name, c)
chart, err := chartFromComponent(c)
if err != nil {
return err
}
Expand All @@ -78,37 +83,59 @@ func InstallAsRelease(name string, c components.Component, kubeconfig string) er

wait := c.Metadata().Helm.Wait

helmAction := &helmAction{
releaseName: name,
chart: chart,
actionConfig: actionConfig,
wait: wait,
}

if !exists {
install := action.NewInstall(actionConfig)
install.ReleaseName = name
install.Namespace = ns

// Currently, we install components one-by-one, in the order how they are
// defined in the configuration and we do not support any dependencies between
// the components.
//
// If it is critical for component to have it's dependencies ready before it is
// installed, all dependencies should set Wait field to 'true' in components.HelmMetadata
// struct.
//
// The example of such dependency is between prometheus-operator and openebs-storage-class, where
// both openebs-operator and openebs-storage-class components must be fully functional, before
// prometheus-operator is deployed, otherwise it won't pick the default storage class.
install.Wait = wait

if _, err := install.Run(chart, map[string]interface{}{}); err != nil {
return fmt.Errorf("installing component '%s' as chart failed: %w", name, err)
}

return nil
return install(helmAction, ns)
}

upgrade := action.NewUpgrade(actionConfig)
upgrade.Wait = wait
return upgrade(helmAction)
}

type helmAction struct {
releaseName string
chart *chart.Chart
actionConfig *action.Configuration
wait bool
}

func install(helmAction *helmAction, namespace string) error {
install := action.NewInstall(helmAction.actionConfig)
install.ReleaseName = helmAction.releaseName
install.Namespace = namespace

// Currently, we install components one-by-one, in the order how they are
// defined in the configuration and we do not support any dependencies between
// the components.
//
// If it is critical for component to have it's dependencies ready before it is
// installed, all dependencies should set Wait field to 'true' in components.HelmMetadata
// struct.
//
// The example of such dependency is between prometheus-operator and openebs-storage-class, where
// both openebs-operator and openebs-storage-class components must be fully functional, before
// prometheus-operator is deployed, otherwise it won't pick the default storage class.
install.Wait = helmAction.wait

if _, err := install.Run(helmAction.chart, map[string]interface{}{}); err != nil {
return fmt.Errorf("installing release failed: %w", err)
}

return nil
}

func upgrade(helmAction *helmAction) error {
upgrade := action.NewUpgrade(helmAction.actionConfig)
upgrade.Wait = helmAction.wait
upgrade.RecreateResources = true

if _, err := upgrade.Run(name, chart, map[string]interface{}{}); err != nil {
return fmt.Errorf("updating chart failed: %w", err)
if _, err := upgrade.Run(helmAction.releaseName, helmAction.chart, map[string]interface{}{}); err != nil {
return fmt.Errorf("upgrading release failed: %w", err)
}

return nil
Expand Down
43 changes: 0 additions & 43 deletions pkg/k8sutil/template.go

This file was deleted.

4 changes: 2 additions & 2 deletions test/components/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ component "flatcar-linux-update-operator" {}
}

k := testutil.KubeconfigPath(t)
if err := util.InstallAsRelease(n, c, k); err != nil {
if err := util.InstallComponent(c, k); err != nil {
t.Fatalf("Installing component as release should succeed, got: %v", err)
}

if err := util.InstallAsRelease(n, c, k); err != nil {
if err := util.InstallComponent(c, k); err != nil {
t.Fatalf("Installing component twice as release should succeed, got: %v", err)
}
}

0 comments on commit b7da7fd

Please sign in to comment.