Skip to content

Commit

Permalink
Support templating Openstack clusters as App CRs (#635)
Browse files Browse the repository at this point in the history
* Playing with Cluster App templating

* Templating App clusters

* Exporting template app

* Exporting rest of the flags for consistency

* Reorganizing to not export runners from cmd/template/app

* Updating after review

* Fixing imports

* change ordering of flags

* Hidding flags

* Adding entry to Changlog

* Removing suffix

* Changing flag to the cluster-topology
  • Loading branch information
ljakimczuk authored Jan 20, 2022
1 parent d8416d2 commit b37a5db
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 41 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project's packages adheres to [Semantic Versioning](http://semver.org/s

## [Unreleased]

### Added

- Add support to `template cluster --provider openstack` for templating clusters as App CRs.

## [1.58.2] - 2022-01-13

### Added
Expand Down
33 changes: 7 additions & 26 deletions cmd/template/app/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

"github.com/giantswarm/microerror"
"github.com/giantswarm/micrologger"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"

Expand All @@ -21,8 +20,8 @@ import (
type runner struct {
flag *flag
logger micrologger.Logger
stdout io.Writer
stderr io.Writer
stdout io.Writer
}

func (r *runner) Run(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -77,13 +76,8 @@ func (r *runner) run(ctx context.Context, cmd *cobra.Command, args []string) err
}

if r.flag.flagUserSecret != "" {
userConfigSecretData, err := key.ReadSecretYamlFromFile(afero.NewOsFs(), r.flag.flagUserSecret)
if err != nil {
return microerror.Mask(err)
}

secretConfig := templateapp.SecretConfig{
Data: userConfigSecretData,
secretConfig := templateapp.UserConfig{
Path: r.flag.flagUserSecret,
Name: assetName,
Namespace: namespace,
}
Expand All @@ -100,19 +94,12 @@ func (r *runner) run(ctx context.Context, cmd *cobra.Command, args []string) err
}

if r.flag.flagUserConfigMap != "" {
var configMapData string
if r.flag.flagUserConfigMap != "" {
configMapData, err = key.ReadConfigMapYamlFromFile(afero.NewOsFs(), r.flag.flagUserConfigMap)
if err != nil {
return microerror.Mask(err)
}
}

configMapConfig := templateapp.ConfigMapConfig{
Data: configMapData,
configMapConfig := templateapp.UserConfig{
Path: r.flag.flagUserConfigMap,
Name: assetName,
Namespace: namespace,
}

userConfigMap, err := templateapp.NewConfigMap(configMapConfig)
if err != nil {
return microerror.Mask(err)
Expand Down Expand Up @@ -142,13 +129,7 @@ func (r *runner) run(ctx context.Context, cmd *cobra.Command, args []string) err
return microerror.Mask(err)
}

type AppCROutput struct {
AppCR string
UserConfigSecret string
UserConfigConfigMap string
}

appCROutput := AppCROutput{
appCROutput := templateapp.AppCROutput{
AppCR: string(appCRYaml),
UserConfigConfigMap: string(userConfigConfigMapYaml),
UserConfigSecret: string(userConfigSecretYaml),
Expand Down
35 changes: 32 additions & 3 deletions cmd/template/cluster/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ const (
flagAWSEKS = "aws-eks"
flagAWSControlPlaneSubnet = "control-plane-subnet"

// Cluster App only.
flagClusterAppVersion = "cluster-app-version"
flagClusterUserConfigMap = "cluster-user-configmap"
flagClusterTopology = "cluster-topology"
flagDefaultAppsAppVersion = "default-apps-app-version"
flagDefaultAppsUserConfigMap = "default-apps-user-configmap"

// OpenStack only.
flagOpenStackCloud = "cloud"
flagOpenStackCloudConfig = "cloud-config"
Expand Down Expand Up @@ -68,11 +75,20 @@ type openStackFlag struct {
NodeCIDR string // <no equivalent env var>
}

type clusterAppFlag struct {
ClusterUserConfigMap string
ClusterAppVersion string
ClusterTopology bool
DefaultAppsUserConfigMap string
DefaultAppsAppVersion string
}

type flag struct {
Provider string

AWS awsFlag
OpenStack openStackFlag
AWS awsFlag
OpenStack openStackFlag
ClusterApp clusterAppFlag

// Common.
ClusterIDDeprecated string
Expand Down Expand Up @@ -112,6 +128,13 @@ func (f *flag) Init(cmd *cobra.Command) {
cmd.Flags().StringVar(&f.OpenStack.RootVolumeSourceUUID, flagOpenStackRootVolumeSourceUUID, "", "Root volume source UUID (OpenStack only).")
cmd.Flags().StringVar(&f.OpenStack.NodeCIDR, flagOpenStackNodeCIDR, "", "CIDR used for the nodes.")

// OpenStack App only.
cmd.Flags().StringVar(&f.ClusterApp.ClusterUserConfigMap, flagClusterUserConfigMap, "", "Path to the user values configmap YAML file for Cluster App (OpenStack App CR only).")
cmd.Flags().StringVar(&f.ClusterApp.ClusterAppVersion, flagClusterAppVersion, "0.1.0", "Cluster App version to be installed. (OpenStack App CR only).")
cmd.Flags().StringVar(&f.ClusterApp.DefaultAppsUserConfigMap, flagDefaultAppsUserConfigMap, "", "Path to the user values configmap YAML file for Default Apps App (OpenStack App CR only).")
cmd.Flags().StringVar(&f.ClusterApp.DefaultAppsAppVersion, flagDefaultAppsAppVersion, "0.1.0", "Default Apps App version to be installed. (OpenStack App CR only).")
cmd.Flags().BoolVar(&f.ClusterApp.ClusterTopology, flagClusterTopology, false, "Templated cluster as an App CR. (OpenStack App CR only).")

// TODO: Make these flags visible once we have a better method for displaying provider-specific flags.
_ = cmd.Flags().MarkHidden(flagOpenStackCloud)
_ = cmd.Flags().MarkHidden(flagOpenStackCloudConfig)
Expand All @@ -125,6 +148,12 @@ func (f *flag) Init(cmd *cobra.Command) {
_ = cmd.Flags().MarkHidden(flagOpenStackRootVolumeSourceUUID)
_ = cmd.Flags().MarkHidden(flagOpenStackNodeCIDR)

_ = cmd.Flags().MarkHidden(flagClusterTopology)
_ = cmd.Flags().MarkHidden(flagClusterAppVersion)
_ = cmd.Flags().MarkHidden(flagDefaultAppsAppVersion)
_ = cmd.Flags().MarkHidden(flagClusterUserConfigMap)
_ = cmd.Flags().MarkHidden(flagDefaultAppsUserConfigMap)

// Common.
cmd.Flags().StringVar(&f.ClusterIDDeprecated, flagClusterIDDeprecated, "", "Unique identifier of the cluster (deprecated).")
cmd.Flags().StringSliceVar(&f.ControlPlaneAZ, flagControlPlaneAZ, nil, "Availability zone(s) to use by control plane nodes.")
Expand Down Expand Up @@ -265,7 +294,7 @@ func (f *flag) Validate() error {
}
}

if f.Release == "" {
if !f.ClusterApp.ClusterTopology && f.Release == "" {
return microerror.Maskf(invalidFlagError, "--%s must not be empty", flagRelease)
}

Expand Down
102 changes: 101 additions & 1 deletion cmd/template/cluster/provider/capo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ package provider

import (
"context"
"fmt"
"io"
"os"
"text/template"

"github.com/giantswarm/k8sclient/v5/pkg/k8sclient"
"github.com/giantswarm/microerror"
"sigs.k8s.io/yaml"

"github.com/giantswarm/kubectl-gs/cmd/template/cluster/provider/templates/openstack"
"github.com/giantswarm/kubectl-gs/internal/key"
templateapp "github.com/giantswarm/kubectl-gs/pkg/template/app"
)

func WriteOpenStackTemplate(ctx context.Context, client k8sclient.Interface, out io.Writer, config ClusterCRsConfig) error {
func WriteOpenStackTemplateRaw(ctx context.Context, client k8sclient.Interface, out io.Writer, config ClusterCRsConfig) error {
data := struct {
Description string
KubernetesVersion string
Expand Down Expand Up @@ -65,3 +71,97 @@ func WriteOpenStackTemplate(ctx context.Context, client k8sclient.Interface, out

return nil
}

func WriteOpenStackTemplateAppCR(ctx context.Context, config ClusterCRsConfig) error {
var userConfigConfigMapYaml []byte
var err error

clusterAppOutput := templateapp.AppCROutput{}
defaultAppsAppOutput := templateapp.AppCROutput{}

clusterAppConfig := templateapp.Config{
AppName: config.Name,
Catalog: "control-plane-catalog",
InCluster: true,
Name: "cluster-openstack",
Namespace: fmt.Sprintf("org-%s", config.Organization),
Version: config.ClusterAppVersion,
}

defaultAppsAppConfig := templateapp.Config{
AppName: fmt.Sprintf("%s-default-apps", config.Name),
Catalog: "default",
InCluster: true,
Name: "default-apps-openstack",
Namespace: fmt.Sprintf("org-%s", config.Organization),
Version: config.DefaultAppsAppVersion,
}

userConfig := templateapp.UserConfig{
Namespace: fmt.Sprintf("org-%s", config.Organization),
}

if config.ClusterAppUserConfigMap != "" {
userConfig.Name = fmt.Sprintf("%s-userconfig", clusterAppConfig.AppName)
userConfig.Path = config.ClusterAppUserConfigMap

userConfigMap, err := templateapp.NewConfigMap(userConfig)
if err != nil {
return microerror.Mask(err)
}
clusterAppConfig.UserConfigConfigMapName = userConfigMap.GetName()

userConfigConfigMapYaml, err = yaml.Marshal(userConfigMap)
if err != nil {
return microerror.Mask(err)
}

clusterAppOutput.UserConfigConfigMap = string(userConfigConfigMapYaml)
}

if config.DefaultAppsAppUserConfigMap != "" {
userConfig.Name = fmt.Sprintf("%s-userconfig", defaultAppsAppConfig.AppName)
userConfig.Path = config.DefaultAppsAppUserConfigMap

userConfigMap, err := templateapp.NewConfigMap(userConfig)
if err != nil {
return microerror.Mask(err)
}
defaultAppsAppConfig.UserConfigConfigMapName = userConfigMap.GetName()

userConfigConfigMapYaml, err = yaml.Marshal(userConfigMap)
if err != nil {
return microerror.Mask(err)
}

defaultAppsAppOutput.UserConfigConfigMap = string(userConfigConfigMapYaml)
}

clusterAppCRYaml, err := templateapp.NewAppCR(clusterAppConfig)
if err != nil {
return microerror.Mask(err)
}

clusterAppOutput.AppCR = string(clusterAppCRYaml)

defaultAppsAppCRYaml, err := templateapp.NewAppCR(defaultAppsAppConfig)
if err != nil {
return microerror.Mask(err)
}

defaultAppsAppOutput.AppCR = string(defaultAppsAppCRYaml)

t := template.Must(template.New("appCR").Parse(key.AppCRTemplate))

err = t.Execute(os.Stdout, clusterAppOutput)
if err != nil {
return microerror.Mask(err)
}

err = t.Execute(os.Stdout, defaultAppsAppOutput)
if err != nil {
return microerror.Mask(err)
}

return nil
}
7 changes: 7 additions & 0 deletions cmd/template/cluster/provider/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ type ClusterCRsConfig struct {
Labels map[string]string
Namespace string
PodsCIDR string

// App settings
ClusterAppUserConfigMap string
ClusterAppVersion string
DefaultAppsAppUserConfigMap string
DefaultAppsAppVersion string
}

type templateConfig struct {
Expand Down Expand Up @@ -114,6 +120,7 @@ func runMutation(ctx context.Context, client k8sclient.Interface, templateData i
if err != nil {
return microerror.Mask(err)
}

// Transform to unstructured.Unstructured.
obj := &unstructured.Unstructured{}
dec := apiyaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
Expand Down
12 changes: 11 additions & 1 deletion cmd/template/cluster/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ func (r *runner) run(ctx context.Context, cmd *cobra.Command, args []string) err
RootVolumeDiskSize: r.flag.OpenStack.RootVolumeDiskSize,
RootVolumeSourceType: r.flag.OpenStack.RootVolumeSourceType,
RootVolumeSourceUUID: r.flag.OpenStack.RootVolumeSourceUUID,

ClusterAppVersion: r.flag.ClusterApp.ClusterAppVersion,
ClusterAppUserConfigMap: r.flag.ClusterApp.ClusterUserConfigMap,
DefaultAppsAppVersion: r.flag.ClusterApp.DefaultAppsAppVersion,
DefaultAppsAppUserConfigMap: r.flag.ClusterApp.DefaultAppsUserConfigMap,
}

if len(r.flag.MasterAZ) > 0 {
Expand Down Expand Up @@ -134,7 +139,12 @@ func (r *runner) run(ctx context.Context, cmd *cobra.Command, args []string) err
return microerror.Mask(err)
}
case key.ProviderOpenStack:
err = provider.WriteOpenStackTemplate(ctx, c.K8sClient, output, config)
if r.flag.ClusterApp.ClusterTopology {
err = provider.WriteOpenStackTemplateAppCR(ctx, config)
} else {
err = provider.WriteOpenStackTemplateRaw(ctx, c.K8sClient, output, config)
}

if err != nil {
return microerror.Mask(err)
}
Expand Down
Loading

0 comments on commit b37a5db

Please sign in to comment.