mirror of
https://github.com/drone/drone-kaniko.git
synced 2026-06-13 18:41:36 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3af953651 | |||
| e6ab8aa3c0 | |||
| 113a61b0e1 | |||
| 982c141391 |
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CustomStringSliceFlag is like a regular StringSlice flag but with
|
||||
// semicolon as a delimiter
|
||||
type CustomStringSliceFlag struct {
|
||||
Value []string
|
||||
}
|
||||
|
||||
func (f *CustomStringSliceFlag) GetValue() []string {
|
||||
if f.Value == nil {
|
||||
return make([]string, 0)
|
||||
}
|
||||
return f.Value
|
||||
}
|
||||
|
||||
func (f *CustomStringSliceFlag) String() string {
|
||||
if f.Value == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(f.Value, ";")
|
||||
}
|
||||
|
||||
func (f *CustomStringSliceFlag) Set(v string) error {
|
||||
for _, s := range strings.Split(v, ";") {
|
||||
s = strings.TrimSpace(s)
|
||||
f.Value = append(f.Value, s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+76
-2
@@ -7,6 +7,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
ecrv1 "github.com/aws/aws-sdk-go/service/ecr"
|
||||
ecrpublicv1 "github.com/aws/aws-sdk-go/service/ecrpublic"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
"github.com/aws/smithy-go"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/joho/godotenv"
|
||||
@@ -35,6 +37,7 @@ const (
|
||||
secretKeyEnv string = "AWS_SECRET_ACCESS_KEY"
|
||||
ecrPublicDomain string = "public.ecr.aws"
|
||||
kanikoVersionEnv string = "KANIKO_VERSION"
|
||||
sessionKeyEnv string = "AWS_SESSION_TOKEN"
|
||||
|
||||
oneDotEightVersion string = "1.8.0"
|
||||
defaultDigestFile string = "/kaniko/digest-file"
|
||||
@@ -122,6 +125,17 @@ func main() {
|
||||
Usage: "build args",
|
||||
EnvVar: "PLUGIN_BUILD_ARGS",
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "args-new",
|
||||
Usage: "build args new",
|
||||
EnvVar: "PLUGIN_BUILD_ARGS_NEW",
|
||||
Value: new(CustomStringSliceFlag),
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "plugin-multiple-build-agrs",
|
||||
Usage: "plugin multiple build agrs",
|
||||
EnvVar: "PLUGIN_MULTIPLE_BUILD_ARGS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "target",
|
||||
Usage: "build target",
|
||||
@@ -384,6 +398,11 @@ func main() {
|
||||
Usage: "Number of retries for downloading base images.",
|
||||
EnvVar: "PLUGIN_IMAGE_DOWNLOAD_RETRY",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "oidc-token-id",
|
||||
Usage: "OIDC token for assuming role via web identity",
|
||||
EnvVar: "PLUGIN_OIDC_TOKEN_ID",
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
@@ -398,6 +417,7 @@ func run(c *cli.Context) error {
|
||||
noPush := c.Bool("no-push")
|
||||
assumeRole := c.String("assume-role")
|
||||
externalId := c.String("external-id")
|
||||
oidcToken := c.String("oidc-token-id")
|
||||
|
||||
// setup docker config for azure registry and base image docker registry
|
||||
err := setDockerAuth(
|
||||
@@ -411,6 +431,7 @@ func run(c *cli.Context) error {
|
||||
externalId,
|
||||
region,
|
||||
noPush,
|
||||
oidcToken,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create docker config")
|
||||
@@ -454,6 +475,8 @@ func run(c *cli.Context) error {
|
||||
AutoTagSuffix: c.String("auto-tag-suffix"),
|
||||
ExpandTag: c.Bool("expand-tag"),
|
||||
Args: c.StringSlice("args"),
|
||||
ArgsNew: c.Generic("args-new").(*CustomStringSliceFlag).GetValue(),
|
||||
IsMultipleBuildArgs: c.Bool("plugin-multiple-build-agrs"),
|
||||
Target: c.String("target"),
|
||||
Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")),
|
||||
Mirrors: c.StringSlice("registry-mirrors"),
|
||||
@@ -518,7 +541,7 @@ func run(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func setDockerAuth(dockerRegistry, dockerUsername, dockerPassword, accessKey, secretKey,
|
||||
registry, assumeRole, externalId, region string, noPush bool) error {
|
||||
registry, assumeRole, externalId, region string, noPush bool, oidcToken string) error {
|
||||
dockerConfig := docker.NewConfig()
|
||||
credentials := []docker.RegistryCredentials{}
|
||||
// set docker credentials for base image registry
|
||||
@@ -531,7 +554,24 @@ func setDockerAuth(dockerRegistry, dockerUsername, dockerPassword, accessKey, se
|
||||
credentials = append(credentials, pullFromRegistryCreds)
|
||||
}
|
||||
|
||||
if assumeRole != "" {
|
||||
if assumeRole != "" && oidcToken != "" {
|
||||
oidcAccessKey, oidcSecretKey, oidcSessionKey, err := getOidcCreds(oidcToken, assumeRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = os.Setenv(accessKeyEnv, oidcAccessKey)
|
||||
_ = os.Setenv(secretKeyEnv, oidcSecretKey)
|
||||
_ = os.Setenv(sessionKeyEnv, oidcSessionKey)
|
||||
|
||||
// kaniko-executor >=1.8.0 does not require additional cred helper logic for ECR,
|
||||
// as it discovers ECR repositories automatically and acts accordingly.
|
||||
if isKanikoVersionBelowOneDotEight(os.Getenv(kanikoVersionEnv)) {
|
||||
dockerConfig.SetCredHelper(ecrPublicDomain, "ecr-login")
|
||||
dockerConfig.SetCredHelper(registry, "ecr-login")
|
||||
}
|
||||
|
||||
} else if assumeRole != "" {
|
||||
var err error
|
||||
username, password, registry, err := getAssumeRoleCreds(region, assumeRole, externalId, "")
|
||||
if err != nil {
|
||||
@@ -771,3 +811,37 @@ func isKanikoVersionBelowOneDotEight(v string) bool {
|
||||
|
||||
return currVer.LessThan(oneEightVer)
|
||||
}
|
||||
|
||||
func getOidcCreds(oidcToken, assumeRole string) (string, string, string, error) {
|
||||
// Create a new session
|
||||
sess, err := session.NewSession()
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("failed to create AWS session: %w", err)
|
||||
}
|
||||
|
||||
// Create a new STS client
|
||||
svc := sts.New(sess)
|
||||
|
||||
// Prepare the input parameters for the STS call
|
||||
duration := int64(time.Hour / time.Second)
|
||||
input := &sts.AssumeRoleWithWebIdentityInput{
|
||||
RoleArn: aws.String(assumeRole),
|
||||
RoleSessionName: aws.String("kaniko-ecr-oidc"),
|
||||
WebIdentityToken: aws.String(oidcToken),
|
||||
DurationSeconds: aws.Int64(duration),
|
||||
}
|
||||
|
||||
// Call the AssumeRoleWithWebIdentity function
|
||||
result, err := svc.AssumeRoleWithWebIdentity(input)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("failed to assume role with web identity: %w", err)
|
||||
}
|
||||
|
||||
// Check if credentials exist in the result
|
||||
if result.Credentials == nil {
|
||||
return "", "", "", errors.New("no credentials returned by AssumeRoleWithWebIdentity")
|
||||
}
|
||||
|
||||
// Return the credentials
|
||||
return *result.Credentials.AccessKeyId, *result.Credentials.SecretAccessKey, *result.Credentials.SessionToken, nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone-kaniko/pkg/artifact"
|
||||
@@ -16,30 +17,32 @@ import (
|
||||
type (
|
||||
// Build defines Docker build parameters.
|
||||
Build struct {
|
||||
DroneCommitRef string // Drone git commit reference
|
||||
DroneRepoBranch string // Drone repo branch
|
||||
Dockerfile string // Docker build Dockerfile
|
||||
Context string // Docker build context
|
||||
Tags []string // Docker build tags
|
||||
AutoTag bool // Set this to auto detect tags from git commits and semver-tagged labels
|
||||
AutoTagSuffix string // Suffix to append to the auto detect tags
|
||||
ExpandTag bool // Set this to expand the `Tags` into semver-tagged labels
|
||||
Args []string // Docker build args
|
||||
Target string // Docker build target
|
||||
Repo string // Docker build repository
|
||||
Mirrors []string // Docker repository mirrors
|
||||
Labels []string // Label map
|
||||
SkipTlsVerify bool // Docker skip tls certificate verify for registry
|
||||
SnapshotMode string // Kaniko snapshot mode
|
||||
EnableCache bool // Whether to enable kaniko cache
|
||||
CacheRepo string // Remote repository that will be used to store cached layers
|
||||
CacheTTL int // Cache timeout in hours
|
||||
DigestFile string // Digest file location
|
||||
NoPush bool // Set this flag if you only want to build the image, without pushing to a registry
|
||||
Verbosity string // Log level
|
||||
Platform string // Allows to build with another default platform than the host, similarly to docker build --platform
|
||||
SkipUnusedStages bool // Build only used stages
|
||||
TarPath string // Set this flag to save the image as a tarball at path
|
||||
DroneCommitRef string // Drone git commit reference
|
||||
DroneRepoBranch string // Drone repo branch
|
||||
Dockerfile string // Docker build Dockerfile
|
||||
Context string // Docker build context
|
||||
Tags []string // Docker build tags
|
||||
AutoTag bool // Set this to auto detect tags from git commits and semver-tagged labels
|
||||
AutoTagSuffix string // Suffix to append to the auto detect tags
|
||||
ExpandTag bool // Set this to expand the `Tags` into semver-tagged labels
|
||||
Args []string // Docker build args
|
||||
ArgsNew []string // docker build args with comma seperated values
|
||||
IsMultipleBuildArgs bool // env variable for fallback for docker build args
|
||||
Target string // Docker build target
|
||||
Repo string // Docker build repository
|
||||
Mirrors []string // Docker repository mirrors
|
||||
Labels []string // Label map
|
||||
SkipTlsVerify bool // Docker skip tls certificate verify for registry
|
||||
SnapshotMode string // Kaniko snapshot mode
|
||||
EnableCache bool // Whether to enable kaniko cache
|
||||
CacheRepo string // Remote repository that will be used to store cached layers
|
||||
CacheTTL int // Cache timeout in hours
|
||||
DigestFile string // Digest file location
|
||||
NoPush bool // Set this flag if you only want to build the image, without pushing to a registry
|
||||
Verbosity string // Log level
|
||||
Platform string // Allows to build with another default platform than the host, similarly to docker build --platform
|
||||
SkipUnusedStages bool // Build only used stages
|
||||
TarPath string // Set this flag to save the image as a tarball at path
|
||||
|
||||
Cache bool // Enable or disable caching during the build process.
|
||||
CacheDir string // Directory to store cached layers.
|
||||
@@ -202,8 +205,14 @@ func (p Plugin) Exec() error {
|
||||
}
|
||||
|
||||
// Set the build arguments
|
||||
for _, arg := range p.Build.Args {
|
||||
cmdArgs = append(cmdArgs, fmt.Sprintf("--build-arg=%s", arg))
|
||||
if p.Build.IsMultipleBuildArgs {
|
||||
for _, arg := range p.Build.ArgsNew {
|
||||
cmdArgs = append(cmdArgs, "--build-arg", arg)
|
||||
}
|
||||
} else {
|
||||
for _, arg := range p.Build.Args {
|
||||
cmdArgs = append(cmdArgs, "--build-arg", arg)
|
||||
}
|
||||
}
|
||||
// Set the labels
|
||||
for _, label := range p.Build.Labels {
|
||||
@@ -258,6 +267,12 @@ func (p Plugin) Exec() error {
|
||||
}
|
||||
|
||||
if p.Build.TarPath != "" {
|
||||
tarDir := filepath.Dir(p.Build.TarPath)
|
||||
if _, err := os.Stat(tarDir); os.IsNotExist(err) {
|
||||
if mkdirErr := os.MkdirAll(tarDir, 0755); mkdirErr != nil {
|
||||
return fmt.Errorf("failed to create directory for tar path %s: %v", tarDir, mkdirErr)
|
||||
}
|
||||
}
|
||||
cmdArgs = append(cmdArgs, fmt.Sprintf("--tar-path=%s", p.Build.TarPath))
|
||||
}
|
||||
|
||||
@@ -407,7 +422,11 @@ func (p Plugin) Exec() error {
|
||||
}
|
||||
|
||||
if p.Output.OutputFile != "" {
|
||||
if err = output.WritePluginOutputFile(p.Output.OutputFile, getDigest(p.Build.DigestFile)); err != nil {
|
||||
var tarPath string
|
||||
if p.Build.TarPath != "" {
|
||||
tarPath = getTarPath(p.Build.TarPath)
|
||||
}
|
||||
if err = output.WritePluginOutputFile(p.Output.OutputFile, getDigest(p.Build.DigestFile), tarPath); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to write plugin output file at path: %s with error: %s\n", p.Output.OutputFile, err)
|
||||
}
|
||||
}
|
||||
@@ -415,6 +434,15 @@ func (p Plugin) Exec() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTarPath(tarPath string) string {
|
||||
tarDir := filepath.Dir(tarPath)
|
||||
if _, err := os.Stat(tarDir); err != nil && os.IsNotExist(err) {
|
||||
fmt.Fprintf(os.Stderr, "Warning: tar path does not exist: %s\n", tarPath)
|
||||
return ""
|
||||
}
|
||||
return tarPath
|
||||
}
|
||||
|
||||
func getDigest(digestFile string) string {
|
||||
content, err := ioutil.ReadFile(digestFile)
|
||||
if err != nil {
|
||||
|
||||
+132
@@ -1,6 +1,8 @@
|
||||
package kaniko
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -148,3 +150,133 @@ func TestBuild_AutoTags(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTarPathValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
tarPath string
|
||||
setup func(string) error
|
||||
cleanup func(string) error
|
||||
expectSuccess bool
|
||||
privileged bool
|
||||
}{
|
||||
{
|
||||
name: "valid_path_privileged",
|
||||
tarPath: "",
|
||||
setup: func(path string) error {
|
||||
tmpDir, err := os.MkdirTemp("", "test-image-tar")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Setenv("DRONE_WORKSPACE", tmpDir)
|
||||
return nil
|
||||
},
|
||||
cleanup: func(path string) error {
|
||||
tmpDir := os.Getenv("DRONE_WORKSPACE")
|
||||
os.Unsetenv("DRONE_WORKSPACE")
|
||||
return os.RemoveAll(tmpDir)
|
||||
},
|
||||
expectSuccess: true,
|
||||
privileged: true,
|
||||
},
|
||||
{
|
||||
name: "valid_path_unprivileged",
|
||||
tarPath: "",
|
||||
setup: func(path string) error {
|
||||
tmpDir, err := os.MkdirTemp("", "test-image-tar")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Setenv("DRONE_WORKSPACE", tmpDir)
|
||||
return nil
|
||||
},
|
||||
cleanup: func(path string) error {
|
||||
tmpDir := os.Getenv("DRONE_WORKSPACE")
|
||||
os.Unsetenv("DRONE_WORKSPACE")
|
||||
return os.RemoveAll(tmpDir)
|
||||
},
|
||||
expectSuccess: true,
|
||||
privileged: false,
|
||||
},
|
||||
{
|
||||
name: "empty_path",
|
||||
tarPath: "",
|
||||
setup: func(path string) error { return nil },
|
||||
cleanup: func(path string) error { return nil },
|
||||
expectSuccess: false,
|
||||
privileged: false,
|
||||
},
|
||||
{
|
||||
name: "relative_path_dots",
|
||||
tarPath: "",
|
||||
setup: func(path string) error {
|
||||
tmpDir, err := os.MkdirTemp("", "test-image-tar")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Setenv("DRONE_WORKSPACE", tmpDir)
|
||||
return nil
|
||||
},
|
||||
cleanup: func(path string) error {
|
||||
tmpDir := os.Getenv("DRONE_WORKSPACE")
|
||||
os.Unsetenv("DRONE_WORKSPACE")
|
||||
return os.RemoveAll(tmpDir)
|
||||
},
|
||||
expectSuccess: true,
|
||||
privileged: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Skip privileged tests if not running as root
|
||||
if tt.privileged && os.Getuid() != 0 {
|
||||
t.Skip("Skipping privileged test as not running as root")
|
||||
}
|
||||
|
||||
if err := tt.setup(tt.tarPath); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
defer tt.cleanup(tt.tarPath)
|
||||
|
||||
// Determine tar path based on test case
|
||||
var tarPath string
|
||||
tmpDir := os.Getenv("DRONE_WORKSPACE")
|
||||
switch tt.name {
|
||||
case "valid_path_privileged", "valid_path_unprivileged":
|
||||
tarPath = filepath.Join(tmpDir, "test", "image.tar")
|
||||
case "invalid_path_no_permissions":
|
||||
tarPath = "/test/image.tar"
|
||||
case "relative_path_dots":
|
||||
tarPath = filepath.Join("..", "test", "image.tar")
|
||||
default:
|
||||
tarPath = tt.tarPath
|
||||
}
|
||||
|
||||
p := Plugin{
|
||||
Build: Build{
|
||||
TarPath: tarPath,
|
||||
},
|
||||
}
|
||||
|
||||
tarDir := filepath.Dir(p.Build.TarPath)
|
||||
err := os.MkdirAll(tarDir, 0755)
|
||||
if tt.expectSuccess {
|
||||
if err != nil {
|
||||
t.Errorf("Expected directory creation to succeed, got error: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(tarDir); err != nil {
|
||||
t.Errorf("Expected directory to exist after creation, got error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
result := getTarPath(p.Build.TarPath)
|
||||
if tt.expectSuccess && result == "" {
|
||||
t.Error("Expected non-empty tar path, got empty string")
|
||||
}
|
||||
if !tt.expectSuccess && result != "" {
|
||||
t.Error("Expected empty tar path, got non-empty string")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,15 @@ import (
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func WritePluginOutputFile(outputFilePath, digest string) error {
|
||||
output := map[string]string{
|
||||
"digest": digest,
|
||||
func WritePluginOutputFile(outputFilePath, digest string, pluginTarPath string) error {
|
||||
output := make(map[string]string)
|
||||
if digest != "" {
|
||||
output["digest"] = digest
|
||||
}
|
||||
|
||||
if pluginTarPath != "" {
|
||||
output["IMAGE_TAR_PATH"] = pluginTarPath
|
||||
}
|
||||
|
||||
return godotenv.Write(output, outputFilePath)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWritePluginOutputFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
outputPath string
|
||||
digest string
|
||||
tarPath string
|
||||
setup func(string) error
|
||||
cleanup func(string) error
|
||||
expectError bool
|
||||
privileged bool
|
||||
}{
|
||||
{
|
||||
name: "valid_output_privileged",
|
||||
outputPath: "",
|
||||
digest: "sha256:test",
|
||||
tarPath: "",
|
||||
setup: func(path string) error {
|
||||
tmpDir, err := os.MkdirTemp("", "test-output")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Setenv("DRONE_WORKSPACE", tmpDir)
|
||||
return nil
|
||||
},
|
||||
cleanup: func(path string) error {
|
||||
tmpDir := os.Getenv("DRONE_WORKSPACE")
|
||||
os.Unsetenv("DRONE_WORKSPACE")
|
||||
return os.RemoveAll(tmpDir)
|
||||
},
|
||||
expectError: false,
|
||||
privileged: true,
|
||||
},
|
||||
{
|
||||
name: "valid_output_unprivileged",
|
||||
outputPath: "",
|
||||
digest: "sha256:test",
|
||||
tarPath: "",
|
||||
setup: func(path string) error {
|
||||
tmpDir, err := os.MkdirTemp("", "test-output")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Setenv("DRONE_WORKSPACE", tmpDir)
|
||||
return nil
|
||||
},
|
||||
cleanup: func(path string) error {
|
||||
tmpDir := os.Getenv("DRONE_WORKSPACE")
|
||||
os.Unsetenv("DRONE_WORKSPACE")
|
||||
return os.RemoveAll(tmpDir)
|
||||
},
|
||||
expectError: false,
|
||||
privileged: false,
|
||||
},
|
||||
{
|
||||
name: "digest_only",
|
||||
outputPath: "",
|
||||
digest: "sha256:test",
|
||||
tarPath: "",
|
||||
setup: func(path string) error {
|
||||
tmpDir, err := os.MkdirTemp("", "test-output")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Setenv("DRONE_WORKSPACE", tmpDir)
|
||||
return nil
|
||||
},
|
||||
cleanup: func(path string) error {
|
||||
tmpDir := os.Getenv("DRONE_WORKSPACE")
|
||||
os.Unsetenv("DRONE_WORKSPACE")
|
||||
return os.RemoveAll(tmpDir)
|
||||
},
|
||||
expectError: false,
|
||||
privileged: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Skip privileged tests if not running as root
|
||||
if tt.privileged && os.Getuid() != 0 {
|
||||
t.Skip("Skipping privileged test as not running as root")
|
||||
}
|
||||
|
||||
if err := tt.setup(tt.outputPath); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
defer tt.cleanup(tt.outputPath)
|
||||
|
||||
tmpDir := os.Getenv("DRONE_WORKSPACE")
|
||||
var outputPath, tarPath string
|
||||
switch tt.name {
|
||||
case "valid_output_privileged", "valid_output_unprivileged":
|
||||
outputPath = filepath.Join(tmpDir, "test", "output.env")
|
||||
tarPath = filepath.Join(tmpDir, "test", "image.tar")
|
||||
case "invalid_output_path":
|
||||
outputPath = filepath.Join("/root", "test", "output.env")
|
||||
tarPath = filepath.Join("/root", "test", "image.tar")
|
||||
case "digest_only":
|
||||
outputPath = filepath.Join(tmpDir, "test", "output.env")
|
||||
tarPath = ""
|
||||
}
|
||||
|
||||
err := os.MkdirAll(filepath.Dir(outputPath), 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create output directory: %v", err)
|
||||
}
|
||||
|
||||
err = WritePluginOutputFile(outputPath, tt.digest, tarPath)
|
||||
|
||||
if tt.expectError && err == nil {
|
||||
t.Error("Expected error, got none")
|
||||
}
|
||||
if !tt.expectError && err != nil {
|
||||
t.Errorf("Expected no error, got: %v", err)
|
||||
}
|
||||
|
||||
if !tt.expectError && err == nil {
|
||||
content, err := os.ReadFile(outputPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read output file: %v", err)
|
||||
}
|
||||
|
||||
if tt.digest != "" && !contains(string(content), tt.digest) {
|
||||
t.Error("Expected digest in output file")
|
||||
}
|
||||
|
||||
if tarPath != "" && !contains(string(content), tarPath) {
|
||||
t.Error("Expected tar path in output file")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func contains(content, substring string) bool {
|
||||
return len(substring) > 0 && content != "" && content != "\n" && content != "\r\n"
|
||||
}
|
||||
Reference in New Issue
Block a user