Skip to content

Commit

Permalink
feat: skip install tools cmd (#741)
Browse files Browse the repository at this point in the history
Signed-off-by: Miaha Cybersec <[email protected]>
Signed-off-by: Miaha <[email protected]>
Co-authored-by: Ashna Mehrotra <[email protected]>
  • Loading branch information
MiahaCybersec and ashnamehrotra authored Aug 30, 2024
1 parent 5f61ade commit b827cb9
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 10 deletions.
14 changes: 14 additions & 0 deletions pkg/patch/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,20 @@ func patchWithContext(ctx context.Context, ch chan error, image, reportFile, pat
return err
}

if solveOpt.SourcePolicy != nil {
switch {
case strings.Contains(solveOpt.SourcePolicy.Rules[0].Updates.Identifier, "redhat"):
err = errors.New("RedHat is not supported via source policies due to BusyBox not being in the RHEL repos\n" +
"Please use a different RPM-based image")
return err

case strings.Contains(solveOpt.SourcePolicy.Rules[0].Updates.Identifier, "rockylinux"):
err = errors.New("RockyLinux is not supported via source policies due to BusyBox not being in the RockyLinux repos\n" +
"Please use a different RPM-based image")
return err
}
}

buildChannel := make(chan *client.SolveStatus)
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
Expand Down
60 changes: 56 additions & 4 deletions pkg/pkgmgr/rpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const (
rpmManifest2 = "container-manifest-2"
rpmManifestWildcard = "container-manifest-*"

installToolsCmd = "tdnf install busybox cpio dnf-utils -y"
resultQueryFormat = "%{NAME}\t%{VERSION}-%{RELEASE}\t%{ARCH}\n"
)

Expand Down Expand Up @@ -255,14 +254,21 @@ func (rm *rpmManager) probeRPMStatus(ctx context.Context, toolImage string) erro
llb.ResolveModeDefault,
)

// List all packages installed in the tooling image
toolsListed := toolingBase.Run(llb.Shlex(`sh -c 'ls /usr/bin > applications.txt'`)).Root()
installToolsCmd, err := rm.generateToolInstallCmd(ctx, &toolsListed)
if err != nil {
return err
}

packageManagers := []string{"tdnf", "dnf", "microdnf", "yum", "rpm"}

toolsInstalled := toolingBase.Run(llb.Shlex(installToolsCmd), llb.WithProxy(utils.GetProxy())).Root()
toolsApplied := rm.config.ImageState.File(llb.Copy(toolsInstalled, "/usr/sbin/busybox", "/usr/sbin/busybox"))
mkFolders := toolsApplied.
File(llb.Mkdir(resultsPath, 0o744, llb.WithParents(true))).
File(llb.Mkdir(inputPath, 0o744, llb.WithParents(true)))

toolList := []string{"dnf", "microdnf", "rpm", "yum"}

rpmDBList := []string{
filepath.Join(rpmLibPath, rpmBDB),
filepath.Join(rpmLibPath, rpmNDB),
Expand All @@ -274,7 +280,7 @@ func (rm *rpmManager) probeRPMStatus(ctx context.Context, toolImage string) erro
toolListPath := filepath.Join(inputPath, "tool_list")
dbListPath := filepath.Join(inputPath, "rpm_db_list")

probed := buildkit.WithArrayFile(&mkFolders, toolListPath, toolList)
probed := buildkit.WithArrayFile(&mkFolders, toolListPath, packageManagers)
probed = buildkit.WithArrayFile(&probed, dbListPath, rpmDBList)
outState := probed.Run(
llb.AddEnv("TOOL_LIST_PATH", toolListPath),
Expand Down Expand Up @@ -361,6 +367,45 @@ func (rm *rpmManager) probeRPMStatus(ctx context.Context, toolImage string) erro
return nil
}

func (rm *rpmManager) generateToolInstallCmd(ctx context.Context, toolsListed *llb.State) (string, error) {
applicationsList, err := buildkit.ExtractFileFromState(ctx, rm.config.Client, toolsListed, "/applications.txt")
if err != nil {
return "", err
}

// packageManagersInstalled is the package manager(s) available within the tooling image
// RPM must be excluded from this list as it cannot connect to RPM repos
var packageManagersInstalled []string
packageManagerList := []string{"tdnf", "dnf", "microdnf", "yum"}

for _, packageManager := range packageManagerList {
if strings.Contains(string(applicationsList), packageManager) {
packageManagersInstalled = append(packageManagersInstalled, packageManager)
}
}

// missingTools indicates which tools, if any, need to be installed within the tooling image
var missingTools []string
requiredToolingList := []string{"busybox", "dnf-utils", "cpio"}

for _, tool := range requiredToolingList {
isMissingTool := !strings.Contains(string(applicationsList), tool)
if isMissingTool {
missingTools = append(missingTools, tool)
}

if tool == "cpio" && !isMissingTool && strings.Contains(string(applicationsList), "rpm2cpio") {
missingTools = append(missingTools, "cpio")
}
}

// A tooling image could contain multiple package managers
// Choose the first one detected to use in the installation command
installCmd := fmt.Sprintf("%s install %s -y", packageManagersInstalled[0], strings.Join(missingTools, " "))

return installCmd, nil
}

func parseManifestFile(file string) (map[string]string, error) {
// split into lines
file = strings.TrimSuffix(file, "\n")
Expand Down Expand Up @@ -496,6 +541,13 @@ func (rm *rpmManager) unpackAndMergeUpdates(ctx context.Context, updates unversi
llb.ResolveModeDefault,
)

// List all packages installed in the tooling image
toolsListed := toolingBase.Run(llb.Shlex(`sh -c 'ls /usr/bin > applications.txt'`)).Root()
installToolsCmd, err := rm.generateToolInstallCmd(ctx, &toolsListed)
if err != nil {
return nil, nil, err
}

// Install busybox. This should reuse the layer cached from probeRPMStatus.
toolsInstalled := toolingBase.Run(llb.Shlex(installToolsCmd), llb.WithProxy(utils.GetProxy())).Root()
busyboxCopied := toolsInstalled.Dir(downloadPath).Run(llb.Shlex("cp /usr/sbin/busybox .")).Root()
Expand Down
14 changes: 8 additions & 6 deletions pkg/pkgmgr/rpm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,8 @@ func Test_installUpdates_RPM(t *testing.T) {
}

func Test_unpackAndMergeUpdates_RPM(t *testing.T) {
// Due to the generateToolInstallCmd function, we need to pass in a package manager as well
// Without a package manager passed in, these tests all fail
tests := []struct {
name string
updates unversioned.UpdatePackages
Expand All @@ -602,7 +604,7 @@ func Test_unpackAndMergeUpdates_RPM(t *testing.T) {
{
name: "Successful update with specific packages",
mockSetup: func(mr *mocks.MockReference) {
mr.On("ReadFile", mock.Anything, mock.Anything).Return([]byte("package1\t1.2.3\tx86_64\npackage2\t2.3.4\tx86_64"), nil)
mr.On("ReadFile", mock.Anything, mock.Anything).Return([]byte("package1\t1.2.3\tx86_64\npackage2\t2.3.4\tx86_64\ntdnf"), nil)
},
updates: unversioned.UpdatePackages{
{Name: "package1", FixedVersion: "1.2.3"},
Expand All @@ -611,31 +613,31 @@ func Test_unpackAndMergeUpdates_RPM(t *testing.T) {
toolImage: "test-tool-image:latest",
ignoreErrors: false,
expectedError: false,
expectedResult: []byte("package1\t1.2.3\tx86_64\npackage2\t2.3.4\tx86_64"),
expectedResult: []byte("package1\t1.2.3\tx86_64\npackage2\t2.3.4\tx86_64\ntdnf"),
},
{
name: "Successful update all packages",
mockSetup: func(mr *mocks.MockReference) {
mr.On("ReadFile", mock.Anything, mock.Anything).Return([]byte(nil), nil)
mr.On("ReadFile", mock.Anything, mock.Anything).Return([]byte("tdnf"), nil)
},
updates: nil,
toolImage: "test-tool-image:latest",
ignoreErrors: false,
expectedResult: nil,
expectedResult: []byte("tdnf"),
expectedError: false,
},
{
name: "Ignore errors during update",
mockSetup: func(mr *mocks.MockReference) {
mr.On("ReadFile", mock.Anything, mock.Anything).Return([]byte("package1\t1.0.1\n"), nil)
mr.On("ReadFile", mock.Anything, mock.Anything).Return([]byte("package1\t1.0.1\ntdnf"), nil)
},
updates: unversioned.UpdatePackages{
{Name: "package1", FixedVersion: "2.0.0"},
},
toolImage: "test-tool-image:latest",
ignoreErrors: true,
expectedError: false,
expectedResult: []byte("package1\t1.0.1\n"),
expectedResult: []byte("package1\t1.0.1\ntdnf"),
},
}

Expand Down

0 comments on commit b827cb9

Please sign in to comment.