Skip to content

Commit

Permalink
GIT-83: enable helm support for test workflow
Browse files Browse the repository at this point in the history
GIT-83: relocate the package and enable example

GIT-83: enable helper to setup helm binary for test

GIT-83: restructure test data

GIT-83: move helm install hack into PHONY

GIT-83: add example of using upstream helm repo

GIT-83: cleanup helm charts and support selective helm ops
  • Loading branch information
harshanarayana committed Jan 15, 2022
1 parent 7c2fa1b commit 0c8ca84
Show file tree
Hide file tree
Showing 13 changed files with 601 additions and 2 deletions.
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,12 @@ update-deps-go: ## Update all golang dependencies for this repo

##@ Tests

.PHONY: test
test: ## Runs golang unit tests
.PHONY: install-helm test

install-helm: ## Install Helm toolchain for 3rd party integration
./hack/install-helm.sh

test: install-helm ## Runs golang unit tests
./hack/test-go.sh

##@ Helpers
Expand Down
6 changes: 6 additions & 0 deletions examples/third_party_integration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Third Party Integration

This section of the repository contains the example of how the third party tooling are integrated into the
`e2e-framework`

1. [Helm](./helm)
33 changes: 33 additions & 0 deletions examples/third_party_integration/helm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Helm Integration

This section of the document gives you an example of how to integrate the helm chart related workflow
into the `e2e-framework` while writing your tests.

## Pre-Requisites

1. `Helm3` Installed on your system where the tests are being run

## How does `TestLocalHelmChartWorkflow` test work ?

1. It creates a Kind Cluster with `third-party` prefix
2. Creates a new namespace with `third-party` prefix
3. Deploys the local helm chart under [example_chart](testdata/example_chart) with a name `example` to namespace created in Step #2
5. Run `helm test example` command to run a test on the Helm chart deployed in step #3

## How does `TestHelmChartRepoWorkflow` test work?

1. It creates a Kind Cluster with `third-party` prefix
2. Creates a new namespace with `third-party` prefix
3. Adds the `nginx-stable` helm repo and triggers a repo update workflow
4. Installs the helm chart using `nginx-stable/nginx-ingress` (without Wait mode) with name `nginx`
6. Waits for the Deployment to be up and running
7. Runs the `helm test nginx` command to run a basic helm test


## How to Run the Tests

```bash
go test -c -o helm.test .

./helm.test --v 4
```
128 changes: 128 additions & 0 deletions examples/third_party_integration/helm/helm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package helm

import (
"context"
"os"
"path/filepath"
"testing"

appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"sigs.k8s.io/e2e-framework/klient/k8s"
"sigs.k8s.io/e2e-framework/klient/wait"
"sigs.k8s.io/e2e-framework/klient/wait/conditions"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/features"
"sigs.k8s.io/e2e-framework/third_party/helm"
)

var curDir, _ = os.Getwd()

func TestHelmChartRepoWorkflow(t *testing.T) {
feature := features.New("Repo based helm chart workflow").
Setup(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
manager := helm.New(config.KubeconfigFile())
err := manager.RunRepo(helm.WithArgs("add", "nginx-stable", "https://helm.nginx.com/stable"))
if err != nil {
t.Fatal("failed to add nginx helm chart repo")
}
err = manager.RunRepo(helm.WithArgs("update"))
if err != nil {
t.Fatal("failed to upgrade helm repo")
}
err = manager.RunInstall(helm.WithName("nginx"), helm.WithNamespace(namespace), helm.WithReleaseName("nginx-stable/nginx-ingress"))
if err != nil {
t.Fatal("failed to install nginx Helm chart")
}
return ctx
}).
Assess("Deployment is running successfully", func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
deployment := &appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "nginx-nginx-ingress",
Namespace: namespace,
},
Spec: appsv1.DeploymentSpec{},
}
err := wait.For(conditions.New(config.Client().Resources()).ResourceScaled(deployment, func(object k8s.Object) int32 {
return object.(*appsv1.Deployment).Status.ReadyReplicas
}, 1))
if err != nil {
t.Fatal("failed waiting for the Deployment to reach a ready state")
}
return ctx
}).
Assess("run Chart Tests", func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
manager := helm.New(config.KubeconfigFile())
err := manager.RunTest(helm.WithArgs("nginx"), helm.WithNamespace(namespace))
if err != nil {
t.Fatal("failed waiting for the Deployment to reach a ready state")
}
return ctx
}).
Teardown(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
manager := helm.New(config.KubeconfigFile())
err := manager.RunRepo(helm.WithArgs("remove", "nginx-stable"))
if err != nil {
t.Fatal("cleanup of the helm repo failed")
}
return ctx
}).Feature()

testEnv.Test(t, feature)
}

func TestLocalHelmChartWorkflow(t *testing.T) {
feature := features.New("Local Helm chart workflow").
Setup(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
manager := helm.New(config.KubeconfigFile())
err := manager.RunInstall(helm.WithName("example"), helm.WithNamespace(namespace), helm.WithChart(filepath.Join(curDir, "testdata", "example_chart")), helm.WithWait(), helm.WithTimeout("10m"))
if err != nil {
t.Fatal("failed to invoke helm install operation due to an error", err)
}
return ctx
}).
Assess("Deployment Is Running Successfully", func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
deployment := &appsv1.Deployment{
ObjectMeta: v1.ObjectMeta{
Name: "example",
Namespace: namespace,
},
Spec: appsv1.DeploymentSpec{},
}
err := wait.For(conditions.New(config.Client().Resources()).ResourceScaled(deployment, func(object k8s.Object) int32 {
return object.(*appsv1.Deployment).Status.ReadyReplicas
}, 1))
if err != nil {
t.Fatal("failed waiting for the Deployment to reach a ready state")
}
return ctx
}).
Assess("run Helm Test Workflow", func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
manager := helm.New(config.KubeconfigFile())
err := manager.RunTest(helm.WithName("example"), helm.WithNamespace(namespace))
if err != nil {
t.Fatal("failed to perform helm test operation to check if the chart deployment is good")
}
return ctx
}).Feature()

testEnv.Test(t, feature)
}
50 changes: 50 additions & 0 deletions examples/third_party_integration/helm/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package helm

import (
"os"
"testing"

"sigs.k8s.io/e2e-framework/pkg/env"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/envfuncs"
)

var (
testEnv env.Environment
namespace string
kindClusterName string
)

func TestMain(m *testing.M) {
cfg, _ := envconf.NewFromFlags()
testEnv = env.NewWithConfig(cfg)
kindClusterName = envconf.RandomName("third-party", 16)
namespace = envconf.RandomName("third-party", 16)

testEnv.Setup(
envfuncs.CreateKindCluster(kindClusterName),
envfuncs.CreateNamespace(namespace),
)

testEnv.Finish(
envfuncs.DeleteNamespace(namespace),
envfuncs.DestroyKindCluster(kindClusterName),
)
os.Exit(testEnv.Run(m))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
name: example
description: An example Helm chart for Kubernetes e2e-framework
type: application
version: 0.1.0
appVersion: "1.16.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
automountServiceAccountToken: true
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Release.Name }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app: {{ .Release.Name }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-test-connection"
namespace: {{ .Release.Namespace }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ .Release.Name }}:{{ .Values.service.port }}']
restartPolicy: Never
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
replicaCount: 1


image:
repository: nginx
pullPolicy: IfNotPresent
tag: ""

service:
type: ClusterIP
port: 80
26 changes: 26 additions & 0 deletions hack/install-helm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash

# Copyright 2021 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -o errexit
set -o nounset
set -o pipefail

HELM_DOWNLOAD_URL=https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

if ! command -v helm; then
# Running the piped command with sudo disabled to avoid any security concerns that might arise
curl -sfL $HELM_DOWNLOAD_URL | USE_SUDO=false bash
fi
Loading

0 comments on commit 0c8ca84

Please sign in to comment.