351 lines
8.4 KiB
Go
351 lines
8.4 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package helm
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/provider"
|
|
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
|
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
|
|
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
)
|
|
|
|
const (
|
|
testNamespacePrefix = "terraform-acc-test"
|
|
testResourceName = "test"
|
|
testChartsPath = "./testdata/charts"
|
|
testRepositoryDir = "./testdata/repository"
|
|
)
|
|
|
|
var (
|
|
accTest bool
|
|
testRepositoryURL string
|
|
client kubernetes.Interface = nil
|
|
testMeta *Meta
|
|
)
|
|
|
|
var providerFactory map[string]func() (tfprotov6.ProviderServer, error)
|
|
|
|
func protoV6ProviderFactories() map[string]func() (tfprotov6.ProviderServer, error) {
|
|
if len(providerFactory) != 0 {
|
|
return providerFactory
|
|
}
|
|
|
|
providerFactory = map[string]func() (tfprotov6.ProviderServer, error){
|
|
"helm": providerserver.NewProtocol6WithError(New("test")()),
|
|
}
|
|
|
|
return providerFactory
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
home, err := ioutil.TempDir(os.TempDir(), "helm")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer os.RemoveAll(home)
|
|
|
|
err = os.Setenv("HELM_REPOSITORY_CONFIG", filepath.Join(home, "config/repositories.yaml"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = os.Setenv("HELM_REPOSITORY_CACHE", filepath.Join(home, "cache/helm/repository"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = os.Setenv("HELM_REGISTRY_CONFIG", filepath.Join(home, "config/registry.json"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = os.Setenv("HELM_PLUGINS", filepath.Join(home, "plugins"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = os.Setenv("XDG_CACHE_HOME", filepath.Join(home, "cache"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
accTest = os.Getenv("TF_ACC") == "1"
|
|
|
|
var stopRepositoryServer func()
|
|
if accTest {
|
|
_, err := exec.LookPath("helm")
|
|
if err != nil {
|
|
panic(`command "helm" needs to be available to run the test suite`)
|
|
}
|
|
|
|
// create the Kubernetes client
|
|
c, err := createKubernetesClient()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
client = c
|
|
|
|
// Build the test repository and start the server
|
|
buildChartRepository()
|
|
testRepositoryURL, stopRepositoryServer = startRepositoryServer()
|
|
}
|
|
|
|
ec := m.Run()
|
|
|
|
if accTest {
|
|
stopRepositoryServer()
|
|
cleanupChartRepository()
|
|
}
|
|
|
|
os.Exit(ec)
|
|
}
|
|
|
|
// todo
|
|
func TestProvider(t *testing.T) {
|
|
ctx := context.Background()
|
|
provider := New("test")()
|
|
|
|
// Create the provider server
|
|
providerServer, err := createProviderServer(provider)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create provider server: %s", err)
|
|
}
|
|
// Perform config validation
|
|
|
|
validateResponse, err := providerServer.ValidateProviderConfig(ctx, &tfprotov6.ValidateProviderConfigRequest{})
|
|
if err != nil {
|
|
t.Fatalf("Provider config validation failed, error: %v", err)
|
|
}
|
|
|
|
if hasError(validateResponse.Diagnostics) {
|
|
t.Fatalf("Provider config validation failed, diagnostics: %v", validateResponse.Diagnostics)
|
|
}
|
|
}
|
|
|
|
func createProviderServer(provider provider.Provider) (tfprotov6.ProviderServer, error) {
|
|
providerServerFunc := providerserver.NewProtocol6WithError(provider)
|
|
server, err := providerServerFunc()
|
|
if err != nil {
|
|
} else {
|
|
}
|
|
return server, err
|
|
}
|
|
|
|
func buildChartRepository() {
|
|
if _, err := os.Stat(testRepositoryDir); os.IsNotExist(err) {
|
|
os.Mkdir(testRepositoryDir, os.ModePerm)
|
|
}
|
|
|
|
charts, err := ioutil.ReadDir(testChartsPath)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// package all the charts
|
|
for _, c := range charts {
|
|
cmd := exec.Command("helm", "package", "-u",
|
|
filepath.Join(testChartsPath, c.Name()),
|
|
"-d", testRepositoryDir)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Println(string(out))
|
|
panic(err)
|
|
}
|
|
|
|
log.Printf("Created repository package for %q\n", c.Name())
|
|
}
|
|
|
|
// build the repository index
|
|
cmd := exec.Command("helm", "repo", "index", testRepositoryDir)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Println(string(out))
|
|
panic(err)
|
|
}
|
|
|
|
log.Println("Built chart repository index")
|
|
}
|
|
|
|
func cleanupChartRepository() {
|
|
if _, err := os.Stat(testRepositoryDir); err == nil {
|
|
err := os.RemoveAll(testRepositoryDir)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func startRepositoryServer() (string, func()) {
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(1)
|
|
|
|
var shutdownFunc func()
|
|
go func() {
|
|
fileserver := http.Server{
|
|
Handler: http.FileServer(http.Dir(testRepositoryDir)),
|
|
}
|
|
fileserver.SetKeepAlivesEnabled(false)
|
|
shutdownFunc = func() { fileserver.Shutdown(context.Background()) }
|
|
listener, err := net.Listen("tcp", ":0")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
port := listener.Addr().(*net.TCPAddr).Port
|
|
testRepositoryURL = fmt.Sprintf("http://localhost:%d", port)
|
|
wg.Done()
|
|
err = fileserver.Serve(listener)
|
|
if err != nil && err != http.ErrServerClosed {
|
|
panic(err)
|
|
}
|
|
}()
|
|
wg.Wait()
|
|
|
|
return testRepositoryURL, shutdownFunc
|
|
}
|
|
|
|
func createAndConfigureProviderServer(provider provider.Provider, ctx context.Context) (tfprotov6.ProviderServer, error) {
|
|
log.Println("Starting createAndConfigureProviderServer...")
|
|
|
|
providerServerFunc := providerserver.NewProtocol6WithError(provider)
|
|
providerServer, err := providerServerFunc()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to create protocol6 provider: %w", err)
|
|
}
|
|
log.Println("Provider server function created successfully.")
|
|
|
|
configResponse, err := providerServer.ConfigureProvider(ctx, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error configuring provider: %w", err)
|
|
}
|
|
log.Println("Provider configured successfully.")
|
|
|
|
if hasError(configResponse.Diagnostics) {
|
|
return nil, fmt.Errorf("Provider configuration failed, diagnostics: %#v", configResponse.Diagnostics[0])
|
|
}
|
|
|
|
if helmProvider, ok := provider.(*HelmProvider); ok {
|
|
testMeta = helmProvider.meta
|
|
if testMeta == nil {
|
|
log.Println("testMeta is nil after type assertion.")
|
|
} else {
|
|
log.Printf("testMeta initialized: %+v", testMeta)
|
|
}
|
|
} else {
|
|
return nil, fmt.Errorf("Failed to type assert provider to HelmProvider")
|
|
}
|
|
|
|
return providerServer, nil
|
|
}
|
|
|
|
func testAccPreCheck(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping acceptance tests in short mode")
|
|
}
|
|
http.DefaultClient.CloseIdleConnections()
|
|
|
|
ctx := context.TODO()
|
|
|
|
provider := New("test")()
|
|
|
|
// Create and configure the ProviderServer
|
|
_, err := createAndConfigureProviderServer(provider, ctx)
|
|
if err != nil {
|
|
t.Fatalf("Pre-check failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func createKubernetesClient() (kubernetes.Interface, error) {
|
|
rules := clientcmd.NewDefaultClientConfigLoadingRules()
|
|
|
|
kubeconfig := os.Getenv("KUBE_CONFIG_PATH")
|
|
if kubeconfig == "" {
|
|
panic("Need to set KUBE_CONFIG_PATH")
|
|
}
|
|
rules.ExplicitPath = kubeconfig
|
|
|
|
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, &clientcmd.ConfigOverrides{}).ClientConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c, err := kubernetes.NewForConfig(config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func createRandomNamespace(t *testing.T) string {
|
|
if !accTest {
|
|
t.Skip("TF_ACC=1 not set")
|
|
return ""
|
|
}
|
|
|
|
namespace := fmt.Sprintf("%s-%s", testNamespacePrefix, acctest.RandString(10))
|
|
ns := &v1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: namespace,
|
|
},
|
|
}
|
|
_, err := client.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
|
|
if err != nil {
|
|
t.Fatalf("Could not create test namespace %q: %s", namespace, err)
|
|
}
|
|
return namespace
|
|
}
|
|
|
|
func deleteNamespace(t *testing.T, namespace string) {
|
|
if !accTest {
|
|
t.Skip("TF_ACC=1 not set")
|
|
return
|
|
}
|
|
|
|
gracePeriodSeconds := int64(0)
|
|
deleteOptions := metav1.DeleteOptions{
|
|
GracePeriodSeconds: &gracePeriodSeconds,
|
|
}
|
|
err := client.CoreV1().Namespaces().Delete(context.TODO(), namespace, deleteOptions)
|
|
if err != nil {
|
|
t.Fatalf("An error occurred while deleting namespace %q: %q", namespace, err)
|
|
}
|
|
}
|
|
|
|
func randName(prefix string) string {
|
|
return fmt.Sprintf("%s-%s", prefix, acctest.RandString(10))
|
|
}
|
|
|
|
func hasError(diagnostics []*tfprotov6.Diagnostic) bool {
|
|
for _, diagnostic := range diagnostics {
|
|
if diagnostic.Severity == tfprotov6.DiagnosticSeverityError {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func DynamicValueEmpty() *tfprotov6.DynamicValue {
|
|
return &tfprotov6.DynamicValue{
|
|
MsgPack: nil,
|
|
JSON: nil,
|
|
}
|
|
}
|