Skip to content

Commit

Permalink
Enabling interoperability between cluster install and cli install (#514)
Browse files Browse the repository at this point in the history
* Enabling interoperability between cluster install and cli install

* Added kapp rule

* Since we can't pause by default, we use 10y as syncPeriod

* Adding used config to educates-config configmap

* Adding command to view config to be applied
  • Loading branch information
jorgemoralespou authored Aug 1, 2024
1 parent 583386c commit 652f0e3
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 36 deletions.
15 changes: 0 additions & 15 deletions carvel-packages/installer/README.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: kapp.k14s.io/v1alpha1
kind: Config
rebaseRules:
- paths:
- [spec, selector, matchLabels]
type: copy
sources: [existing, new]
resourceMatchers:
- apiVersionKindMatcher: { apiVersion: apps/v1, kind: DaemonSet }
- apiVersionKindMatcher: { apiVersion: apps/v1, kind: Deployment }
- paths:
- [spec, selector]
type: copy
sources: [existing, new]
resourceMatchers:
- apiVersionKindMatcher: { apiVersion: v1, kind: Service }
- apiVersionKindMatcher: { apiVersion: apps/v1, kind: Deployment }
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
load("@ytt:struct", "struct")

def removeNulls(data):
# Iterate over a struct of scalar values and return only those where value is not null
filtered_data = {}
for key in struct.decode(data):
value = getattr(data, key, None)
if type(value) == "struct":
value = removeNulls(value)
end
if value: #! This means that value is not an empty string, dict, struct, ...
filtered_data[key] = value
end
end
return struct.encode(filtered_data)
end

Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
#@ load("@ytt:overlay", "overlay")
#@ load("@ytt:data", "data")
#@ load("@ytt:yaml", "yaml")
#@ load("functions.star", "removeNulls")

#! We create educates namespace in case educates package is not enabled
#@ if/end not data.values.values.clusterPackages.educates.enabled:
---
apiVersion: v1
kind: Namespace
metadata:
name: educates-config
name: educates

---
apiVersion: v1
kind: ConfigMap
metadata:
name: educates-config
namespace: educates-config
namespace: educates
data:
values.yaml: #@ yaml.encode(data.values)
config.yaml: #@ yaml.encode(removeNulls(data.values.config))
values.yaml: #@ yaml.encode(data.values.values)
9 changes: 7 additions & 2 deletions carvel-packages/installer/bundle/config/ytt/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
--- #@ template.replace(overlay.apply(library.get(packagePath).with_data_values(packageValues).eval(), addKappAnnotations(name, overlayedValues, orderedPackagesList)))
#@ end
#@ end
#@ if/end overlayedValues.clusterPackages["educates"].enabled:
--- #@ template.replace(overlay.apply(library.get("config").with_data_values(overlayedValues).eval(), addKappAnnotations("educates", overlayedValues, orderedPackagesList)))

#@ allInfo = struct.make(config=data.values, values=overlayedValues)
#@ if overlayedValues.clusterPackages["educates"].enabled:
--- #@ template.replace(overlay.apply(library.get("config").with_data_values(allInfo).eval(), addKappAnnotations("educates", overlayedValues, orderedPackagesList)))
#@ else:
--- #@ template.replace(overlay.apply(library.get("config").with_data_values(allInfo).eval()))
#@ end
#@ end
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ clusterInfrastructure:
#@schema/validation min_len=1
name: ""
#!--------- educates installation schema
#@schema/nullable
localKindCluster:
listenAddress: ""
apiServer:
Expand All @@ -125,6 +126,7 @@ localKindCluster:
- hostPath: ""
containerPath: ""
readOnly: false
#@schema/nullable
localDNSResolver:
targetAddress: ""
extraDomains:
Expand Down
21 changes: 20 additions & 1 deletion carvel-packages/installer/config/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,32 @@
#@ return "{}/educates-installer:{}".format(registry, data.values.version)
#@ end

#! This configmap provides interoperability between the kapp-controller installation and
#! the educates CLI installation, by preconfiguring the label kapp-controller's App will use to
#! be the same as the one used by the educates CLI.
#! The name of the configmap will be the same as the App, but with `.app` appended.
#! The `spec` needs `labelKey` and `labelValue` fields to be set.
---
apiVersion: v1
kind: ConfigMap
metadata:
labels:
kapp.k14s.io/is-app: ""
annotations:
kapp.k14s.io/app-changes-use-app-label: ""
name: installer.educates.dev.app
namespace: educates-installer
data:
spec: '{"labelKey":"installer","labelValue":"educates-installer.app"}'
---
apiVersion: kappctrl.k14s.io/v1alpha1
kind: App
metadata:
name: installer.educates.dev
namespace: educates-installer
spec:
serviceAccountName: educates-installer
syncPeriod: 87600h #! 10 years
fetch:
- imgpkgBundle:
image: #@ bundle_reference()
Expand Down Expand Up @@ -48,5 +67,5 @@ spec:
deploy:
- kapp:
rawOptions:
- "--app-changes-max-to-keep=5"
- "--app-changes-max-to-keep=0"
- "--diff-changes=true"
11 changes: 0 additions & 11 deletions carvel-packages/repository.yaml

This file was deleted.

1 change: 1 addition & 0 deletions client-programs/pkg/cmd/admin_platform_cmd_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func (p *ProjectInfo) NewAdminPlatformCmdGroup() *cobra.Command {
Commands: []*cobra.Command{
p.NewAdminPlatformDeployCmd(),
p.NewAdminPlatformDeleteCmd(),
p.NewAdminPlatformConfigCmd(),
p.NewAdminPlatformValuesCmd(),
},
},
Expand Down
133 changes: 133 additions & 0 deletions client-programs/pkg/cmd/admin_platform_config_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"

"github.com/vmware-tanzu-labs/educates-training-platform/client-programs/pkg/config"
"github.com/vmware-tanzu-labs/educates-training-platform/client-programs/pkg/installer"
)

var (
adminPlatformConfigExample = `
# Show configuration config for local deployment
educates admin platform config --local-config
# Show configuration config for specific config file
educates admin platform config --config config.yaml
# Get configuration used to deploy to the current cluster
educates admin platform config --from-cluster
educates admin platform config --from-cluster --kubeconfig /path/to/kubeconfig --context my-cluster
# Get configuration config using locally built educates package (version latest does the same and skips image resolution)
educates admin platform config --config config.yaml --package-repository localhost:5001 --version 0.0.1
educates admin platform config --config config.yaml --version latest
# Get configuration config with different domain (to make copies of the config)
educates admin platform config --local-config --domain cluster1.dev.educates.io > cluster1-config.yaml
educates admin platform config --config config.yaml --domain cluster2.dev.educates.io > cluster2-config.yaml
`
)

type PkatformConfigOptions struct {
KubeconfigOptions
Domain string
Version string
PackageRepository string
LocalConfig bool
FromCluster bool
Verbose bool
}

func (o *PkatformConfigOptions) Run() error {
installer := installer.NewInstaller()

if o.FromCluster {
config, err := installer.GetConfigFromCluster(o.Kubeconfig, o.Context)
if err != nil {
return err
}
fmt.Println(config)
} else {
fullConfig, err := config.ConfigForLocalClusters("", o.Domain, o.LocalConfig)

if err != nil {
return err
}

config.PrintConfigToStdout(fullConfig)
}

return nil
}

func (p *ProjectInfo) NewAdminPlatformConfigCmd() *cobra.Command {
var o PkatformConfigOptions

var c = &cobra.Command{
Args: cobra.NoArgs,
Use: "config",
Short: "Show config used when deploying the platform",
RunE: func(cmd *cobra.Command, _ []string) error {
return o.Run()
},
Example: adminPlatformConfigExample,
}

c.Flags().StringVar(
&o.Kubeconfig,
"kubeconfig",
"",
"kubeconfig file to use instead of $KUBECONFIG or $HOME/.kube/config",
)
c.Flags().StringVar(
&o.Context,
"context",
"",
"Context to use from Kubeconfig",
)
c.Flags().StringVar(
&o.Domain,
"domain",
"",
"wildcard ingress subdomain name for Educates",
)
c.Flags().BoolVar(
&o.Verbose,
"verbose",
false,
"print verbose output",
)
c.Flags().StringVar(
&o.PackageRepository,
"package-repository",
p.ImageRepository,
"image repository hosting package bundles",
)
c.Flags().StringVar(
&o.Version,
"version",
p.Version,
"version to be installed",
)
c.Flags().BoolVar(
&o.LocalConfig,
"local-config",
false,
"Use local configuration. When used, --config and --domain flags are ignored",
)
// TODO: From cluster
c.Flags().BoolVar(
&o.FromCluster,
"from-cluster",
false,
"Show the configuration (from the cluster) used when the plaform was deployed",
)

c.MarkFlagsMutuallyExclusive("local-config", "from-cluster")
c.MarkFlagsOneRequired("local-config", "from-cluster")

return c
}
2 changes: 1 addition & 1 deletion client-programs/pkg/cmd/admin_platform_values_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (o *PlatformValuesOptions) Run() error {
installer := installer.NewInstaller()

if o.FromCluster {
config, err := installer.GetConfigFromCluster(o.Kubeconfig, o.Context)
config, err := installer.GetValuesFromCluster(o.Kubeconfig, o.Context)
if err != nil {
return err
}
Expand Down
34 changes: 31 additions & 3 deletions client-programs/pkg/installer/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (

const EducatesInstallerString = "educates-installer"
const EducatesInstallerAppString = "label:installer=educates-installer.app"
const educatesConfigNamespace = "educates"
const educatesConfigConfigMapName = "educates-config"

// We use a NullWriter to suppress the output of some commands, like kbld
type NullWriter int
Expand Down Expand Up @@ -166,7 +168,7 @@ func (inst *Installer) Delete(fullConfig *config.InstallationConfig, clusterConf
return nil
}

func (inst *Installer) GetConfigFromCluster(kubeconfig string, kubeContext string) (string, error) {
func (inst *Installer) GetValuesFromCluster(kubeconfig string, kubeContext string) (string, error) {
clusterConfig := cluster.NewClusterConfig(kubeconfig, kubeContext)

client, err := clusterConfig.GetClient()
Expand All @@ -175,9 +177,9 @@ func (inst *Installer) GetConfigFromCluster(kubeconfig string, kubeContext strin
return "", errors.Wrapf(err, "unable to create Kubernetes client")
}

configMapClient := client.CoreV1().ConfigMaps("educates-config")
configMapClient := client.CoreV1().ConfigMaps(educatesConfigNamespace)

values, err := configMapClient.Get(context.TODO(), "educates-config", metav1.GetOptions{})
values, err := configMapClient.Get(context.TODO(), educatesConfigConfigMapName, metav1.GetOptions{})

if err != nil {
return "", errors.Wrap(err, "error querying the cluster")
Expand All @@ -192,6 +194,32 @@ func (inst *Installer) GetConfigFromCluster(kubeconfig string, kubeContext strin
return string(valuesData), nil
}

func (inst *Installer) GetConfigFromCluster(kubeconfig string, kubeContext string) (string, error) {
clusterConfig := cluster.NewClusterConfig(kubeconfig, kubeContext)

client, err := clusterConfig.GetClient()

if err != nil {
return "", errors.Wrapf(err, "unable to create Kubernetes client")
}

configMapClient := client.CoreV1().ConfigMaps(educatesConfigNamespace)

values, err := configMapClient.Get(context.TODO(), educatesConfigConfigMapName, metav1.GetOptions{})

if err != nil {
return "", errors.Wrap(err, "error querying the cluster")
}

valuesData, ok := values.Data["config.yaml"]

if !ok {
return "", errors.New("no platform configuration found")
}

return string(valuesData), nil
}

func (inst *Installer) fetch(tempDir string, version string, packageRepository string, verbose bool) (string, error) {
if verbose {
fmt.Println("Running fetch ...")
Expand Down

0 comments on commit 652f0e3

Please sign in to comment.