Skip to content

Commit

Permalink
Validate OS, Arch, Variant and optional labels on rebase
Browse files Browse the repository at this point in the history
For 0.12 and beyond, rebase is now validating the OS, Arch, Variant and io.buildpack labels specific to OS. This can be skipped with the --force flag.

Signed-off-by: Jesse Brown <jabrown85@gmail.com>
  • Loading branch information
jabrown85 committed Mar 30, 2023
1 parent b7f652a commit bb85224
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 2 deletions.
69 changes: 67 additions & 2 deletions rebaser.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (r *Rebaser) Rebase(workingImage imgutil.Image, newBaseImage imgutil.Image,
return RebaseReport{}, fmt.Errorf("incompatible stack: '%s' is not compatible with '%s'", newBaseStackID, appStackID)
}

if err := r.validateRebaseable(workingImage); err != nil {
if err := r.validateRebaseable(workingImage, newBaseImage); err != nil {
return RebaseReport{}, err
}

Expand Down Expand Up @@ -128,7 +128,7 @@ func validateMixins(appImg, newBaseImg imgutil.Image) error {
return nil
}

func (r *Rebaser) validateRebaseable(appImg imgutil.Image) error {
func (r *Rebaser) validateRebaseable(appImg imgutil.Image, newBaseImg imgutil.Image) error {
if r.PlatformAPI.AtLeast("0.12") {
rebaseable, err := appImg.Label(platform.RebaseableLabel)
if err != nil {
Expand All @@ -137,6 +137,71 @@ func (r *Rebaser) validateRebaseable(appImg imgutil.Image) error {
if !r.Force && rebaseable == "false" {
return fmt.Errorf("app image is not marked as rebaseable")
}

// check the OS, architecture, and variant values
// if they are not the same, the image cannot be rebased unless the force flag is set
if !r.Force {
appOS, err := appImg.OS()
if err != nil {
return errors.Wrap(err, "get app image os")
}
newBaseOS, err := newBaseImg.OS()
if err != nil {
return errors.Wrap(err, "get new base image os")
}
if appOS != newBaseOS {
return fmt.Errorf("incompatible os: '%s' is not compatible with '%s'", newBaseOS, appOS)
}

appArch, err := appImg.Architecture()
if err != nil {
return errors.Wrap(err, "get app image architecture")
}
newBaseArch, err := newBaseImg.Architecture()
if err != nil {
return errors.Wrap(err, "get new base image architecture")
}
if appArch != newBaseArch {
return fmt.Errorf("incompatible architecture: '%s' is not compatible with '%s'", newBaseArch, appArch)
}

appVariant, err := appImg.Variant()
if err != nil {
return errors.Wrap(err, "get app image variant")
}
newBaseVariant, err := newBaseImg.Variant()
if err != nil {
return errors.Wrap(err, "get new base image variant")
}
if appVariant != newBaseVariant {
return fmt.Errorf("incompatible variant: '%s' is not compatible with '%s'", newBaseVariant, appVariant)
}

// check optional labels OSDistributionName and OSDistributionVersion
distroName, err := appImg.Label(platform.OSDistributionNameLabel)
if err != nil {
return errors.Wrap(err, "get app image os distribution name")
}
newBaseDistroName, err := newBaseImg.Label(platform.OSDistributionNameLabel)
if err != nil {
return errors.Wrap(err, "get new base image os distribution name")
}
if distroName != newBaseDistroName {
return fmt.Errorf("incompatible io.buildpacks.distribution.name: '%s' is not compatible with '%s'", newBaseDistroName, distroName)
}

distroVersion, err := appImg.Label(platform.OSDistributionVersionLabel)
if err != nil {
return errors.Wrap(err, "get app image os distribution version")
}
newBaseDistroVersion, err := newBaseImg.Label(platform.OSDistributionVersionLabel)
if err != nil {
return errors.Wrap(err, "get new base image os distribution version")
}
if distroVersion != newBaseDistroVersion {
return fmt.Errorf("incompatible io.buildpacks.distribution.version: '%s' is not compatible with '%s'", newBaseDistroVersion, distroVersion)
}
}
}
return nil
}
Expand Down
46 changes: 46 additions & 0 deletions rebaser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,52 @@ func testRebaser(t *testing.T, when spec.G, it spec.S) {
})

when("app image and run image are based on different stacks", func() {
when("platform API >= 0.12", func() {
it.Before(func() {
rebaser.PlatformAPI = api.MustParse("0.12")
})

it("returns an error and prevents the rebase from taking place when the os are different", func() {
h.AssertNil(t, fakeAppImage.SetOS("linux"))
h.AssertNil(t, fakeNewBaseImage.SetOS("notlinux"))

_, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames)
h.AssertError(t, err, "incompatible os: 'notlinux' is not compatible with 'linux'")
})

it("returns an error and prevents the rebase from taking place when the architecture are different", func() {
h.AssertNil(t, fakeAppImage.SetArchitecture("amd64"))
h.AssertNil(t, fakeNewBaseImage.SetArchitecture("arm64"))

_, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames)
h.AssertError(t, err, "incompatible architecture: 'arm64' is not compatible with 'amd64'")
})

it("returns an error and prevents the rebase from taking place when the architecture are different", func() {
h.AssertNil(t, fakeAppImage.SetVariant("variant1"))
h.AssertNil(t, fakeNewBaseImage.SetVariant("variant2"))

_, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames)
h.AssertError(t, err, "incompatible variant: 'variant2' is not compatible with 'variant1'")
})

it("returns an error and prevents the rebase from taking place when the io.buildpacks.distribution.name are different", func() {
h.AssertNil(t, fakeAppImage.SetLabel("io.buildpacks.distribution.name", "distro1"))
h.AssertNil(t, fakeNewBaseImage.SetLabel("io.buildpacks.distribution.name", "distro2"))

_, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames)
h.AssertError(t, err, "incompatible io.buildpacks.distribution.name: 'distro2' is not compatible with 'distro1'")
})

it("returns an error and prevents the rebase from taking place when the io.buildpacks.distribution.version are different", func() {
h.AssertNil(t, fakeAppImage.SetLabel("io.buildpacks.distribution.version", "version1"))
h.AssertNil(t, fakeNewBaseImage.SetLabel("io.buildpacks.distribution.version", "version2"))

_, err := rebaser.Rebase(fakeAppImage, fakeNewBaseImage, fakeAppImage.Name(), additionalNames)
h.AssertError(t, err, "incompatible io.buildpacks.distribution.version: 'version2' is not compatible with 'version1'")
})
})

it("returns an error and prevents the rebase from taking place when the stacks are different", func() {
h.AssertNil(t, fakeAppImage.SetLabel(platform.StackIDLabel, "io.buildpacks.stacks.bionic"))
h.AssertNil(t, fakeNewBaseImage.SetLabel(platform.StackIDLabel, "io.buildpacks.stacks.cflinuxfs3"))
Expand Down

0 comments on commit bb85224

Please sign in to comment.