Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add an option for passing custom k8s rest config #1354

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions modules/k8s/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ func GetKubernetesClientFromOptionsE(t testing.TestingT, options *KubectlOptions
return nil, err
}
logger.Log(t, "Configuring Kubernetes client to use the in-cluster serviceaccount token")
} else if options.RestConfig != nil {
config = options.RestConfig
logger.Log(t, "Configuring Kubernetes client to use provided rest config object set with API server address: %s", config.Host)
} else {
kubeConfigPath, err := options.GetConfigPath(t)
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions modules/k8s/kubectl_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package k8s
import (
"github.com/gruntwork-io/terratest/modules/logger"
"github.com/gruntwork-io/terratest/modules/testing"
"k8s.io/client-go/rest"
)

// KubectlOptions represents common options necessary to specify for all Kubectl calls
Expand All @@ -12,6 +13,7 @@ type KubectlOptions struct {
Namespace string
Env map[string]string
InClusterAuth bool
RestConfig *rest.Config
Logger *logger.Logger
}

Expand All @@ -32,6 +34,14 @@ func NewKubectlOptionsWithInClusterAuth() *KubectlOptions {
}
}

// NewKubectlOptionsWithRestConfig will return a pointer to a new instance of KubectlOptions with pre-built config object
func NewKubectlOptionsWithRestConfig(config *rest.Config, namespace string) *KubectlOptions {
return &KubectlOptions{
Namespace: namespace,
RestConfig: config,
}
}

// GetConfigPath will return a sensible default if the config path is not set on the options.
func (kubectlOptions *KubectlOptions) GetConfigPath(t testing.TestingT) (string, error) {
// We predeclare `err` here so that we can update `kubeConfigPath` in the if block below. Otherwise, go complains
Expand Down
71 changes: 71 additions & 0 deletions test/kubernetes_rest_config_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//go:build kubeall || kubernetes
// +build kubeall kubernetes

// NOTE: we have build tags to differentiate kubernetes tests from non-kubernetes tests. This is done because minikube
// is heavy and can interfere with docker related tests in terratest. Specifically, many of the tests start to fail with
// `connection refused` errors from `minikube`. To avoid overloading the system, we run the kubernetes tests and helm
// tests separately from the others. This may not be necessary if you have a sufficiently powerful machine. We
// recommend at least 4 cores and 16GB of RAM if you want to run all the tests together.

package test

import (
"fmt"
"os/user"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/require"
"k8s.io/client-go/tools/clientcmd"

"github.com/gruntwork-io/terratest/modules/k8s"
"github.com/gruntwork-io/terratest/modules/random"
)

func TestKubernetesRestConfigBasicExampleConfig(t *testing.T) {
t.Parallel()

// website::tag::1::Path to the Kubernetes resource config we will test
kubeResourcePath, err := filepath.Abs("../examples/kubernetes-basic-example/nginx-deployment.yml")
require.NoError(t, err)

// To ensure we can reuse the resource config on the same cluster to test different scenarios, we setup a unique
// namespace for the resources for this test.
// Note that namespaces must be lowercase.
namespaceName := fmt.Sprintf("kubernetes-basic-example-%s", strings.ToLower(random.UniqueId()))

usr, err := user.Current()
if err != nil {
require.NoError(t, err)
}

// Construct the path to the kubeconfig file
kubeconfigPath := filepath.Join(usr.HomeDir, ".kube", "config")

// Generate rest.Config from kubeconfig file
config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
panic(err.Error())
}

// website::tag::2:: Setup the kubectl config and context.
options := k8s.NewKubectlOptionsWithRestConfig(config, namespaceName)

k8s.CreateNamespace(t, options, namespaceName)
// website::tag::5::Make sure to delete the namespace at the end of the test
defer k8s.DeleteNamespace(t, options, namespaceName)

// website::tag::6::At the end of the test, run `kubectl delete -f RESOURCE_CONFIG` to clean up any resources that were created.
defer k8s.KubectlDelete(t, options, kubeResourcePath)

// website::tag::3::Apply kubectl with 'kubectl apply -f RESOURCE_CONFIG' command.
// This will run `kubectl apply -f RESOURCE_CONFIG` and fail the test if there are any errors
k8s.KubectlApply(t, options, kubeResourcePath)

// website::tag::4::Check if NGINX service was deployed successfully.
// This will get the service resource and verify that it exists and was retrieved successfully. This function will
// fail the test if the there is an error retrieving the service resource from Kubernetes.
service := k8s.GetService(t, options, "nginx-service")
require.Equal(t, service.Name, "nginx-service")
}