github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_cattle_agent_test.go (about)

     1  // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package mcagent
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"net/http"
    12  	"net/http/httptest"
    13  	"os"
    14  	"testing"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"go.uber.org/zap"
    18  	corev1 "k8s.io/api/core/v1"
    19  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    20  	"k8s.io/apimachinery/pkg/runtime"
    21  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    22  )
    23  
    24  // TestSyncer_syncCattleClusterAgent tests the syncCattleClusterAgent of Syncer
    25  // GIVEN a call to syncCattleClusterAgent
    26  // WHEN there exists no previous cattle agent hash
    27  // THEN update the cattle-cluster-agent deployment, create the cattle-credential secret and update the hash
    28  func TestSyncer_syncCattleClusterAgent(t *testing.T) {
    29  	asserts := assert.New(t)
    30  	log := zap.S().With("test")
    31  
    32  	scheme := runtime.NewScheme()
    33  	err := corev1.SchemeBuilder.AddToScheme(scheme)
    34  	asserts.NoError(err)
    35  
    36  	secret, err := getSampleSecret("testdata/registration-manifest.yaml")
    37  	asserts.NoError(err)
    38  
    39  	adminClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&secret).Build()
    40  
    41  	server := newServer()
    42  	defer server.Close()
    43  
    44  	err = createFakeKubeConfig(server.URL)
    45  	defer deleteFakeKubeConfig()
    46  	asserts.NoError(err)
    47  
    48  	kubeConfigPath, err := getFakeKubeConfigPath()
    49  	asserts.NoError(err)
    50  
    51  	s := Syncer{
    52  		AdminClient:        adminClient,
    53  		Log:                log,
    54  		ManagedClusterName: "cluster1",
    55  		Context:            context.TODO(),
    56  	}
    57  
    58  	newCattleAgentHash, err := s.syncCattleClusterAgent("", kubeConfigPath)
    59  	asserts.NoError(err)
    60  	asserts.NotEmpty(newCattleAgentHash)
    61  }
    62  
    63  // TestSyncer_syncCattleClusterAgentNoRancherManifest tests the syncCattleClusterAgent of Syncer
    64  // GIVEN a call to syncCattleClusterAgent
    65  // WHEN the registration manifest doesn't have the required rancher resources
    66  // THEN do nothing and return
    67  func TestSyncer_syncCattleClusterAgentNoRancherManifest(t *testing.T) {
    68  	asserts := assert.New(t)
    69  	log := zap.S().With("test")
    70  
    71  	scheme := runtime.NewScheme()
    72  	err := corev1.SchemeBuilder.AddToScheme(scheme)
    73  	asserts.NoError(err)
    74  
    75  	secret, err := getSampleSecret("testdata/incomplete-registration-manifest.yaml")
    76  	asserts.NoError(err)
    77  
    78  	adminClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&secret).Build()
    79  
    80  	s := Syncer{
    81  		AdminClient:        adminClient,
    82  		Log:                log,
    83  		ManagedClusterName: "cluster1",
    84  		Context:            context.TODO(),
    85  	}
    86  
    87  	newCattleAgentHash, err := s.syncCattleClusterAgent("", "")
    88  	asserts.NoError(err)
    89  	asserts.Empty(newCattleAgentHash)
    90  }
    91  
    92  // TestSyncer_syncCattleClusterAgentHashExists tests the syncCattleClusterAgent of Syncer
    93  func TestSyncer_syncCattleClusterAgentHashExists(t *testing.T) {
    94  	asserts := assert.New(t)
    95  	log := zap.S().With("test")
    96  
    97  	scheme := runtime.NewScheme()
    98  	err := corev1.SchemeBuilder.AddToScheme(scheme)
    99  	asserts.NoError(err)
   100  
   101  	secret, err := getSampleSecret("testdata/registration-manifest.yaml")
   102  	asserts.NoError(err)
   103  
   104  	adminClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&secret).Build()
   105  
   106  	server := newServer()
   107  	defer server.Close()
   108  
   109  	err = createFakeKubeConfig(server.URL)
   110  	defer deleteFakeKubeConfig()
   111  	asserts.NoError(err)
   112  
   113  	kubeConfigPath, err := getFakeKubeConfigPath()
   114  	asserts.NoError(err)
   115  
   116  	// Initialise the cattleAgentHash with a random hash string
   117  	previousCattleAgentHash := "[1 61 83 140 196 214 70 37 227 165 192 170 234 13 222 47 123]"
   118  
   119  	s := Syncer{
   120  		AdminClient:        adminClient,
   121  		Log:                log,
   122  		ManagedClusterName: "cluster1",
   123  		Context:            context.TODO(),
   124  	}
   125  
   126  	// GIVEN a call to syncCattleClusterAgent
   127  	// WHEN a hash already exists
   128  	// THEN if the hash has changed, update the resources and the hash
   129  	newCattleAgentHash, err := s.syncCattleClusterAgent(previousCattleAgentHash, kubeConfigPath)
   130  	asserts.NoError(err)
   131  	asserts.NotEmpty(newCattleAgentHash)
   132  	asserts.NotEqual(previousCattleAgentHash, newCattleAgentHash)
   133  
   134  	previousCattleAgentHash = newCattleAgentHash
   135  
   136  	// GIVEN a call to syncCattleClusterAgent
   137  	// WHEN a hash already exists
   138  	// THEN if the hash has not changed, do nothing
   139  	newerCattleAgentHash, err := s.syncCattleClusterAgent(previousCattleAgentHash, "")
   140  	asserts.NoError(err)
   141  	asserts.NotEmpty(newerCattleAgentHash)
   142  	asserts.Equal(previousCattleAgentHash, newerCattleAgentHash)
   143  }
   144  
   145  // newServer returns a httptest server which the
   146  // dynamic client and discovery client can send
   147  // GET/POST/PATCH/DELETE requests to instead of a real cluster
   148  func newServer() *httptest.Server {
   149  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   150  		var obj interface{}
   151  		switch req.URL.Path {
   152  		case "/api/v1/namespaces/cattle-system":
   153  			obj = &metav1.APIVersions{
   154  				TypeMeta: metav1.TypeMeta{
   155  					Kind: "APIVersions",
   156  				},
   157  				Versions: []string{
   158  					"v1",
   159  				},
   160  			}
   161  		case "/api":
   162  			obj = &metav1.APIVersions{
   163  				Versions: []string{
   164  					"v1",
   165  				},
   166  			}
   167  		case "/api/v1":
   168  			obj = &metav1.APIResourceList{
   169  				GroupVersion: "v1",
   170  				APIResources: []metav1.APIResource{
   171  					{Name: "secrets", Namespaced: true, Kind: "Secret"},
   172  				},
   173  			}
   174  		case "/apis/apps/v1/namespaces/cattle-system/deployments/cattle-cluster-agent":
   175  			body, _ := io.ReadAll(req.Body)
   176  			w.Write(body)
   177  			return
   178  		case "/api/v1/namespaces/cattle-system/secrets":
   179  			body, _ := io.ReadAll(req.Body)
   180  			w.Write(body)
   181  			return
   182  		default:
   183  			w.WriteHeader(http.StatusNotFound)
   184  			return
   185  		}
   186  		output, err := json.Marshal(obj)
   187  		if err != nil {
   188  			return
   189  		}
   190  		w.Header().Set("Content-Type", "application/json")
   191  		w.WriteHeader(http.StatusOK)
   192  		w.Write(output)
   193  	}))
   194  	return server
   195  }
   196  
   197  // createFakeKubeConfig creates a fake kubeconfig
   198  // in the pwd with the url of the httptest server
   199  func createFakeKubeConfig(url string) error {
   200  	fakeKubeConfig, err := os.Create("dummy-kubeconfig")
   201  	defer fakeKubeConfig.Close()
   202  
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	_, err = fmt.Fprintf(fakeKubeConfig, `apiVersion: v1
   208  clusters:
   209  - cluster:
   210      # This is dummy data
   211      certificate-authority-data: RFVNTVkgQ0VSVElGSUNBVEU=
   212      server: %s
   213    name: user-test
   214  users:
   215  - name: user-test
   216  contexts:
   217  - context:
   218      cluster: user-test
   219      user: user-test
   220    name: user-test
   221  current-context: user-test`, url)
   222  
   223  	return err
   224  }
   225  
   226  func getFakeKubeConfigPath() (string, error) {
   227  	pwd, err := os.Getwd()
   228  
   229  	if err != nil {
   230  		return pwd, err
   231  	}
   232  
   233  	pwd = pwd + "/dummy-kubeconfig"
   234  	return pwd, nil
   235  }
   236  
   237  func deleteFakeKubeConfig() error {
   238  	err := os.Remove("dummy-kubeconfig")
   239  	return err
   240  }