github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/inspectimage/writer/yaml_test.go (about)

     1  package writer_test
     2  
     3  import (
     4  	"bytes"
     5  	"testing"
     6  
     7  	"github.com/buildpacks/lifecycle/buildpack"
     8  	"github.com/buildpacks/lifecycle/launch"
     9  	"github.com/buildpacks/lifecycle/platform/files"
    10  	"github.com/heroku/color"
    11  	"github.com/sclevine/spec"
    12  	"github.com/sclevine/spec/report"
    13  
    14  	"github.com/buildpacks/pack/internal/config"
    15  	"github.com/buildpacks/pack/internal/inspectimage"
    16  	"github.com/buildpacks/pack/internal/inspectimage/writer"
    17  	"github.com/buildpacks/pack/pkg/client"
    18  	"github.com/buildpacks/pack/pkg/logging"
    19  	h "github.com/buildpacks/pack/testhelpers"
    20  )
    21  
    22  func TestYAML(t *testing.T) {
    23  	color.Disable(true)
    24  	defer color.Disable(false)
    25  	spec.Run(t, "YAML Writer", testYAML, spec.Parallel(), spec.Report(report.Terminal{}))
    26  }
    27  
    28  func testYAML(t *testing.T, when spec.G, it spec.S) {
    29  	var (
    30  		assert = h.NewAssertionManager(t)
    31  		outBuf bytes.Buffer
    32  
    33  		remoteInfo            *client.ImageInfo
    34  		remoteInfoNoRebasable *client.ImageInfo
    35  		localInfo             *client.ImageInfo
    36  		localInfoNoRebasable  *client.ImageInfo
    37  
    38  		expectedLocalOutput = `---
    39  local_info:
    40    stack: test.stack.id.local
    41    rebasable: true
    42    base_image:
    43      top_layer: some-local-top-layer
    44      reference: some-local-run-image-reference
    45    run_images:
    46    - name: user-configured-mirror-for-local
    47      user_configured: true
    48    - name: some-local-run-image
    49    - name: some-local-mirror
    50    - name: other-local-mirror
    51    buildpacks:
    52    - homepage: https://some-homepage-one
    53      id: test.bp.one.local
    54      version: 1.0.0
    55    - homepage: https://some-homepage-two
    56      id: test.bp.two.local
    57      version: 2.0.0
    58    extensions: []
    59    processes:
    60    - type: some-local-type
    61      shell: bash
    62      command: "/some/local command"
    63      default: true
    64      args:
    65      - some
    66      - local
    67      - args
    68      working-dir: /some-test-work-dir
    69    - type: other-local-type
    70      shell: ''
    71      command: "/other/local/command"
    72      default: false
    73      args:
    74      - other
    75      - local
    76      - args
    77      working-dir: /other-test-work-dir
    78  `
    79  		expectedLocalNoRebasableOutput = `---
    80  local_info:
    81    stack: test.stack.id.local
    82    rebasable: false
    83    base_image:
    84      top_layer: some-local-top-layer
    85      reference: some-local-run-image-reference
    86    run_images:
    87    - name: user-configured-mirror-for-local
    88      user_configured: true
    89    - name: some-local-run-image
    90    - name: some-local-mirror
    91    - name: other-local-mirror
    92    buildpacks:
    93    - homepage: https://some-homepage-one
    94      id: test.bp.one.local
    95      version: 1.0.0
    96    - homepage: https://some-homepage-two
    97      id: test.bp.two.local
    98      version: 2.0.0
    99    extensions: []
   100    processes:
   101    - type: some-local-type
   102      shell: bash
   103      command: "/some/local command"
   104      default: true
   105      args:
   106      - some
   107      - local
   108      - args
   109      working-dir: /some-test-work-dir
   110    - type: other-local-type
   111      shell: ''
   112      command: "/other/local/command"
   113      default: false
   114      args:
   115      - other
   116      - local
   117      - args
   118      working-dir: /other-test-work-dir
   119  `
   120  		expectedRemoteOutput = `---
   121  remote_info:
   122    stack: test.stack.id.remote
   123    rebasable: true
   124    base_image:
   125      top_layer: some-remote-top-layer
   126      reference: some-remote-run-image-reference
   127    run_images:
   128    - name: user-configured-mirror-for-remote
   129      user_configured: true
   130    - name: some-remote-run-image
   131    - name: some-remote-mirror
   132    - name: other-remote-mirror
   133    buildpacks:
   134    - homepage: https://some-homepage-one
   135      id: test.bp.one.remote
   136      version: 1.0.0
   137    - homepage: https://some-homepage-two
   138      id: test.bp.two.remote
   139      version: 2.0.0
   140    extensions: []
   141    processes:
   142    - type: some-remote-type
   143      shell: bash
   144      command: "/some/remote command"
   145      default: true
   146      args:
   147      - some
   148      - remote
   149      - args
   150      working-dir: /some-test-work-dir
   151    - type: other-remote-type
   152      shell: ''
   153      command: "/other/remote/command"
   154      default: false
   155      args:
   156      - other
   157      - remote
   158      - args
   159      working-dir: /other-test-work-dir
   160  `
   161  		expectedRemoteNoRebasableOutput = `---
   162  remote_info:
   163    stack: test.stack.id.remote
   164    rebasable: false
   165    base_image:
   166      top_layer: some-remote-top-layer
   167      reference: some-remote-run-image-reference
   168    run_images:
   169    - name: user-configured-mirror-for-remote
   170      user_configured: true
   171    - name: some-remote-run-image
   172    - name: some-remote-mirror
   173    - name: other-remote-mirror
   174    buildpacks:
   175    - homepage: https://some-homepage-one
   176      id: test.bp.one.remote
   177      version: 1.0.0
   178    - homepage: https://some-homepage-two
   179      id: test.bp.two.remote
   180      version: 2.0.0
   181    extensions: []
   182    processes:
   183    - type: some-remote-type
   184      shell: bash
   185      command: "/some/remote command"
   186      default: true
   187      args:
   188      - some
   189      - remote
   190      - args
   191      working-dir: /some-test-work-dir
   192    - type: other-remote-type
   193      shell: ''
   194      command: "/other/remote/command"
   195      default: false
   196      args:
   197      - other
   198      - remote
   199      - args
   200      working-dir: /other-test-work-dir
   201  `
   202  	)
   203  
   204  	when("Print", func() {
   205  		it.Before(func() {
   206  			type someData struct {
   207  				String string
   208  				Bool   bool
   209  				Int    int
   210  				Nested struct {
   211  					String string
   212  				}
   213  			}
   214  
   215  			remoteInfo = &client.ImageInfo{
   216  				StackID: "test.stack.id.remote",
   217  				Buildpacks: []buildpack.GroupElement{
   218  					{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"},
   219  					{ID: "test.bp.two.remote", Version: "2.0.0", Homepage: "https://some-homepage-two"},
   220  				},
   221  				Base: files.RunImageForRebase{
   222  					TopLayer:  "some-remote-top-layer",
   223  					Reference: "some-remote-run-image-reference",
   224  				},
   225  				Stack: files.Stack{
   226  					RunImage: files.RunImageForExport{
   227  						Image:   "some-remote-run-image",
   228  						Mirrors: []string{"some-remote-mirror", "other-remote-mirror"},
   229  					},
   230  				},
   231  				BOM: []buildpack.BOMEntry{{
   232  					Require: buildpack.Require{
   233  						Name:    "name-1",
   234  						Version: "version-1",
   235  						Metadata: map[string]interface{}{
   236  							"RemoteData": someData{
   237  								String: "aString",
   238  								Bool:   true,
   239  								Int:    123,
   240  								Nested: struct {
   241  									String string
   242  								}{
   243  									String: "anotherString",
   244  								},
   245  							},
   246  						},
   247  					},
   248  					Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"},
   249  				}},
   250  				Processes: client.ProcessDetails{
   251  					DefaultProcess: &launch.Process{
   252  						Type:             "some-remote-type",
   253  						Command:          launch.RawCommand{Entries: []string{"/some/remote command"}},
   254  						Args:             []string{"some", "remote", "args"},
   255  						Direct:           false,
   256  						WorkingDirectory: "/some-test-work-dir",
   257  					},
   258  					OtherProcesses: []launch.Process{
   259  						{
   260  							Type:             "other-remote-type",
   261  							Command:          launch.RawCommand{Entries: []string{"/other/remote/command"}},
   262  							Args:             []string{"other", "remote", "args"},
   263  							Direct:           true,
   264  							WorkingDirectory: "/other-test-work-dir",
   265  						},
   266  					},
   267  				},
   268  				Rebasable: true,
   269  			}
   270  			remoteInfoNoRebasable = &client.ImageInfo{
   271  				StackID: "test.stack.id.remote",
   272  				Buildpacks: []buildpack.GroupElement{
   273  					{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"},
   274  					{ID: "test.bp.two.remote", Version: "2.0.0", Homepage: "https://some-homepage-two"},
   275  				},
   276  				Base: files.RunImageForRebase{
   277  					TopLayer:  "some-remote-top-layer",
   278  					Reference: "some-remote-run-image-reference",
   279  				},
   280  				Stack: files.Stack{
   281  					RunImage: files.RunImageForExport{
   282  						Image:   "some-remote-run-image",
   283  						Mirrors: []string{"some-remote-mirror", "other-remote-mirror"},
   284  					},
   285  				},
   286  				BOM: []buildpack.BOMEntry{{
   287  					Require: buildpack.Require{
   288  						Name:    "name-1",
   289  						Version: "version-1",
   290  						Metadata: map[string]interface{}{
   291  							"RemoteData": someData{
   292  								String: "aString",
   293  								Bool:   true,
   294  								Int:    123,
   295  								Nested: struct {
   296  									String string
   297  								}{
   298  									String: "anotherString",
   299  								},
   300  							},
   301  						},
   302  					},
   303  					Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"},
   304  				}},
   305  				Processes: client.ProcessDetails{
   306  					DefaultProcess: &launch.Process{
   307  						Type:             "some-remote-type",
   308  						Command:          launch.RawCommand{Entries: []string{"/some/remote command"}},
   309  						Args:             []string{"some", "remote", "args"},
   310  						Direct:           false,
   311  						WorkingDirectory: "/some-test-work-dir",
   312  					},
   313  					OtherProcesses: []launch.Process{
   314  						{
   315  							Type:             "other-remote-type",
   316  							Command:          launch.RawCommand{Entries: []string{"/other/remote/command"}},
   317  							Args:             []string{"other", "remote", "args"},
   318  							Direct:           true,
   319  							WorkingDirectory: "/other-test-work-dir",
   320  						},
   321  					},
   322  				},
   323  				Rebasable: false,
   324  			}
   325  
   326  			localInfo = &client.ImageInfo{
   327  				StackID: "test.stack.id.local",
   328  				Buildpacks: []buildpack.GroupElement{
   329  					{ID: "test.bp.one.local", Version: "1.0.0", Homepage: "https://some-homepage-one"},
   330  					{ID: "test.bp.two.local", Version: "2.0.0", Homepage: "https://some-homepage-two"},
   331  				},
   332  				Base: files.RunImageForRebase{
   333  					TopLayer:  "some-local-top-layer",
   334  					Reference: "some-local-run-image-reference",
   335  				},
   336  				Stack: files.Stack{
   337  					RunImage: files.RunImageForExport{
   338  						Image:   "some-local-run-image",
   339  						Mirrors: []string{"some-local-mirror", "other-local-mirror"},
   340  					},
   341  				},
   342  				BOM: []buildpack.BOMEntry{{
   343  					Require: buildpack.Require{
   344  						Name:    "name-1",
   345  						Version: "version-1",
   346  						Metadata: map[string]interface{}{
   347  							"LocalData": someData{
   348  								Bool: false,
   349  								Int:  456,
   350  							},
   351  						},
   352  					},
   353  					Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"},
   354  				}},
   355  				Processes: client.ProcessDetails{
   356  					DefaultProcess: &launch.Process{
   357  						Type:             "some-local-type",
   358  						Command:          launch.RawCommand{Entries: []string{"/some/local command"}},
   359  						Args:             []string{"some", "local", "args"},
   360  						Direct:           false,
   361  						WorkingDirectory: "/some-test-work-dir",
   362  					},
   363  					OtherProcesses: []launch.Process{
   364  						{
   365  							Type:             "other-local-type",
   366  							Command:          launch.RawCommand{Entries: []string{"/other/local/command"}},
   367  							Args:             []string{"other", "local", "args"},
   368  							Direct:           true,
   369  							WorkingDirectory: "/other-test-work-dir",
   370  						},
   371  					},
   372  				},
   373  				Rebasable: true,
   374  			}
   375  			localInfoNoRebasable = &client.ImageInfo{
   376  				StackID: "test.stack.id.local",
   377  				Buildpacks: []buildpack.GroupElement{
   378  					{ID: "test.bp.one.local", Version: "1.0.0", Homepage: "https://some-homepage-one"},
   379  					{ID: "test.bp.two.local", Version: "2.0.0", Homepage: "https://some-homepage-two"},
   380  				},
   381  				Base: files.RunImageForRebase{
   382  					TopLayer:  "some-local-top-layer",
   383  					Reference: "some-local-run-image-reference",
   384  				},
   385  				Stack: files.Stack{
   386  					RunImage: files.RunImageForExport{
   387  						Image:   "some-local-run-image",
   388  						Mirrors: []string{"some-local-mirror", "other-local-mirror"},
   389  					},
   390  				},
   391  				BOM: []buildpack.BOMEntry{{
   392  					Require: buildpack.Require{
   393  						Name:    "name-1",
   394  						Version: "version-1",
   395  						Metadata: map[string]interface{}{
   396  							"LocalData": someData{
   397  								Bool: false,
   398  								Int:  456,
   399  							},
   400  						},
   401  					},
   402  					Buildpack: buildpack.GroupElement{ID: "test.bp.one.remote", Version: "1.0.0", Homepage: "https://some-homepage-one"},
   403  				}},
   404  				Processes: client.ProcessDetails{
   405  					DefaultProcess: &launch.Process{
   406  						Type:             "some-local-type",
   407  						Command:          launch.RawCommand{Entries: []string{"/some/local command"}},
   408  						Args:             []string{"some", "local", "args"},
   409  						Direct:           false,
   410  						WorkingDirectory: "/some-test-work-dir",
   411  					},
   412  					OtherProcesses: []launch.Process{
   413  						{
   414  							Type:             "other-local-type",
   415  							Command:          launch.RawCommand{Entries: []string{"/other/local/command"}},
   416  							Args:             []string{"other", "local", "args"},
   417  							Direct:           true,
   418  							WorkingDirectory: "/other-test-work-dir",
   419  						},
   420  					},
   421  				},
   422  				Rebasable: false,
   423  			}
   424  
   425  			outBuf = bytes.Buffer{}
   426  		})
   427  
   428  		when("local and remote image exits", func() {
   429  			it("prints both local and remote image info in a YAML format", func() {
   430  				runImageMirrors := []config.RunImage{
   431  					{
   432  						Image:   "un-used-run-image",
   433  						Mirrors: []string{"un-used"},
   434  					},
   435  					{
   436  						Image:   "some-local-run-image",
   437  						Mirrors: []string{"user-configured-mirror-for-local"},
   438  					},
   439  					{
   440  						Image:   "some-remote-run-image",
   441  						Mirrors: []string{"user-configured-mirror-for-remote"},
   442  					},
   443  				}
   444  				sharedImageInfo := inspectimage.GeneralInfo{
   445  					Name:            "test-image",
   446  					RunImageMirrors: runImageMirrors,
   447  				}
   448  				yamlWriter := writer.NewYAML()
   449  
   450  				logger := logging.NewLogWithWriters(&outBuf, &outBuf)
   451  				err := yamlWriter.Print(logger, sharedImageInfo, localInfo, remoteInfo, nil, nil)
   452  				assert.Nil(err)
   453  
   454  				assert.ContainsYAML(outBuf.String(), `"image_name": "test-image"`)
   455  				assert.ContainsYAML(outBuf.String(), expectedLocalOutput)
   456  				assert.ContainsYAML(outBuf.String(), expectedRemoteOutput)
   457  			})
   458  			it("prints both local and remote no rebasable images info in a YAML format", func() {
   459  				runImageMirrors := []config.RunImage{
   460  					{
   461  						Image:   "un-used-run-image",
   462  						Mirrors: []string{"un-used"},
   463  					},
   464  					{
   465  						Image:   "some-local-run-image",
   466  						Mirrors: []string{"user-configured-mirror-for-local"},
   467  					},
   468  					{
   469  						Image:   "some-remote-run-image",
   470  						Mirrors: []string{"user-configured-mirror-for-remote"},
   471  					},
   472  				}
   473  				sharedImageInfo := inspectimage.GeneralInfo{
   474  					Name:            "test-image",
   475  					RunImageMirrors: runImageMirrors,
   476  				}
   477  				yamlWriter := writer.NewYAML()
   478  
   479  				logger := logging.NewLogWithWriters(&outBuf, &outBuf)
   480  				err := yamlWriter.Print(logger, sharedImageInfo, localInfoNoRebasable, remoteInfoNoRebasable, nil, nil)
   481  				assert.Nil(err)
   482  
   483  				assert.ContainsYAML(outBuf.String(), `"image_name": "test-image"`)
   484  				assert.ContainsYAML(outBuf.String(), expectedLocalNoRebasableOutput)
   485  				assert.ContainsYAML(outBuf.String(), expectedRemoteNoRebasableOutput)
   486  			})
   487  		})
   488  
   489  		when("only local image exists", func() {
   490  			it("prints local image info in YAML format", func() {
   491  				runImageMirrors := []config.RunImage{
   492  					{
   493  						Image:   "un-used-run-image",
   494  						Mirrors: []string{"un-used"},
   495  					},
   496  					{
   497  						Image:   "some-local-run-image",
   498  						Mirrors: []string{"user-configured-mirror-for-local"},
   499  					},
   500  					{
   501  						Image:   "some-remote-run-image",
   502  						Mirrors: []string{"user-configured-mirror-for-remote"},
   503  					},
   504  				}
   505  				sharedImageInfo := inspectimage.GeneralInfo{
   506  					Name:            "test-image",
   507  					RunImageMirrors: runImageMirrors,
   508  				}
   509  				yamlWriter := writer.NewYAML()
   510  
   511  				logger := logging.NewLogWithWriters(&outBuf, &outBuf)
   512  				err := yamlWriter.Print(logger, sharedImageInfo, localInfo, nil, nil, nil)
   513  				assert.Nil(err)
   514  
   515  				assert.ContainsYAML(outBuf.String(), `"image_name": "test-image"`)
   516  				assert.ContainsYAML(outBuf.String(), expectedLocalOutput)
   517  				assert.NotContains(outBuf.String(), "test.stack.id.remote")
   518  			})
   519  		})
   520  
   521  		when("only remote image exists", func() {
   522  			it("prints remote image info in YAML format", func() {
   523  				runImageMirrors := []config.RunImage{
   524  					{
   525  						Image:   "un-used-run-image",
   526  						Mirrors: []string{"un-used"},
   527  					},
   528  					{
   529  						Image:   "some-local-run-image",
   530  						Mirrors: []string{"user-configured-mirror-for-local"},
   531  					},
   532  					{
   533  						Image:   "some-remote-run-image",
   534  						Mirrors: []string{"user-configured-mirror-for-remote"},
   535  					},
   536  				}
   537  				sharedImageInfo := inspectimage.GeneralInfo{
   538  					Name:            "test-image",
   539  					RunImageMirrors: runImageMirrors,
   540  				}
   541  				yamlWriter := writer.NewYAML()
   542  
   543  				logger := logging.NewLogWithWriters(&outBuf, &outBuf)
   544  				err := yamlWriter.Print(logger, sharedImageInfo, nil, remoteInfo, nil, nil)
   545  				assert.Nil(err)
   546  
   547  				assert.ContainsYAML(outBuf.String(), `"image_name": "test-image"`)
   548  				assert.NotContains(outBuf.String(), "test.stack.id.local")
   549  				assert.ContainsYAML(outBuf.String(), expectedRemoteOutput)
   550  			})
   551  		})
   552  	})
   553  }