github.com/joelanford/operator-sdk@v0.8.2/internal/pkg/scaffold/olm-catalog/csv_test.go (about)

     1  // Copyright 2018 The Operator-SDK Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package catalog
    16  
    17  import (
    18  	"bytes"
    19  	"io"
    20  	"io/ioutil"
    21  	"os"
    22  	"path/filepath"
    23  	"testing"
    24  
    25  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold"
    26  	"github.com/operator-framework/operator-sdk/internal/pkg/scaffold/input"
    27  	testutil "github.com/operator-framework/operator-sdk/internal/pkg/scaffold/internal/testutil"
    28  	"github.com/operator-framework/operator-sdk/internal/util/diffutil"
    29  	"github.com/operator-framework/operator-sdk/pkg/k8sutil"
    30  
    31  	"github.com/coreos/go-semver/semver"
    32  	"github.com/ghodss/yaml"
    33  	olmapiv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
    34  	olminstall "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
    35  	"github.com/spf13/afero"
    36  	appsv1 "k8s.io/api/apps/v1"
    37  )
    38  
    39  const testDataDir = "testdata"
    40  
    41  var testDeployDir = filepath.Join(testDataDir, scaffold.DeployDir)
    42  
    43  func TestCSVNew(t *testing.T) {
    44  	buf := &bytes.Buffer{}
    45  	s := &scaffold.Scaffold{
    46  		GetWriter: func(_ string, _ os.FileMode) (io.Writer, error) {
    47  			return buf, nil
    48  		},
    49  	}
    50  	csvVer := "0.1.0"
    51  	projectName := "app-operator"
    52  
    53  	sc := &CSV{CSVVersion: csvVer, pathPrefix: testDataDir}
    54  	err := s.Execute(&input.Config{ProjectName: projectName}, sc)
    55  	if err != nil {
    56  		t.Fatalf("Failed to execute the scaffold: (%v)", err)
    57  	}
    58  
    59  	// Get the expected CSV manifest from test data dir.
    60  	csvExpBytes, err := afero.ReadFile(s.Fs, sc.getCSVPath(csvVer))
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  	csvExp := string(csvExpBytes)
    65  	if csvExp != buf.String() {
    66  		diffs := diffutil.Diff(csvExp, buf.String())
    67  		t.Errorf("Expected vs actual differs.\n%v", diffs)
    68  	}
    69  }
    70  
    71  func TestCSVFromOld(t *testing.T) {
    72  	s := &scaffold.Scaffold{Fs: afero.NewMemMapFs()}
    73  	projectName := "app-operator"
    74  	oldCSVVer, newCSVVer := "0.1.0", "0.2.0"
    75  
    76  	// Write all files in testdata/deploy to fs so manifests are present when
    77  	// writing a new CSV.
    78  	if err := testutil.WriteOSPathToFS(afero.NewOsFs(), s.Fs, testDeployDir); err != nil {
    79  		t.Fatalf("Failed to write %s to in-memory test fs: (%v)", testDeployDir, err)
    80  	}
    81  
    82  	sc := &CSV{
    83  		CSVVersion:  newCSVVer,
    84  		FromVersion: oldCSVVer,
    85  		pathPrefix:  testDataDir,
    86  	}
    87  	err := s.Execute(&input.Config{ProjectName: projectName}, sc)
    88  	if err != nil {
    89  		t.Fatalf("Failed to execute the scaffold: (%v)", err)
    90  	}
    91  
    92  	// Check if a new file was written at the expected path.
    93  	newCSVPath := sc.getCSVPath(newCSVVer)
    94  	newCSV, newExists, err := getCSVFromFSIfExists(s.Fs, newCSVPath)
    95  	if err != nil {
    96  		t.Fatalf("Failed to get new CSV %s: (%v)", newCSVPath, err)
    97  	}
    98  	if !newExists {
    99  		t.Fatalf("New CSV does not exist at %s", newCSVPath)
   100  	}
   101  
   102  	expName := getCSVName(projectName, newCSVVer)
   103  	if newCSV.ObjectMeta.Name != expName {
   104  		t.Errorf("Expected CSV metadata.name %s, got %s", expName, newCSV.ObjectMeta.Name)
   105  	}
   106  	expReplaces := getCSVName(projectName, oldCSVVer)
   107  	if newCSV.Spec.Replaces != expReplaces {
   108  		t.Errorf("Expected CSV spec.replaces %s, got %s", expReplaces, newCSV.Spec.Replaces)
   109  	}
   110  }
   111  
   112  func TestUpdateVersion(t *testing.T) {
   113  	projectName := "app-operator"
   114  	oldCSVVer, newCSVVer := "0.1.0", "0.2.0"
   115  	sc := &CSV{
   116  		Input:      input.Input{ProjectName: projectName},
   117  		CSVVersion: newCSVVer,
   118  		pathPrefix: testDataDir,
   119  	}
   120  	csvExpBytes, err := ioutil.ReadFile(sc.getCSVPath(oldCSVVer))
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  	csv := &olmapiv1alpha1.ClusterServiceVersion{}
   125  	if err := yaml.Unmarshal(csvExpBytes, csv); err != nil {
   126  		t.Fatal(err)
   127  	}
   128  
   129  	if err := sc.updateCSVVersions(csv); err != nil {
   130  		t.Fatalf("Failed to update csv with version %s: (%v)", newCSVVer, err)
   131  	}
   132  
   133  	wantedSemver := semver.New(newCSVVer)
   134  	if !csv.Spec.Version.Equal(*wantedSemver) {
   135  		t.Errorf("Wanted csv version %v, got %v", *wantedSemver, csv.Spec.Version)
   136  	}
   137  	wantedName := getCSVName(projectName, newCSVVer)
   138  	if csv.ObjectMeta.Name != wantedName {
   139  		t.Errorf("Wanted csv name %s, got %s", wantedName, csv.ObjectMeta.Name)
   140  	}
   141  
   142  	var resolver *olminstall.StrategyResolver
   143  	stratInterface, err := resolver.UnmarshalStrategy(csv.Spec.InstallStrategy)
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  	strat, ok := stratInterface.(*olminstall.StrategyDetailsDeployment)
   148  	if !ok {
   149  		t.Fatalf("Strategy of type %T was not StrategyDetailsDeployment", stratInterface)
   150  	}
   151  	csvPodImage := strat.DeploymentSpecs[0].Spec.Template.Spec.Containers[0].Image
   152  	// updateCSVVersions should not update podspec image.
   153  	wantedImage := "quay.io/example-inc/operator:v0.1.0"
   154  	if csvPodImage != wantedImage {
   155  		t.Errorf("Podspec image changed from %s to %s", wantedImage, csvPodImage)
   156  	}
   157  
   158  	wantedReplaces := getCSVName(projectName, "0.1.0")
   159  	if csv.Spec.Replaces != wantedReplaces {
   160  		t.Errorf("Wanted csv replaces %s, got %s", wantedReplaces, csv.Spec.Replaces)
   161  	}
   162  }
   163  
   164  func TestGetDisplayName(t *testing.T) {
   165  	cases := []struct {
   166  		input, wanted string
   167  	}{
   168  		{"Appoperator", "Appoperator"},
   169  		{"appoperator", "Appoperator"},
   170  		{"appoperatoR", "Appoperato R"},
   171  		{"AppOperator", "App Operator"},
   172  		{"appOperator", "App Operator"},
   173  		{"app-operator", "App Operator"},
   174  		{"app-_operator", "App Operator"},
   175  		{"App-operator", "App Operator"},
   176  		{"app-_Operator", "App Operator"},
   177  		{"app--Operator", "App Operator"},
   178  		{"app--_Operator", "App Operator"},
   179  		{"APP", "APP"},
   180  		{"another-AppOperator_againTwiceThrice More", "Another App Operator Again Twice Thrice More"},
   181  	}
   182  
   183  	for _, c := range cases {
   184  		dn := getDisplayName(c.input)
   185  		if dn != c.wanted {
   186  			t.Errorf("Wanted %s, got %s", c.wanted, dn)
   187  		}
   188  	}
   189  }
   190  
   191  func TestSetAndCheckOLMNamespaces(t *testing.T) {
   192  	depBytes, err := ioutil.ReadFile(filepath.Join(testDeployDir, "operator.yaml"))
   193  	if err != nil {
   194  		t.Fatalf("Failed to read Deployment bytes: %v", err)
   195  	}
   196  
   197  	// The test operator.yaml doesn't have "olm.targetNamespaces", so first
   198  	// check that depHasOLMNamespaces() returns false.
   199  	dep := &appsv1.Deployment{}
   200  	if err := yaml.Unmarshal(depBytes, dep); err != nil {
   201  		t.Fatalf("Failed to unmarshal Deployment bytes: %v", err)
   202  	}
   203  	if depHasOLMNamespaces(dep) {
   204  		t.Error("Expected depHasOLMNamespaces to return false, got true")
   205  	}
   206  
   207  	// Insert "olm.targetNamespaces" into WATCH_NAMESPACE and check that
   208  	// depHasOLMNamespaces() returns true.
   209  	setWatchNamespacesEnv(dep)
   210  	if !depHasOLMNamespaces(dep) {
   211  		t.Error("Expected depHasOLMNamespaces to return true, got false")
   212  	}
   213  
   214  	// Overwrite WATCH_NAMESPACE and check that depHasOLMNamespaces() returns
   215  	// false.
   216  	overwriteContainerEnvVar(dep, k8sutil.WatchNamespaceEnvVar, newEnvVar("FOO", "bar"))
   217  	if depHasOLMNamespaces(dep) {
   218  		t.Error("Expected depHasOLMNamespaces to return false, got true")
   219  	}
   220  
   221  	// Insert "olm.targetNamespaces" elsewhere in the deployment pod spec
   222  	// and check that depHasOLMNamespaces() returns true.
   223  	dep = &appsv1.Deployment{}
   224  	if err := yaml.Unmarshal(depBytes, dep); err != nil {
   225  		t.Fatalf("Failed to unmarshal Deployment bytes: %v", err)
   226  	}
   227  	dep.Spec.Template.ObjectMeta.Labels["namespace"] = olmTNMeta
   228  	if !depHasOLMNamespaces(dep) {
   229  		t.Error("Expected depHasOLMNamespaces to return true, got false")
   230  	}
   231  }