github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/commands/extension_package_test.go (about) 1 package commands_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "testing" 7 8 "github.com/heroku/color" 9 "github.com/pkg/errors" 10 "github.com/sclevine/spec" 11 "github.com/sclevine/spec/report" 12 "github.com/spf13/cobra" 13 14 pubbldpkg "github.com/buildpacks/pack/buildpackage" 15 "github.com/buildpacks/pack/internal/commands" 16 "github.com/buildpacks/pack/internal/commands/fakes" 17 "github.com/buildpacks/pack/internal/config" 18 "github.com/buildpacks/pack/pkg/dist" 19 "github.com/buildpacks/pack/pkg/image" 20 "github.com/buildpacks/pack/pkg/logging" 21 h "github.com/buildpacks/pack/testhelpers" 22 ) 23 24 func TestExtensionPackageCommand(t *testing.T) { 25 color.Disable(true) 26 defer color.Disable(false) 27 spec.Run(t, "ExtensionPackageCommand", testExtensionPackageCommand, spec.Parallel(), spec.Report(report.Terminal{})) 28 } 29 30 func testExtensionPackageCommand(t *testing.T, when spec.G, it spec.S) { 31 var ( 32 logger *logging.LogWithWriters 33 outBuf bytes.Buffer 34 ) 35 36 it.Before(func() { 37 logger = logging.NewLogWithWriters(&outBuf, &outBuf) 38 }) 39 40 when("Package#Execute", func() { 41 var fakeExtensionPackager *fakes.FakeBuildpackPackager 42 43 it.Before(func() { 44 fakeExtensionPackager = &fakes.FakeBuildpackPackager{} 45 }) 46 47 when("valid package config", func() { 48 it("reads package config from the configured path", func() { 49 fakePackageConfigReader := fakes.NewFakePackageConfigReader() 50 expectedPackageConfigPath := "/path/to/some/file" 51 52 cmd := packageExtensionCommand( 53 withExtensionPackageConfigReader(fakePackageConfigReader), 54 withExtensionPackageConfigPath(expectedPackageConfigPath), 55 ) 56 err := cmd.Execute() 57 h.AssertNil(t, err) 58 59 h.AssertEq(t, fakePackageConfigReader.ReadCalledWithArg, expectedPackageConfigPath) 60 }) 61 62 it("creates package with correct image name", func() { 63 cmd := packageExtensionCommand( 64 withExtensionImageName("my-specific-image"), 65 withExtensionPackager(fakeExtensionPackager), 66 ) 67 err := cmd.Execute() 68 h.AssertNil(t, err) 69 70 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 71 h.AssertEq(t, receivedOptions.Name, "my-specific-image") 72 }) 73 74 it("creates package with config returned by the reader", func() { 75 myConfig := pubbldpkg.Config{ 76 Extension: dist.BuildpackURI{URI: "test"}, 77 } 78 79 cmd := packageExtensionCommand( 80 withExtensionPackager(fakeExtensionPackager), 81 withExtensionPackageConfigReader(fakes.NewFakePackageConfigReader(whereReadReturns(myConfig, nil))), 82 ) 83 err := cmd.Execute() 84 h.AssertNil(t, err) 85 86 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 87 h.AssertEq(t, receivedOptions.Config, myConfig) 88 }) 89 90 when("file format", func() { 91 when("extension is .cnb", func() { 92 it("does not modify the name", func() { 93 cmd := packageExtensionCommand(withExtensionPackager(fakeExtensionPackager)) 94 cmd.SetArgs([]string{"test.cnb", "-f", "file"}) 95 h.AssertNil(t, cmd.Execute()) 96 97 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 98 h.AssertEq(t, receivedOptions.Name, "test.cnb") 99 }) 100 }) 101 when("extension is empty", func() { 102 it("appends .cnb to the name", func() { 103 cmd := packageExtensionCommand(withExtensionPackager(fakeExtensionPackager)) 104 cmd.SetArgs([]string{"test", "-f", "file"}) 105 h.AssertNil(t, cmd.Execute()) 106 107 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 108 h.AssertEq(t, receivedOptions.Name, "test.cnb") 109 }) 110 }) 111 when("extension is something other than .cnb", func() { 112 it("does not modify the name but shows a warning", func() { 113 cmd := packageExtensionCommand(withExtensionPackager(fakeExtensionPackager), withExtensionLogger(logger)) 114 cmd.SetArgs([]string{"test.tar.gz", "-f", "file"}) 115 h.AssertNil(t, cmd.Execute()) 116 117 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 118 h.AssertEq(t, receivedOptions.Name, "test.tar.gz") 119 h.AssertContains(t, outBuf.String(), "'.gz' is not a valid extension for a packaged extension. Packaged extensions must have a '.cnb' extension") 120 }) 121 }) 122 }) 123 124 when("pull-policy", func() { 125 var pullPolicyArgs = []string{ 126 "some-image-name", 127 "--config", "/path/to/some/file", 128 "--pull-policy", 129 } 130 131 it("pull-policy=never sets policy", func() { 132 cmd := packageExtensionCommand(withExtensionPackager(fakeExtensionPackager)) 133 cmd.SetArgs(append(pullPolicyArgs, "never")) 134 h.AssertNil(t, cmd.Execute()) 135 136 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 137 h.AssertEq(t, receivedOptions.PullPolicy, image.PullNever) 138 }) 139 140 it("pull-policy=always sets policy", func() { 141 cmd := packageExtensionCommand(withExtensionPackager(fakeExtensionPackager)) 142 cmd.SetArgs(append(pullPolicyArgs, "always")) 143 h.AssertNil(t, cmd.Execute()) 144 145 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 146 h.AssertEq(t, receivedOptions.PullPolicy, image.PullAlways) 147 }) 148 }) 149 when("no --pull-policy", func() { 150 var pullPolicyArgs = []string{ 151 "some-image-name", 152 "--config", "/path/to/some/file", 153 } 154 155 it("uses the default policy when no policy configured", func() { 156 cmd := packageExtensionCommand(withExtensionPackager(fakeExtensionPackager)) 157 cmd.SetArgs(pullPolicyArgs) 158 h.AssertNil(t, cmd.Execute()) 159 160 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 161 h.AssertEq(t, receivedOptions.PullPolicy, image.PullAlways) 162 }) 163 it("uses the configured pull policy when policy configured", func() { 164 cmd := packageExtensionCommand( 165 withExtensionPackager(fakeExtensionPackager), 166 withExtensionClientConfig(config.Config{PullPolicy: "never"}), 167 ) 168 169 cmd.SetArgs([]string{ 170 "some-image-name", 171 "--config", "/path/to/some/file", 172 }) 173 174 err := cmd.Execute() 175 h.AssertNil(t, err) 176 177 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 178 h.AssertEq(t, receivedOptions.PullPolicy, image.PullNever) 179 }) 180 }) 181 }) 182 183 when("no config path is specified", func() { 184 when("no path is specified", func() { 185 it("creates a default config with the uri set to the current working directory", func() { 186 cmd := packageExtensionCommand(withExtensionPackager(fakeExtensionPackager)) 187 cmd.SetArgs([]string{"some-name"}) 188 h.AssertNil(t, cmd.Execute()) 189 190 receivedOptions := fakeExtensionPackager.CreateCalledWithOptions 191 h.AssertEq(t, receivedOptions.Config.Extension.URI, ".") 192 }) 193 }) 194 }) 195 }) 196 197 when("invalid flags", func() { 198 when("both --publish and --pull-policy never flags are specified", func() { 199 it("errors with a descriptive message", func() { 200 cmd := packageExtensionCommand() 201 cmd.SetArgs([]string{ 202 "some-image-name", "--config", "/path/to/some/file", 203 "--publish", 204 "--pull-policy", "never", 205 }) 206 207 err := cmd.Execute() 208 h.AssertNotNil(t, err) 209 h.AssertError(t, err, "--publish and --pull-policy=never cannot be used together. The --publish flag requires the use of remote images.") 210 }) 211 }) 212 213 it("logs an error and exits when package toml is invalid", func() { 214 expectedErr := errors.New("it went wrong") 215 216 cmd := packageExtensionCommand( 217 withExtensionLogger(logger), 218 withExtensionPackageConfigReader( 219 fakes.NewFakePackageConfigReader(whereReadReturns(pubbldpkg.Config{}, expectedErr)), 220 ), 221 ) 222 223 err := cmd.Execute() 224 h.AssertNotNil(t, err) 225 226 h.AssertContains(t, outBuf.String(), fmt.Sprintf("ERROR: reading config: %s", expectedErr)) 227 }) 228 229 when("package-config is specified", func() { 230 it("errors with a descriptive message", func() { 231 cmd := packageExtensionCommand() 232 cmd.SetArgs([]string{"some-name", "--package-config", "some-path"}) 233 234 err := cmd.Execute() 235 h.AssertError(t, err, "unknown flag: --package-config") 236 }) 237 }) 238 239 when("--pull-policy unknown-policy", func() { 240 it("fails to run", func() { 241 cmd := packageExtensionCommand() 242 cmd.SetArgs([]string{ 243 "some-image-name", 244 "--config", "/path/to/some/file", 245 "--pull-policy", 246 "unknown-policy", 247 }) 248 249 h.AssertError(t, cmd.Execute(), "parsing pull policy") 250 }) 251 }) 252 }) 253 } 254 255 type packageExtensionCommandConfig struct { 256 logger *logging.LogWithWriters 257 packageConfigReader *fakes.FakePackageConfigReader 258 extensionPackager *fakes.FakeBuildpackPackager 259 clientConfig config.Config 260 imageName string 261 configPath string 262 } 263 264 type packageExtensionCommandOption func(config *packageExtensionCommandConfig) 265 266 func packageExtensionCommand(ops ...packageExtensionCommandOption) *cobra.Command { 267 config := &packageExtensionCommandConfig{ 268 logger: logging.NewLogWithWriters(&bytes.Buffer{}, &bytes.Buffer{}), 269 packageConfigReader: fakes.NewFakePackageConfigReader(), 270 extensionPackager: &fakes.FakeBuildpackPackager{}, 271 clientConfig: config.Config{}, 272 imageName: "some-image-name", 273 configPath: "/path/to/some/file", 274 } 275 276 for _, op := range ops { 277 op(config) 278 } 279 280 cmd := commands.ExtensionPackage(config.logger, config.clientConfig, config.extensionPackager, config.packageConfigReader) 281 cmd.SetArgs([]string{config.imageName, "--config", config.configPath}) 282 283 return cmd 284 } 285 286 func withExtensionLogger(logger *logging.LogWithWriters) packageExtensionCommandOption { 287 return func(config *packageExtensionCommandConfig) { 288 config.logger = logger 289 } 290 } 291 292 func withExtensionPackageConfigReader(reader *fakes.FakePackageConfigReader) packageExtensionCommandOption { 293 return func(config *packageExtensionCommandConfig) { 294 config.packageConfigReader = reader 295 } 296 } 297 298 func withExtensionPackager(creator *fakes.FakeBuildpackPackager) packageExtensionCommandOption { 299 return func(config *packageExtensionCommandConfig) { 300 config.extensionPackager = creator 301 } 302 } 303 304 func withExtensionImageName(name string) packageExtensionCommandOption { 305 return func(config *packageExtensionCommandConfig) { 306 config.imageName = name 307 } 308 } 309 310 func withExtensionPackageConfigPath(path string) packageExtensionCommandOption { 311 return func(config *packageExtensionCommandConfig) { 312 config.configPath = path 313 } 314 } 315 316 func withExtensionClientConfig(clientCfg config.Config) packageExtensionCommandOption { 317 return func(config *packageExtensionCommandConfig) { 318 config.clientConfig = clientCfg 319 } 320 }