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 }