Skip to content

Commit

Permalink
Apply review suggestions
Browse files Browse the repository at this point in the history
* Easier option handling
* Improved logging

Signed-off-by: Jan Schlicht <jan@d2iq.com>
  • Loading branch information
Jan Schlicht committed Jun 2, 2020
1 parent de69d7f commit 5ac19d9
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 162 deletions.
16 changes: 6 additions & 10 deletions pkg/kudoctl/cmd/install/install.go
Expand Up @@ -79,18 +79,14 @@ func installOperator(operatorArgument string, options *Options, fs afero.Fs, set
return fmt.Errorf("failed to resolve operator package for: %s %w", operatorArgument, err)
}

installOpts := []install.Option{}

if options.SkipInstance {
installOpts = append(installOpts, install.SkipInstance())
}

if options.CreateNameSpace {
installOpts = append(installOpts, install.CreateNamespace())
installOpts := install.Options{
SkipInstance: options.SkipInstance,
CreateNamespace: options.CreateNameSpace,
}

if options.Wait {
installOpts = append(installOpts, install.WaitForInstance(time.Duration(options.WaitTime)*time.Second))
waitDuration := time.Duration(options.WaitTime) * time.Second
installOpts.Wait = &waitDuration
}

return install.Package(
Expand All @@ -99,5 +95,5 @@ func installOperator(operatorArgument string, options *Options, fs afero.Fs, set
settings.Namespace,
*pkg.Resources,
options.Parameters,
installOpts...)
installOpts)
}
141 changes: 0 additions & 141 deletions pkg/kudoctl/packages/install/install.go
@@ -1,144 +1,3 @@
// Package install provides function to install package resources
// on a Kubernetes cluster.
package install

import (
"strings"
"time"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1"
"github.com/kudobuilder/kudo/pkg/kudoctl/clog"
"github.com/kudobuilder/kudo/pkg/kudoctl/packages"
"github.com/kudobuilder/kudo/pkg/kudoctl/util/kudo"
)

type Options struct {
skipInstance bool
wait *time.Duration
createNamespace bool
}

type Option func(*Options)

// SkipInstance installs only Operator and OperatorVersion
// of an operator package.
func SkipInstance() Option {
return func(o *Options) {
o.skipInstance = true
}
}

// WaitForInstance waits an amount of time for the instance
// to complete installation.
func WaitForInstance(duration time.Duration) Option {
return func(o *Options) {
o.wait = &duration
}
}

// CreateNamespace creates the specified namespace before installation.
// If available, a namespace manifest in the operator package is
// rendered using the installation parameters.
func CreateNamespace() Option {
return func(o *Options) {
o.createNamespace = true
}
}

// Package installs an operator package with parameters into a namespace.
// Instance name, namespace and operator parameters are applied to the
// operator package resources. These rendered resources are then created
// on the Kubernetes cluster.
func Package(
client *kudo.Client,
instanceName string,
namespace string,
resources packages.Resources,
parameters map[string]string,
opts ...Option) error {
clog.V(3).Printf("operator name: %v", resources.Operator.Name)
clog.V(3).Printf("operator version: %v", resources.OperatorVersion.Spec.Version)

options := Options{}
for _, o := range opts {
o(&options)
}

applyOverrides(&resources, instanceName, namespace, parameters)

if err := client.ValidateServerForOperator(resources.Operator); err != nil {
return err
}

if options.createNamespace {
if err := installNamespace(client, resources, parameters); err != nil {
return err
}
}

if err := installOperatorAndOperatorVersion(client, resources); err != nil {
return err
}

if options.skipInstance {
return nil
}

if err := validateParameters(
*resources.Instance,
resources.OperatorVersion.Spec.Parameters); err != nil {
return err
}

if err := installInstance(client, resources.Instance); err != nil {
return err
}

if options.wait != nil {
if err := waitForInstance(client, resources.Instance, *options.wait); err != nil {
return err
}
}

return nil
}

func applyOverrides(
resources *packages.Resources,
instanceName string,
namespace string,
parameters map[string]string) {
resources.Operator.SetNamespace(namespace)
resources.OperatorVersion.SetNamespace(namespace)
resources.Instance.SetNamespace(namespace)

if instanceName != "" {
resources.Instance.SetName(instanceName)
clog.V(3).Printf("instance name: %v", instanceName)
}
if parameters != nil {
resources.Instance.Spec.Parameters = parameters
clog.V(3).Printf("parameters in use: %v", parameters)
}
}

func validateParameters(instance v1beta1.Instance, parameters []v1beta1.Parameter) error {
missingParameters := []string{}

for _, p := range parameters {
if *p.Required && p.Default == nil {
_, ok := instance.Spec.Parameters[p.Name]
if !ok {
missingParameters = append(missingParameters, p.Name)
}
}
}

if len(missingParameters) > 0 {
return clog.Errorf(
"missing required parameters during installation: %s",
strings.Join(missingParameters, ","))
}

return nil
}
41 changes: 34 additions & 7 deletions pkg/kudoctl/packages/install/operator.go
Expand Up @@ -13,23 +13,50 @@ import (
func installOperatorAndOperatorVersion(client *kudo.Client, resources packages.Resources) error {
if !client.OperatorExistsInCluster(resources.Operator.Name, resources.Operator.Namespace) {
if _, err := client.InstallOperatorObjToCluster(resources.Operator, resources.Operator.Namespace); err != nil {
return fmt.Errorf("failed to install %s-operator.yaml: %v", resources.Operator.Name, err)
return fmt.Errorf(
"failed to install %s-operator.yaml in namespace %s: %v",
resources.Operator.Name,
resources.Operator.Namespace,
err)
}
clog.Printf("operator.%s/%s created", resources.Operator.APIVersion, resources.Operator.Name)
clog.Printf(
"operator.%s/%s created in namespace %s",
resources.Operator.APIVersion,
resources.Operator.Name,
resources.Operator.Namespace)
}

versionsInstalled, err := client.OperatorVersionsInstalled(resources.Operator.Name, resources.Operator.Namespace)
if err != nil {
return fmt.Errorf("failed to retrieve existing operator versions: %v", err)
return fmt.Errorf(
"failed to retrieve existing operator versions of operator.%s/%s in namespace %s: %v",
resources.Operator.APIVersion,
resources.Operator.Name,
resources.Operator.Namespace,
err)
}

if !funk.ContainsString(versionsInstalled, resources.OperatorVersion.Spec.Version) {
if _, err := client.InstallOperatorVersionObjToCluster(resources.OperatorVersion, resources.OperatorVersion.Namespace); err != nil {
return fmt.Errorf("failed to install %s-operatorversion.yaml: %v", resources.Operator.Name, err)
if _, err := client.InstallOperatorVersionObjToCluster(
resources.OperatorVersion,
resources.OperatorVersion.Namespace); err != nil {
return fmt.Errorf(
"failed to install %s-operatorversion.yaml in namespace %s: %v",
resources.OperatorVersion.Name,
resources.OperatorVersion.Namespace,
err)
}
clog.Printf("operatorversion.%s/%s created", resources.OperatorVersion.APIVersion, resources.OperatorVersion.Name)
clog.Printf(
"operatorversion.%s/%s created in namespace %s",
resources.OperatorVersion.APIVersion,
resources.OperatorVersion.Name,
resources.OperatorVersion.Namespace)
} else {
clog.Printf("operatorversion.%s/%s already installed", resources.OperatorVersion.APIVersion, resources.OperatorVersion.Name)
clog.Printf(
"operatorversion.%s/%s already installed in namespace %s",
resources.OperatorVersion.APIVersion,
resources.OperatorVersion.Name,
resources.OperatorVersion.Namespace)
}

return nil
Expand Down
122 changes: 122 additions & 0 deletions pkg/kudoctl/packages/install/package.go
@@ -0,0 +1,122 @@
package install

import (
"strings"
"time"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1"
"github.com/kudobuilder/kudo/pkg/kudoctl/clog"
"github.com/kudobuilder/kudo/pkg/kudoctl/packages"
"github.com/kudobuilder/kudo/pkg/kudoctl/util/kudo"
)

type Options struct {
// Skip instance resource creation.
SkipInstance bool
// Wait until the instance has been created.
Wait *time.Duration
// Create the namespace for the operator package.
CreateNamespace bool
}

// Package installs an operator package with parameters into a namespace.
// Instance name, namespace and operator parameters are applied to the
// operator package resources. These rendered resources are then created
// on the Kubernetes cluster.
func Package(
client *kudo.Client,
instanceName string,
namespace string,
resources packages.Resources,
parameters map[string]string,
options Options) error {
clog.V(3).Printf(
"Preparing %s/%s:%s for installation",
namespace,
resources.Operator.Name,
resources.OperatorVersion.Spec.Version)

applyOverrides(&resources, instanceName, namespace, parameters)

if err := client.ValidateServerForOperator(resources.Operator); err != nil {
return err
}

if options.CreateNamespace {
if err := installNamespace(client, resources, parameters); err != nil {
return err
}
}

if err := installOperatorAndOperatorVersion(client, resources); err != nil {
return err
}

if options.SkipInstance {
return nil
}

if err := validateParameters(
*resources.Instance,
resources.OperatorVersion.Spec.Parameters); err != nil {
return err
}

if err := installInstance(client, resources.Instance); err != nil {
return err
}

if options.Wait != nil {
if err := waitForInstance(client, resources.Instance, *options.Wait); err != nil {
return err
}
}

return nil
}

func applyOverrides(
resources *packages.Resources,
instanceName string,
namespace string,
parameters map[string]string) {
resources.Operator.SetNamespace(namespace)
resources.OperatorVersion.SetNamespace(namespace)
resources.Instance.SetNamespace(namespace)

if instanceName != "" {
clog.V(3).Printf(
"Overriding instance name %s/%s to %s/%s",
namespace,
resources.Instance.Name,
namespace,
instanceName)
resources.Instance.SetName(instanceName)
}

if parameters != nil {
clog.V(3).Printf("parameters in use: %v", parameters)
resources.Instance.Spec.Parameters = parameters
}
}

func validateParameters(instance v1beta1.Instance, parameters []v1beta1.Parameter) error {
missingParameters := []string{}

for _, p := range parameters {
if *p.Required && p.Default == nil {
_, ok := instance.Spec.Parameters[p.Name]
if !ok {
missingParameters = append(missingParameters, p.Name)
}
}
}

if len(missingParameters) > 0 {
return clog.Errorf(
"missing required parameters during installation: %s",
strings.Join(missingParameters, ","))
}

return nil
}
Expand Up @@ -97,12 +97,11 @@ func Test_InstallPackage(t *testing.T) {

const namespace = "default"

options := []Option{}
if tt.skipInstance {
options = append(options, SkipInstance())
options := Options{
SkipInstance: tt.skipInstance,
}

err := Package(kc, "", namespace, testResources, tt.installParameters, options...)
err := Package(kc, "", namespace, testResources, tt.installParameters, options)
if tt.err != "" {
assert.EqualError(t, err, tt.err)
}
Expand Down

0 comments on commit 5ac19d9

Please sign in to comment.