get.porter.sh/porter@v1.3.0/pkg/cnab/extended_bundle_test.go (about) 1 package cnab 2 3 import ( 4 "testing" 5 6 depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1" 7 "get.porter.sh/porter/pkg/portercontext" 8 porterschema "get.porter.sh/porter/pkg/schema" 9 "github.com/cnabio/cnab-go/bundle" 10 "github.com/cnabio/cnab-go/bundle/definition" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestExtendedBundle_IsPorterBundle(t *testing.T) { 16 t.Run("made by porter", func(t *testing.T) { 17 b := NewBundle(bundle.Bundle{ 18 Custom: map[string]interface{}{ 19 "sh.porter": struct{}{}, 20 }, 21 }) 22 23 assert.True(t, b.IsPorterBundle()) 24 }) 25 26 t.Run("third party bundle", func(t *testing.T) { 27 b := ExtendedBundle{} 28 29 assert.False(t, b.IsPorterBundle()) 30 }) 31 } 32 33 func TestExtendedBundle_IsFileType(t *testing.T) { 34 stringDef := &definition.Schema{ 35 Type: "string", 36 } 37 fileDef := &definition.Schema{ 38 Type: "string", 39 ContentEncoding: "base64", 40 } 41 bun := NewBundle(bundle.Bundle{ 42 RequiredExtensions: []string{ 43 FileParameterExtensionKey, 44 }, 45 Definitions: definition.Definitions{ 46 "string": stringDef, 47 "file": fileDef, 48 }, 49 Parameters: map[string]bundle.Parameter{ 50 "debug": { 51 Definition: "string", 52 Required: true, 53 }, 54 "tfstate": { 55 Definition: "file", 56 }, 57 }, 58 }) 59 60 assert.False(t, bun.IsFileType(stringDef), "strings should not be flagged as files") 61 assert.True(t, bun.IsFileType(fileDef), "strings+base64 with the file-parameters extension should be categorized as files") 62 63 // Ensure we honor the custom extension 64 bun.RequiredExtensions = nil 65 assert.False(t, bun.IsFileType(fileDef), "don't categorize as file type when the extension is missing") 66 67 // Ensure we work with old bundles before the extension was created 68 bun.Custom = map[string]interface{}{ 69 "sh.porter": struct{}{}, 70 } 71 72 assert.True(t, bun.IsFileType(fileDef), "categorize string+base64 in old porter bundles should be categorized as files") 73 } 74 75 func TestExtendedBundle_IsInternalParameter(t *testing.T) { 76 bun := NewBundle(bundle.Bundle{ 77 Definitions: definition.Definitions{ 78 "foo": &definition.Schema{ 79 Type: "string", 80 }, 81 "porter-debug": &definition.Schema{ 82 Type: "string", 83 Comment: PorterInternal, 84 }, 85 }, 86 Parameters: map[string]bundle.Parameter{ 87 "foo": { 88 Definition: "foo", 89 }, 90 "baz": { 91 Definition: "baz", 92 }, 93 "porter-debug": { 94 Definition: "porter-debug", 95 }, 96 }, 97 }) 98 99 t.Run("empty bundle", func(t *testing.T) { 100 b := ExtendedBundle{} 101 require.False(t, b.IsInternalParameter("foo")) 102 }) 103 104 t.Run("param does not exist", func(t *testing.T) { 105 require.False(t, bun.IsInternalParameter("bar")) 106 }) 107 108 t.Run("definition does not exist", func(t *testing.T) { 109 require.False(t, bun.IsInternalParameter("baz")) 110 }) 111 112 t.Run("is not internal", func(t *testing.T) { 113 require.False(t, bun.IsInternalParameter("foo")) 114 }) 115 116 t.Run("is internal", func(t *testing.T) { 117 require.True(t, bun.IsInternalParameter("porter-debug")) 118 }) 119 } 120 121 func TestExtendedBundle_IsSensitiveParameter(t *testing.T) { 122 sensitive := true 123 bun := NewBundle(bundle.Bundle{ 124 Definitions: definition.Definitions{ 125 "foo": &definition.Schema{ 126 Type: "string", 127 WriteOnly: &sensitive, 128 }, 129 "porter-debug": &definition.Schema{ 130 Type: "string", 131 Comment: PorterInternal, 132 }, 133 }, 134 Parameters: map[string]bundle.Parameter{ 135 "foo": { 136 Definition: "foo", 137 }, 138 "baz": { 139 Definition: "baz", 140 }, 141 "porter-debug": { 142 Definition: "porter-debug", 143 }, 144 }, 145 }) 146 147 t.Run("empty bundle", func(t *testing.T) { 148 b := ExtendedBundle{} 149 require.False(t, b.IsSensitiveParameter("foo")) 150 }) 151 152 t.Run("param does not exist", func(t *testing.T) { 153 require.False(t, bun.IsSensitiveParameter("bar")) 154 }) 155 156 t.Run("definition does not exist", func(t *testing.T) { 157 require.False(t, bun.IsSensitiveParameter("baz")) 158 }) 159 160 t.Run("is not sensitive", func(t *testing.T) { 161 require.False(t, bun.IsSensitiveParameter("porter-debug")) 162 }) 163 164 t.Run("is sensitive", func(t *testing.T) { 165 require.True(t, bun.IsSensitiveParameter("foo")) 166 }) 167 } 168 169 func TestExtendedBundle_GetReferencedRegistries(t *testing.T) { 170 t.Run("bundle image in different registry", func(t *testing.T) { 171 // Make sure we are looking at the images and the bundle image 172 b := NewBundle(bundle.Bundle{ 173 InvocationImages: []bundle.InvocationImage{ 174 {BaseImage: bundle.BaseImage{Image: "docker.io/example/mybuns:abc123"}}, 175 }, 176 Images: map[string]bundle.Image{ 177 "nginx": {BaseImage: bundle.BaseImage{Image: "quay.io/library/nginx:latest"}}, 178 "redis": {BaseImage: bundle.BaseImage{Image: "quay.io/library/redis:latest"}}, 179 "helm": {BaseImage: bundle.BaseImage{Image: "ghcr.io/library/helm:latest"}}, 180 }, 181 }) 182 183 regs, err := b.GetReferencedRegistries() 184 require.NoError(t, err, "GetReferencedRegistries failed") 185 wantRegs := []string{"docker.io", "ghcr.io", "quay.io"} 186 require.Equal(t, wantRegs, regs, "unexpected registries identified in the bundle") 187 }) 188 189 t.Run("bundle image in same registry", func(t *testing.T) { 190 // Make sure that we don't generate duplicate registry entries 191 b := NewBundle(bundle.Bundle{ 192 InvocationImages: []bundle.InvocationImage{ 193 {BaseImage: bundle.BaseImage{Image: "ghcr.io/example/mybuns:abc123"}}, 194 }, 195 Images: map[string]bundle.Image{ 196 "nginx": {BaseImage: bundle.BaseImage{Image: "quay.io/library/nginx:latest"}}, 197 "redis": {BaseImage: bundle.BaseImage{Image: "quay.io/library/redis:latest"}}, 198 "helm": {BaseImage: bundle.BaseImage{Image: "ghcr.io/library/helm:latest"}}, 199 }, 200 }) 201 202 regs, err := b.GetReferencedRegistries() 203 require.NoError(t, err, "GetReferencedRegistries failed") 204 wantRegs := []string{"ghcr.io", "quay.io"} 205 require.Equal(t, wantRegs, regs, "unexpected registries identified in the bundle") 206 }) 207 } 208 209 func TestValidate(t *testing.T) { 210 testcases := []struct { 211 name string 212 version string 213 strategy porterschema.CheckStrategy 214 hasWarning bool 215 wantErr string 216 }{ 217 {name: "older version", strategy: porterschema.CheckStrategyExact, version: "1.0.0"}, 218 {name: "current version", strategy: porterschema.CheckStrategyExact, version: "1.2.0"}, 219 {name: "unsupported version", strategy: porterschema.CheckStrategyExact, version: "1.3.0", wantErr: "invalid"}, 220 {name: "custom version check strategy", strategy: porterschema.CheckStrategyMajor, version: "1.1.1", hasWarning: true, wantErr: "WARNING"}, 221 } 222 223 cxt := portercontext.NewTestContext(t) 224 defer cxt.Close() 225 226 for _, tc := range testcases { 227 t.Run(tc.name, func(t *testing.T) { 228 b := NewBundle(bundle.Bundle{ 229 SchemaVersion: SchemaVersion(tc.version), 230 InvocationImages: []bundle.InvocationImage{ 231 {BaseImage: bundle.BaseImage{}}, 232 }, 233 }) 234 235 err := b.Validate(cxt.Context, tc.strategy) 236 if tc.wantErr != "" && !tc.hasWarning { 237 require.ErrorContains(t, err, tc.wantErr) 238 return 239 } 240 require.NoError(t, err) 241 require.Contains(t, cxt.GetError(), tc.wantErr) 242 }) 243 } 244 } 245 246 func TestExtendedBundle_ResolveDependencies(t *testing.T) { 247 t.Parallel() 248 249 bun := NewBundle(bundle.Bundle{ 250 Custom: map[string]interface{}{ 251 DependenciesV1ExtensionKey: depsv1ext.Dependencies{ 252 Requires: map[string]depsv1ext.Dependency{ 253 "mysql": { 254 Bundle: "getporter/mysql:5.7", 255 }, 256 "nginx": { 257 Bundle: "localhost:5000/nginx:1.19", 258 }, 259 }, 260 }, 261 }, 262 }) 263 264 eb := ExtendedBundle{} 265 locks, err := eb.ResolveDependencies(bun) 266 require.NoError(t, err) 267 require.Len(t, locks, 2) 268 269 var mysql DependencyLock 270 var nginx DependencyLock 271 for _, lock := range locks { 272 switch lock.Alias { 273 case "mysql": 274 mysql = lock 275 case "nginx": 276 nginx = lock 277 } 278 } 279 280 assert.Equal(t, "getporter/mysql:5.7", mysql.Reference) 281 assert.Equal(t, "localhost:5000/nginx:1.19", nginx.Reference) 282 } 283 284 func TestExtendedBundle_ResolveVersion(t *testing.T) { 285 t.Parallel() 286 287 testcases := []struct { 288 name string 289 dep depsv1ext.Dependency 290 wantVersion string 291 wantError string 292 }{ 293 {name: "pinned version", 294 dep: depsv1ext.Dependency{Bundle: "mysql:5.7"}, 295 wantVersion: "5.7"}, 296 {name: "unimplemented range", 297 dep: depsv1ext.Dependency{Bundle: "mysql", Version: &depsv1ext.DependencyVersion{Ranges: []string{"1 - 1.5"}}}, 298 wantError: "not implemented"}, 299 {name: "default tag to latest", 300 dep: depsv1ext.Dependency{Bundle: "getporterci/porter-test-only-latest"}, 301 wantVersion: "latest"}, 302 {name: "no default tag", 303 dep: depsv1ext.Dependency{Bundle: "getporterci/porter-test-no-default-tag"}, 304 wantError: "no tag was specified"}, 305 {name: "default tag to highest semver", 306 dep: depsv1ext.Dependency{Bundle: "getporterci/porter-test-with-versions", Version: &depsv1ext.DependencyVersion{Ranges: nil, AllowPrereleases: true}}, 307 wantVersion: "v1.3-beta1"}, 308 {name: "default tag to highest semver, explicitly excluding prereleases", 309 dep: depsv1ext.Dependency{Bundle: "getporterci/porter-test-with-versions", Version: &depsv1ext.DependencyVersion{Ranges: nil, AllowPrereleases: false}}, 310 wantVersion: "v1.2"}, 311 {name: "default tag to highest semver, excluding prereleases by default", 312 dep: depsv1ext.Dependency{Bundle: "getporterci/porter-test-with-versions"}, 313 wantVersion: "v1.2"}, 314 } 315 316 for _, tc := range testcases { 317 tc := tc 318 t.Run(tc.name, func(t *testing.T) { 319 t.Parallel() 320 321 eb := ExtendedBundle{} 322 version, err := eb.ResolveVersion("mysql", tc.dep) 323 if tc.wantError != "" { 324 require.Error(t, err, "ResolveVersion should have returned an error") 325 assert.Contains(t, err.Error(), tc.wantError) 326 } else { 327 require.NoError(t, err, "ResolveVersion should not have returned an error") 328 329 assert.Equal(t, tc.wantVersion, version.Tag(), "incorrect version resolved") 330 } 331 }) 332 } 333 }