@@ -21,33 +21,44 @@ import (
2121// and kiln-compatible tile structure that can be baked into a .pivotal file.
2222type Baker interface {
2323 Bake (source string ) error
24+ BakeFromLockfile (source string , lockfilePath string ) error
2425 KilnBake (destination string ) error
2526 GetName () string
2627 GetVersion () (string , error )
28+ GetReleaseTarball () (string , error )
2729 SetWriter (w io.Writer )
30+ SetProgressWriter (w io.Writer )
2831}
2932
3033// NewBaker creates a new Baker for transforming imgpkg bundles into BOSH releases.
3134func NewBaker () Baker {
3235 return & baker {
33- writer : io .Discard ,
36+ writer : io .Discard ,
37+ progressWriter : io .Discard ,
3438 }
3539}
3640
3741type baker struct {
3842 metadata models.Metadata
3943 source , destination string
4044 writer io.Writer
45+ progressWriter io.Writer
4146}
4247
4348func (b * baker ) KilnBake (destination string ) error {
49+ if err := b .ensureGitRepo (); err != nil {
50+ return fmt .Errorf ("failed to initialize git repo for kiln bake: %w" , err )
51+ }
52+
53+ b .progress ("Assembling final .pivotal file..." )
4454 cmd := exec .Command ("kiln" ,
4555 "bake" ,
4656 "--skip-fetch" ,
4757 "--output-file" , destination ,
4858 )
4959 cmd .Dir = b .destination
5060 out , err := cmd .CombinedOutput ()
61+ b .log (string (out ))
5162 if err != nil {
5263 b .log ("failed to invoke kiln: " + string (out ))
5364 return err
@@ -56,10 +67,29 @@ func (b *baker) KilnBake(destination string) error {
5667 return nil
5768}
5869
70+ // ensureGitRepo initializes a git repo with an empty commit in the
71+ // generated tile directory so that `kiln bake` (which runs git status
72+ // and git rev-parse HEAD) can operate on it without failing.
73+ func (b * baker ) ensureGitRepo () error {
74+ commands := []* exec.Cmd {
75+ exec .Command ("git" , "init" ),
76+ exec .Command ("git" , "commit" , "--allow-empty" , "-m" , "carvel tile build" ),
77+ }
78+ for _ , cmd := range commands {
79+ cmd .Dir = b .destination
80+ out , err := cmd .CombinedOutput ()
81+ if err != nil {
82+ return fmt .Errorf ("command %q failed: %s: %w" , cmd .String (), string (out ), err )
83+ }
84+ }
85+ return nil
86+ }
87+
5988func (b * baker ) Bake (source string ) error {
6089 b .source = source
61- b .destination = path .Join (source , ".ezbake " )
90+ b .destination = path .Join (source , ".carvel-tile " )
6291
92+ b .progress ("Reading tile metadata from " + path .Join (source , "base.yml" ))
6393 yamlPath := path .Join (source , "base.yml" )
6494 yamlData , err := os .ReadFile (yamlPath )
6595 if err != nil {
@@ -71,10 +101,11 @@ func (b *baker) Bake(source string) error {
71101 return err
72102 }
73103
74- _ , err = b .GetVersion ()
104+ ver , err : = b .GetVersion ()
75105 if err != nil {
76106 return err
77107 }
108+ b .progress (fmt .Sprintf ("Tile: %s version %s (metadata_version %s)" , b .metadata .Name , ver , b .metadata .MetadataVersion ))
78109
79110 metadataVersion , err := version .NewVersion (b .metadata .MetadataVersion )
80111 if err != nil {
@@ -85,12 +116,14 @@ func (b *baker) Bake(source string) error {
85116 return errors .New ("tile metadata_version too old for kubernetes support (must be >=3.2.0)" )
86117 }
87118
119+ b .progress ("Generating BOSH release structure..." )
88120 err = b .generateBoshReleaseDir ()
89121 if err != nil {
90122 b .log (err .Error ())
91123 return err
92124 }
93125
126+ b .progress ("Generating tile layout in " + b .destination )
94127 err = b .generateOutputTile ()
95128 if err != nil {
96129 b .log (err .Error ())
@@ -100,6 +133,100 @@ func (b *baker) Bake(source string) error {
100133 return nil
101134}
102135
136+ func (b * baker ) BakeFromLockfile (source string , lockfilePath string ) error {
137+ b .source = source
138+ b .destination = path .Join (source , ".carvel-tile" )
139+
140+ b .progress ("Reading tile metadata from " + path .Join (source , "base.yml" ))
141+ yamlPath := path .Join (source , "base.yml" )
142+ yamlData , err := os .ReadFile (yamlPath )
143+ if err != nil {
144+ return err
145+ }
146+
147+ err = yaml .Unmarshal (yamlData , & b .metadata )
148+ if err != nil {
149+ return err
150+ }
151+
152+ ver , err := b .GetVersion ()
153+ if err != nil {
154+ return err
155+ }
156+ b .progress (fmt .Sprintf ("Tile: %s version %s (metadata_version %s)" , b .metadata .Name , ver , b .metadata .MetadataVersion ))
157+
158+ b .progress ("Reading lockfile from " + lockfilePath )
159+ lf , err := models .ReadCarvelLockfile (lockfilePath )
160+ if err != nil {
161+ return fmt .Errorf ("failed to read lockfile: %w" , err )
162+ }
163+
164+ if lf .Release .Name != b .metadata .Name {
165+ return fmt .Errorf ("lockfile release name %q does not match tile name %q" , lf .Release .Name , b .metadata .Name )
166+ }
167+
168+ err = os .RemoveAll (b .destination )
169+ if err != nil {
170+ return err
171+ }
172+ err = os .MkdirAll (b .destination , 0755 )
173+ if err != nil {
174+ return err
175+ }
176+
177+ b .progress ("Generating tile layout in " + b .destination )
178+ err = b .generateBaseYaml ()
179+ if err != nil {
180+ return err
181+ }
182+ err = b .copyFiles ()
183+ if err != nil {
184+ return err
185+ }
186+ err = b .generateJobFiles ()
187+ if err != nil {
188+ return err
189+ }
190+ err = b .generateInstanceGroupFiles ()
191+ if err != nil {
192+ return err
193+ }
194+ err = b .generateRuntimeConfigs ()
195+ if err != nil {
196+ return err
197+ }
198+
199+ releasesDir := path .Join (b .destination , "releases" )
200+ err = os .MkdirAll (releasesDir , 0755 )
201+ if err != nil {
202+ return err
203+ }
204+
205+ cachedTarball := lf .Release .RemotePath
206+ destTarball := path .Join (releasesDir , b .metadata .Name + "-" + ver + ".tgz" )
207+
208+ b .progress ("Copying cached BOSH release from " + cachedTarball )
209+ b .log ("copying cached BOSH release from " + cachedTarball )
210+ err = copyFileContents (cachedTarball , destTarball )
211+ if err != nil {
212+ return fmt .Errorf ("failed to copy cached release tarball: %w" , err )
213+ }
214+
215+ return nil
216+ }
217+
218+ func (b * baker ) GetReleaseTarball () (string , error ) {
219+ ver , err := b .GetVersion ()
220+ if err != nil {
221+ return "" , err
222+ }
223+ tarball := path .Join (b .destination , "releases" , b .metadata .Name + "-" + ver + ".tgz" )
224+ if _ , err := os .Stat (tarball ); err != nil {
225+ return "" , fmt .Errorf ("release tarball not found at %s: %w" , tarball , err )
226+ }
227+ return tarball , nil
228+ }
229+
103230func (b * baker ) GetName () string {
104231 return b .metadata .Name
105232}
@@ -122,18 +249,26 @@ func (b *baker) SetWriter(w io.Writer) {
122249 b .writer = w
123250}
124251
252+ func (b * baker ) SetProgressWriter (w io.Writer ) {
253+ b .progressWriter = w
254+ }
255+
125256func (b * baker ) log (message string ) {
126257 _ , _ = fmt .Fprintln (b .writer , message )
127258}
128259
260+ func (b * baker ) progress (message string ) {
261+ _ , _ = fmt .Fprintln (b .progressWriter , message )
262+ }
263+
129264func (b * baker ) generateBoshReleaseDir () error {
130265 dirName := path .Join (b .source , ".boshrelease" )
131- // first clean out any previous bosh release directory
132266 err := os .RemoveAll (dirName )
133267 if err != nil {
134268 return err
135269 }
136270
271+ b .progress (" Initializing BOSH release" )
137272 commands := []* exec.Cmd {
138273 exec .Command ("bosh" , "init-release" , "--dir=" + dirName ),
139274 exec .Command ("bosh" , "add-blob" , "--dir=" + dirName , path .Join (b .source , "bundle.tar" ), "imgpkg/bundle.tar" ),
@@ -173,12 +308,13 @@ files:
173308 registryDataTemplates := ""
174309 registryDataProperties := ""
175310
176- // we need one PackageInstall YAML manifest for each entry in the metadata.
311+ b . progress ( " Configuring package installs" )
177312 for _ , entry := range b .metadata .PackageInstalls {
178313 entry = strings .Trim (entry , "$() " )
179314 entry = strings .TrimPrefix (entry , "package" )
180315 entry = strings .Trim (entry , `"' ` )
181316
317+ b .progress (" - " + entry )
182318 b .log ("looking for package install: " + entry )
183319
184320 // find this entry in the packageinstalls directory
@@ -320,8 +456,6 @@ spec:
320456}
321457
322458func (b * baker ) generateOutputTile () error {
323- // first clean out any previous tile directory
324- // Note: this directory should only ever contain generated files, which we are about to regenerate.
325459 err := os .RemoveAll (b .destination )
326460 if err != nil {
327461 return err
@@ -332,11 +466,13 @@ func (b *baker) generateOutputTile() error {
332466 return err
333467 }
334468
469+ b .progress (" Generating base.yml" )
335470 err = b .generateBaseYaml ()
336471 if err != nil {
337472 return err
338473 }
339474
475+ b .progress (" Copying forms, properties, and static assets" )
340476 err = b .copyFiles ()
341477 if err != nil {
342478 return err
@@ -352,11 +488,13 @@ func (b *baker) generateOutputTile() error {
352488 return err
353489 }
354490
491+ b .progress (" Generating runtime configs" )
355492 err = b .generateRuntimeConfigs ()
356493 if err != nil {
357494 return err
358495 }
359496
497+ b .progress (" Creating BOSH release tarball (this may take a while)..." )
360498 err = b .createBoshRelease ()
361499 if err != nil {
362500 return err
0 commit comments