github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/tests/integration-tests/rollout_test.go (about)

     1  /*This file is part of kuberpult.
     2  
     3  Kuberpult is free software: you can redistribute it and/or modify
     4  it under the terms of the Expat(MIT) License as published by
     5  the Free Software Foundation.
     6  
     7  Kuberpult is distributed in the hope that it will be useful,
     8  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  MIT License for more details.
    11  
    12  You should have received a copy of the MIT License
    13  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    14  
    15  Copyright 2023 freiheit.com*/
    16  
    17  package integration_tests
    18  
    19  import (
    20  	"bytes"
    21  	"io"
    22  	"os/exec"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	"gopkg.in/yaml.v3"
    28  )
    29  
    30  type simplifiedArgoApp struct {
    31  	Metadata struct {
    32  		Annotations map[string]string `yaml:"annotations"`
    33  	} `yaml:"metadata"`
    34  	Status struct {
    35  		OperationState struct {
    36  			Phase string `yaml:"phase"`
    37  		} `yaml:"operationState"`
    38  	} `yaml:"status"`
    39  }
    40  
    41  type simplifiedConfigMapMeta struct {
    42  	Name      string `yaml:"name"`
    43  	Namespace string `yaml:"namespace"`
    44  }
    45  
    46  type simplifiedConfigMap struct {
    47  	Metadata   simplifiedConfigMapMeta `yaml:"metadata"`
    48  	Data       map[string]string       `yaml:"data"`
    49  	Kind       string                  `yaml:"kind"`
    50  	ApiVersion string                  `yaml:"apiVersion"`
    51  }
    52  
    53  func TestArgoRolloutWork(t *testing.T) {
    54  	testCases := []struct {
    55  		name string
    56  		app  string
    57  	}{
    58  		{
    59  			name: "it can sync manifests into the cluster",
    60  			app:  "rollout-" + appSuffix,
    61  		},
    62  	}
    63  	for _, tc := range testCases {
    64  		tc := tc
    65  		t.Run(tc.name, func(t *testing.T) {
    66  			appName := "development-" + tc.app
    67  			expectedConfig := simplifiedConfigMap{
    68  				ApiVersion: "v1",
    69  				Kind:       "ConfigMap",
    70  				Metadata: simplifiedConfigMapMeta{
    71  					Name:      tc.app,
    72  					Namespace: "development",
    73  				},
    74  				Data: map[string]string{
    75  					"key": tc.app,
    76  				},
    77  			}
    78  			data, err := yaml.Marshal(expectedConfig)
    79  			if err != nil {
    80  				t.Fatal(err)
    81  			}
    82  			// Release a new app that we start completely fresh
    83  			releaseApp(t, tc.app, map[string]string{
    84  				"development": string(data),
    85  			})
    86  			// We have to sync the root app once because we have created a new app
    87  			runArgo(t, "app", "sync", "root")
    88  			// The sync may already be in progress, therefore we wait here for pending operations to finish
    89  			runArgo(t, "app", "wait", appName, "--operation")
    90  			runArgo(t, "app", "sync", appName)
    91  			_, appData := runArgo(t, "app", "get", appName, "-o", "yaml")
    92  			var app simplifiedArgoApp
    93  			err = yaml.Unmarshal(appData, &app)
    94  			if err != nil {
    95  				t.Fatal(err)
    96  			}
    97  			appAnnotation := app.Metadata.Annotations["com.freiheit.kuberpult/application"]
    98  			if appAnnotation != tc.app {
    99  				t.Errorf("wrong value for annotation \"com.freiheit.kuberpult/application\": expected %q but got %q", tc.app, appAnnotation)
   100  			}
   101  			if app.Status.OperationState.Phase != "Succeeded" {
   102  				t.Errorf("wrong value for operation state phase, expected %q got %q", "Succeeded", app.Status.OperationState.Phase)
   103  			}
   104  			_, manifestData := runArgo(t, "app", "manifests", appName)
   105  			var actualConfig simplifiedConfigMap
   106  			err = yaml.Unmarshal(manifestData, &actualConfig)
   107  			if err != nil {
   108  				t.Fatal(err)
   109  			}
   110  			d := cmp.Diff(expectedConfig, actualConfig)
   111  			if d != "" {
   112  				t.Errorf("unexpected diff between config maps: %s", d)
   113  			}
   114  		})
   115  	}
   116  }
   117  
   118  func runArgo(t *testing.T, args ...string) (*exec.Cmd, []byte) {
   119  	var out, stderr bytes.Buffer
   120  	args = append([]string{"--port-forward"}, args...)
   121  	cmd := exec.Command("argocd", args...)
   122  	cmd.Stdout = &out
   123  	cmd.Stderr = &stderr
   124  	err := cmd.Run()
   125  	if err != nil {
   126  		t.Fatalf("argocd %q command exited with code %d\nerr: %s\nstderr: %s", strings.Join(args, " "), cmd.ProcessState.ExitCode(), err, stderr.String())
   127  	}
   128  	if cmd.ProcessState.ExitCode() != 0 {
   129  		t.Fatalf("argocd %q command exited with code %d\nstderr: %s", strings.Join(args, " "), cmd.ProcessState.ExitCode(), stderr.String())
   130  	}
   131  	return cmd, out.Bytes()
   132  }
   133  
   134  func releaseApp(t *testing.T, application string, manifests map[string]string) {
   135  	values := map[string]io.Reader{
   136  		"application": strings.NewReader(application),
   137  	}
   138  	files := map[string]io.Reader{}
   139  	for env, data := range manifests {
   140  		files["manifests["+env+"]"] = strings.NewReader(data)
   141  		files["signatures["+env+"]"] = strings.NewReader(CalcSignature(t, data))
   142  	}
   143  	actualStatusCode, body, err := callRelease(values, files)
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  	if actualStatusCode > 299 {
   148  		t.Fatalf("bad status code: %d, body: %s", actualStatusCode, body)
   149  	}
   150  }