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