Skip to content

Commit

Permalink
remove landscaper agent logic (#219)
Browse files Browse the repository at this point in the history
  • Loading branch information
achimweigel authored Mar 4, 2024
1 parent 150a724 commit 2cb573b
Show file tree
Hide file tree
Showing 23 changed files with 668 additions and 331 deletions.
2 changes: 1 addition & 1 deletion .ci/dev_integration_test
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fi

if ! command -v curl &> /dev/null
then
apk add --no-cache --no-progress curl openssl
apk add --no-cache --no-progress curl openssl apache2-utils
fi

if ! command -v python3 &> /dev/null
Expand Down
83 changes: 83 additions & 0 deletions cmd/quickstart/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package quickstart

import (
"context"
"fmt"
"time"

extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func removeObjects(ctx context.Context, k8sClient client.Client, crd *extv1.CustomResourceDefinition) error {
for k := range crd.Spec.Versions {
gvk := schema.GroupVersionKind{
Group: crd.Spec.Group,
Version: crd.Spec.Versions[k].Name,
Kind: crd.Spec.Names.Kind,
}

fmt.Printf("Removing objects of type %s\n", gvk)

objectList := &unstructured.UnstructuredList{}
objectList.SetGroupVersionKind(gvk)
if err := k8sClient.List(ctx, objectList); err != nil {
return err
}

for i := range objectList.Items {
item := &objectList.Items[i]
if err := removeObject(ctx, k8sClient, item); err != nil {
return err
}
}
}

return nil
}

func removeObject(ctx context.Context, k8sClient client.Client, object client.Object) error {
var err error

fmt.Printf("Removing object: %s\n", client.ObjectKeyFromObject(object).String())

for i := 0; i < 10; i++ {
if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(object), object); err != nil {
if apierrors.IsNotFound(err) {
return nil
}
fmt.Printf("Removing object: get failed: %s\n", err.Error())
continue
}

object.SetFinalizers(nil)
if err = k8sClient.Update(ctx, object); err != nil {
if apierrors.IsNotFound(err) {
return nil
}

fmt.Printf("Removing object: update failed: %s\n", err.Error())
continue
}

if err = k8sClient.Delete(ctx, object); err != nil {
if apierrors.IsNotFound(err) {
return nil
}

fmt.Printf("Removing object: delete failed: %s\n", err.Error())
continue
}

time.Sleep(time.Millisecond * 10)
}

if err != nil {
return err
} else {
return fmt.Errorf("object could not be removed %s", client.ObjectKeyFromObject(object).String())
}
}
185 changes: 159 additions & 26 deletions cmd/quickstart/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import (
"os"
"path"
"strings"
"time"

"sigs.k8s.io/yaml"
version2 "github.com/gardener/landscapercli/pkg/version"

"k8s.io/apimachinery/pkg/util/wait"

"github.com/gardener/landscapercli/pkg/version"
"sigs.k8s.io/yaml"

"github.com/go-logr/logr"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
corev1 "k8s.io/api/core/v1"
extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
Expand All @@ -29,6 +33,14 @@ import (

const (
defaultNamespace = "landscaper"

latestRelease = "latest release"

installExample = `
landscaper-cli quickstart install --kubeconfig ./kubconfig.yaml
landscaper-cli quickstart install --kubeconfig ./kubconfig.yaml --landscaper-values ./landscaper-values.yaml --namespace landscaper --install-oci-registry --install-registry-ingress --registry-username testuser --registry-password some-pw
`
)

type installOptions struct {
Expand Down Expand Up @@ -79,7 +91,7 @@ func NewInstallCommand(ctx context.Context) *cobra.Command {
Use: "install --kubeconfig [kubconfig.yaml] --landscaper-values [landscaper-values.yaml] --namespace landscaper --install-oci-registry --install-registry-ingress --registry-username testuser --registry-password some-pw",
Aliases: []string{"i"},
Short: "command to install Landscaper (including Container, Helm, and Manifest deployers) in a target cluster. An OCI registry for testing can be optionally installed",
Example: "landscaper-cli quickstart install --kubeconfig ./kubconfig.yaml --landscaper-values ./landscaper-values.yaml --namespace landscaper --install-oci-registry --install-registry-ingress --registry-username testuser --registry-password some-pw",
Example: installExample,
Run: func(cmd *cobra.Command, args []string) {
if err := opts.Complete(args); err != nil {
fmt.Println(err.Error())
Expand All @@ -100,20 +112,20 @@ func NewInstallCommand(ctx context.Context) *cobra.Command {

func (o *installOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&o.kubeconfigPath, "kubeconfig", "", "path to the kubeconfig of the target cluster")
fs.StringVar(&o.namespace, "namespace", defaultNamespace, "namespace where Landscaper and the OCI registry will get installed")
fs.StringVar(&o.landscaperValuesPath, "landscaper-values", "", "path to values.yaml for the Landscaper Helm installation")
fs.BoolVar(&o.instOCIRegistry, "install-oci-registry", false, "install an OCI registry in the target cluster")
fs.BoolVar(&o.instRegistryIngress, "install-registry-ingress", false, `install an ingress for accessing the OCI registry.
fs.StringVar(&o.namespace, "namespace", defaultNamespace, "namespace where Landscaper and the OCI registry will get installed (optional)")
fs.StringVar(&o.landscaperValuesPath, "landscaper-values", "", "path to values.yaml for the Landscaper Helm installation (optional)")
fs.BoolVar(&o.instOCIRegistry, "install-oci-registry", false, "install an OCI registry in the target cluster (optional)")
fs.BoolVar(&o.instRegistryIngress, "install-registry-ingress", false, `install an ingress for accessing the OCI registry (optional).
the credentials must be provided via the flags "--registry-username" and "--registry-password".
the Landscaper instance will then be automatically configured with these credentials.
prerequisites (!):
- the target cluster must be a Gardener Shoot (TLS is provided via the Gardener cert manager)
- a nginx ingress controller must be deployed in the target cluster
- the command "htpasswd" must be installed on your local machine`)
fs.StringVar(&o.landscaperChartVersion, "landscaper-chart-version", version.LandscaperChartVersion,
"use a custom Landscaper chart version (corresponds to Landscaper Github release with the same version number)")
fs.StringVar(&o.registryUsername, "registry-username", "", "username for authenticating at the OCI registry")
fs.StringVar(&o.registryPassword, "registry-password", "", "password for authenticating at the OCI registry")
fs.StringVar(&o.landscaperChartVersion, "landscaper-chart-version", latestRelease,
"use a custom Landscaper chart version (optional)")
fs.StringVar(&o.registryUsername, "registry-username", "", "username for authenticating at the OCI registry (optional)")
fs.StringVar(&o.registryPassword, "registry-password", "", "password for authenticating at the OCI registry (optional)")
}

func (o *installOptions) ReadLandscaperValues() error {
Expand Down Expand Up @@ -177,10 +189,34 @@ func (o *installOptions) run(ctx context.Context, log logr.Logger) error {
}
}

if err := o.installLandscaper(ctx); err != nil {
version := o.landscaperChartVersion
if version == latestRelease {
version, err = version2.GetRelease()
if err != nil {
return fmt.Errorf("cannot get latest Landscaper release: %w", err)
}
}

if err := o.installLandscaper(ctx, version); err != nil {
return fmt.Errorf("cannot install landscaper: %w", err)
}

if err := o.waitForCrds(ctx); err != nil {
return fmt.Errorf("waiting for crds failed: %w", err)
}

if err := o.installDeployer(ctx, "helm", version); err != nil {
return fmt.Errorf("cannot install helm deployer: %w", err)
}

if err := o.installDeployer(ctx, "manifest", version); err != nil {
return fmt.Errorf("cannot install manifest deployer: %w", err)
}

if err := o.installDeployer(ctx, "container", version); err != nil {
return fmt.Errorf("cannot install container deployer: %w", err)
}

if o.instOCIRegistry {
if o.instRegistryIngress {
fmt.Println("The OCI registry can be accessed via the URL https://" + o.registryIngressHost)
Expand Down Expand Up @@ -259,23 +295,17 @@ func (o *installOptions) Complete(args []string) error {
}

func (o *installOptions) generateLandscaperValuesOverride() ([]byte, error) {
defaultDeployers := ""
if len(o.landscaperValues.Landscaper.Landscaper.Deployers) == 0 {
defaultDeployers = `
deployers:
- container
- helm
- manifest`
}

landscaperValuesOverride := fmt.Sprintf(`
landscaper:
landscaper:%s
landscaper:
deployers: []
deployerManagement:
disable: true
namespace: %s
agent:
namespace: %s
`, defaultDeployers, o.namespace, o.namespace)
disable: true
`, o.namespace)

if o.instRegistryIngress {
// when installing the ingress, we must add the registry credentials to the Landscaper values file
Expand Down Expand Up @@ -309,7 +339,7 @@ landscaper:
return []byte(landscaperValuesOverride), nil
}

func (o *installOptions) installLandscaper(ctx context.Context) error {
func (o *installOptions) installLandscaper(ctx context.Context, version string) error {
fmt.Println("Installing Landscaper")

tempDir, err := os.MkdirTemp(".", "landscaper-chart-tmp-*")
Expand All @@ -318,11 +348,11 @@ func (o *installOptions) installLandscaper(ctx context.Context) error {
}
defer func() {
if err := os.RemoveAll(tempDir); err != nil {
fmt.Printf("cannot remove temporary directory %s: %s", tempDir, err.Error())
fmt.Printf("cannot remove temporary directory %s: %s\n", tempDir, err.Error())
}
}()

landscaperChartURI := fmt.Sprintf("oci://europe-docker.pkg.dev/sap-gcp-cp-k8s-stable-hub/landscaper/github.com/gardener/landscaper/charts/landscaper --untar --version %s", o.landscaperChartVersion)
landscaperChartURI := fmt.Sprintf("oci://europe-docker.pkg.dev/sap-gcp-cp-k8s-stable-hub/landscaper/github.com/gardener/landscaper/charts/landscaper --untar --version %s", version)
pullCmd := fmt.Sprintf("helm pull %s -d %s", landscaperChartURI, tempDir)
if err := util.ExecCommandBlocking(pullCmd); err != nil {
return err
Expand Down Expand Up @@ -371,6 +401,54 @@ func (o *installOptions) installLandscaper(ctx context.Context) error {
}

fmt.Printf("Landscaper installation succeeded!\n\n")

return nil
}

func (o *installOptions) installDeployer(ctx context.Context, deployer, version string) error {
fmt.Printf("Installing %s deployer\n", deployer)

tempDir, err := os.MkdirTemp(".", fmt.Sprintf("%s-deployer-chart-tmp-*", deployer))
if err != nil {
return err
}
defer func() {
if err := os.RemoveAll(tempDir); err != nil {
fmt.Printf("cannot remove temporary directory %s: %s\n", tempDir, err.Error())
}
}()

landscaperChartURI := fmt.Sprintf("oci://europe-docker.pkg.dev/sap-gcp-cp-k8s-stable-hub/landscaper/github.com/gardener/landscaper/%s-deployer/charts/%s-deployer --untar --version %s",
deployer, deployer, version)
pullCmd := fmt.Sprintf("helm pull %s -d %s", landscaperChartURI, tempDir)
if err := util.ExecCommandBlocking(pullCmd); err != nil {
return err
}

fileInfos, err := os.ReadDir(tempDir)
if err != nil {
return err
}

if len(fileInfos) != 1 {
return fmt.Errorf("found more than 1 item in temp directory for %s Chart export", deployer)
}
chartPath := path.Join(tempDir, fileInfos[0].Name())

installCommand := fmt.Sprintf(
"helm upgrade --install --namespace %s %s-deployer %s --kubeconfig %s",
o.namespace,
deployer,
chartPath,
o.kubeconfigPath,
)

if err := util.ExecCommandBlocking(installCommand); err != nil {
return err
}

fmt.Printf("%s installation succeeded!\n\n", deployer)

return nil
}

Expand All @@ -393,3 +471,58 @@ func (o *installOptions) installOCIRegistry(ctx context.Context, k8sClient clien
fmt.Print("OCI registry installation succeeded!\n\n")
return nil
}

func (o *installOptions) waitForCrds(ctx context.Context) error {
cfg, err := clientcmd.BuildConfigFromFlags("", o.kubeconfigPath)
if err != nil {
return fmt.Errorf("cannot parse K8s config: %w", err)
}

k8sClient, err := client.New(cfg, client.Options{
Scheme: scheme,
})

if err != nil {
return fmt.Errorf("cannot build K8s client: %w", err)
}

err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 1*time.Minute, true, func(ctx context.Context) (done bool, err error) {
fmt.Println("waiting for CRDs")

crdName := "contexts.landscaper.gardener.cloud"
if crdExists := o.crdExists(ctx, k8sClient, crdName); !crdExists {
return false, nil
}

crdName = "dataobjects.landscaper.gardener.cloud"
if crdExists := o.crdExists(ctx, k8sClient, crdName); !crdExists {
return false, nil
}

crdName = "deployitems.landscaper.gardener.cloud"
if crdExists := o.crdExists(ctx, k8sClient, crdName); !crdExists {
return false, nil
}

crdName = "targets.landscaper.gardener.cloud"
if crdExists := o.crdExists(ctx, k8sClient, crdName); !crdExists {
return false, nil
}

return true, nil
})

return err
}

func (o *installOptions) crdExists(ctx context.Context, k8sClient client.Client, crdName string) bool {
crd := &extv1.CustomResourceDefinition{}
crd.SetName(crdName)

if err := k8sClient.Get(ctx, client.ObjectKeyFromObject(crd), crd); err != nil {
fmt.Printf("Crd not found: %s\n\n", crdName)
return false
}

return true
}
20 changes: 1 addition & 19 deletions cmd/quickstart/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,23 +261,5 @@ func TestGenerateLandscaperValuesOverride(t *testing.T) {
assert.True(t, ok)
depList, ok := config.([]interface{})
assert.True(t, ok)
assert.EqualValues(t, []interface{}{"container", "helm", "manifest"}, depList)

// verify that deployers are not overwritten if specified
opts.landscaperValues.Landscaper.Landscaper.Deployers = []string{"mock"}
lsvo, err = opts.generateLandscaperValuesOverride()
assert.NoError(t, err)
data = map[string]interface{}{}
err = yaml.Unmarshal(lsvo, &data)
assert.NoError(t, err)
config, ok = data["landscaper"]
assert.True(t, ok)
data, ok = config.(map[string]interface{})
assert.True(t, ok)
config, ok = data["landscaper"]
assert.True(t, ok)
data, ok = config.(map[string]interface{})
assert.True(t, ok)
_, ok = data["deployers"]
assert.False(t, ok)
assert.EqualValues(t, []interface{}{}, depList)
}
Loading

0 comments on commit 2cb573b

Please sign in to comment.