Skip to content

Commit

Permalink
implement svc label/annotation for dynamic zarf connect, update doom ex
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-mccoy committed Feb 8, 2022
1 parent d370dd0 commit 3db45b2
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 71 deletions.
5 changes: 2 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@
"program": "${workspaceFolder}/cli",
"env": {},
"args": [
"init",
"--confirm",
"--components=gitops-service"
"connect",
"doom"
]
},

Expand Down
3 changes: 3 additions & 0 deletions cli/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const (
ZarfSeedTypeCLIInject = "cli-inject"
ZarfSeedTypeRuntimeRegistry = "runtime-registry"
ZarfSeedTypeInClusterRegistry = "in-cluster-registry"

ZarfConnectLabelName = "zarf.dev/connect-name"
ZarfConnectAnnotationDescription = "zarf.dev/connect-description"
)

var (
Expand Down
66 changes: 51 additions & 15 deletions cli/internal/helm/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package helm
import (
"bytes"
"fmt"
"github.com/defenseunicorns/zarf/cli/types"
"io/ioutil"
"os"
"time"

"github.com/defenseunicorns/zarf/cli/config"
"github.com/defenseunicorns/zarf/cli/types"

"github.com/defenseunicorns/zarf/cli/internal/k8s"
"github.com/defenseunicorns/zarf/cli/internal/message"
"github.com/defenseunicorns/zarf/cli/internal/utils"
Expand All @@ -17,6 +19,7 @@ import (
"helm.sh/helm/v3/pkg/storage/driver"
)

type ConnectStrings map[string]string
type ChartOptions struct {
BasePath string
Chart types.ZarfChart
Expand All @@ -28,12 +31,13 @@ type ChartOptions struct {
}

type renderer struct {
images []string
namespaces []string
images []string
namespaces []string
connectStrings ConnectStrings
}

// InstallOrUpgradeChart performs a helm install of the given chart
func InstallOrUpgradeChart(options ChartOptions) {
func InstallOrUpgradeChart(options ChartOptions) ConnectStrings {
spinner := message.NewProgressSpinner("Processing helm chart %s:%s from %s",
options.Chart.Name,
options.Chart.Version,
Expand All @@ -42,6 +46,7 @@ func InstallOrUpgradeChart(options ChartOptions) {

var output *release.Release

postRender := NewRenderer(options.Images, options.Chart.Namespace)
options.ReleaseName = fmt.Sprintf("zarf-%s", options.Chart.Name)
actionConfig, err := createActionConfig(options.Chart.Namespace)

Expand Down Expand Up @@ -79,12 +84,12 @@ func InstallOrUpgradeChart(options ChartOptions) {
case driver.ErrReleaseNotFound:
// No prior release, try to install it
spinner.Updatef("Attempting chart installation")
output, err = installChart(actionConfig, options)
output, err = installChart(actionConfig, options, postRender)

case nil:
// Otherwise, there is a prior release so upgrade it
spinner.Updatef("Attempting chart upgrade")
output, err = upgradeChart(actionConfig, options)
output, err = upgradeChart(actionConfig, options, postRender)

default:
// 😭 things aren't working
Expand All @@ -102,10 +107,14 @@ func InstallOrUpgradeChart(options ChartOptions) {
}

}

// return any collected connect strings for zarf connect
return postRender.connectStrings
}

// TemplateChart generates a helm template from a given chart
func TemplateChart(options ChartOptions) (string, error) {
message.Debugf("helm.TemplateChart(%v)", options)

actionConfig, err := createActionConfig(options.Chart.Namespace)

Expand Down Expand Up @@ -141,7 +150,8 @@ func TemplateChart(options ChartOptions) (string, error) {
return templatedChart.Manifest, nil
}

func GenerateChart(basePath string, manifest types.ZarfManifest, images []string) {
func GenerateChart(basePath string, manifest types.ZarfManifest, images []string) ConnectStrings {
message.Debugf("helm.GenerateChart(%s, %v, %v)", basePath, manifest, images)
spinner := message.NewProgressSpinner("Starting helm chart generation %s", manifest.Name)
defer spinner.Stop()

Expand Down Expand Up @@ -189,10 +199,11 @@ func GenerateChart(basePath string, manifest types.ZarfManifest, images []string

spinner.Success()

InstallOrUpgradeChart(options)
return InstallOrUpgradeChart(options)
}

func installChart(actionConfig *action.Configuration, options ChartOptions) (*release.Release, error) {
func installChart(actionConfig *action.Configuration, options ChartOptions, postRender *renderer) (*release.Release, error) {
message.Debugf("helm.installChart(%v, %v, %v)", actionConfig, options, postRender)
// Bind the helm action
client := action.NewInstall(actionConfig)

Expand All @@ -211,7 +222,7 @@ func installChart(actionConfig *action.Configuration, options ChartOptions) (*re
client.Namespace = options.Chart.Namespace

// Post-processing our manifests for reasons....
client.PostRenderer = NewRenderer(options.Images, options.Chart.Namespace)
client.PostRenderer = postRender

loadedChart, chartValues, err := loadChartData(options)
if err != nil {
Expand All @@ -222,7 +233,8 @@ func installChart(actionConfig *action.Configuration, options ChartOptions) (*re
return client.Run(loadedChart, chartValues)
}

func upgradeChart(actionConfig *action.Configuration, options ChartOptions) (*release.Release, error) {
func upgradeChart(actionConfig *action.Configuration, options ChartOptions, postRender *renderer) (*release.Release, error) {
message.Debugf("helm.upgradeChart(%v, %v, %v)", actionConfig, options, postRender)
client := action.NewUpgrade(actionConfig)

// Let each chart run for 5 minutes
Expand All @@ -236,7 +248,7 @@ func upgradeChart(actionConfig *action.Configuration, options ChartOptions) (*re
client.Namespace = options.Chart.Namespace

// Post-processing our manifests for reasons....
client.PostRenderer = NewRenderer(options.Images, options.Chart.Namespace)
client.PostRenderer = postRender

loadedChart, chartValues, err := loadChartData(options)
if err != nil {
Expand All @@ -248,6 +260,7 @@ func upgradeChart(actionConfig *action.Configuration, options ChartOptions) (*re
}

func rollbackChart(actionConfig *action.Configuration, name string) error {
message.Debugf("helm.rollbackChart(%v, %s)", actionConfig, name)
client := action.NewRollback(actionConfig)
client.CleanupOnFail = true
client.Force = true
Expand All @@ -257,6 +270,7 @@ func rollbackChart(actionConfig *action.Configuration, name string) error {
}

func uninstallChart(actionConfig *action.Configuration, name string) (*release.UninstallReleaseResponse, error) {
message.Debugf("helm.uninstallChart(%v, %s)", actionConfig, name)
client := action.NewUninstall(actionConfig)
client.KeepHistory = false
client.Timeout = 3 * time.Minute
Expand All @@ -265,6 +279,7 @@ func uninstallChart(actionConfig *action.Configuration, name string) (*release.U
}

func loadChartData(options ChartOptions) (*chart.Chart, map[string]interface{}, error) {
message.Debugf("helm.loadChartData(%v)", options)
var (
loadedChart *chart.Chart
chartValues map[string]interface{}
Expand Down Expand Up @@ -293,14 +308,16 @@ func loadChartData(options ChartOptions) (*chart.Chart, map[string]interface{},
}

func NewRenderer(images []string, namespace string) *renderer {
message.Debugf("helm.NewRenderer(%v, %s)", images, namespace)
return &renderer{
images: images,
namespaces: []string{namespace},
images: images,
namespaces: []string{namespace},
connectStrings: make(ConnectStrings),
}
}

func (r *renderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) {
message.Debug("Post-rendering helm chart")
message.Debugf("helm.Run(%v)", renderedManifests)
// This is very low cost and consistent for how we replace elsewhere, also good for debugging
tempDir, _ := utils.MakeTempDir()
path := tempDir + "/chart.yaml"
Expand Down Expand Up @@ -331,10 +348,28 @@ func (r *renderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) {
// grab the namespace,
namespace := resource.GetNamespace()
message.Debugf("Found namespace %s", namespace)

// and append to the list if it's unique
if namespace != "" && !contains(r.namespaces, namespace) {
r.namespaces = append(r.namespaces, namespace)
}

if resource.GetKind() == "Service" {
// Check service resources for the zarf-connect label
labels := resource.GetLabels()
annotations := resource.GetAnnotations()

if key, keyExists := labels[config.ZarfConnectLabelName]; keyExists {
// If there is a zarf-connect label
if description, descExists := annotations[config.ZarfConnectAnnotationDescription]; descExists {
// and a description set the label and description
r.connectStrings[key] = description
} else {
// Otherwise, just set the label
r.connectStrings[key] = ""
}
}
}
}
}

Expand All @@ -352,6 +387,7 @@ func (r *renderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) {
}

func contains(haystack []string, needle string) bool {
message.Debugf("helm.contains(%v, %s)", haystack, needle)
for _, hay := range haystack {
if hay == needle {
return true
Expand Down
19 changes: 19 additions & 0 deletions cli/internal/k8s/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,31 @@ package k8s
import (
"context"

"github.com/defenseunicorns/zarf/cli/internal/message"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// GetService returns a Kubernetes service resource in the provided namespace with the given name.
func GetService(namespace string, serviceName string) (*corev1.Service, error) {
message.Debugf("k8s.GetService(%s, %s)", namespace, serviceName)
clientset := getClientset()
return clientset.CoreV1().Services(namespace).Get(context.Background(), serviceName, metav1.GetOptions{})
}

// GetServicesByLabelExists returns a list of matched services given a set of labels. TO search all namespaces, pass "" in the namespace arg
func GetServicesByLabelExists(namespace string, label string) (*corev1.ServiceList, error) {
message.Debugf("k8s.GetServicesByLabelExists(%s, %s)", namespace, label)
clientset := getClientset()

// Creat the selector and add the requirement
labelSelector, _ := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{{
Key: label,
Operator: metav1.LabelSelectorOpExists,
}},
})

// Run the query with the selector and return as a ServiceList
return clientset.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector.String()})
}
Loading

0 comments on commit 3db45b2

Please sign in to comment.