From 47be6259d41af508c36eafe316ab426821c49036 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Thu, 25 Jul 2024 02:21:56 -0400 Subject: [PATCH 1/6] Copy the existing gcloud config directory if it exists. This is needed to run integration tests locally. --- integration_test/agents/agents.go | 6 +- integration_test/gce/gce_testing.go | 89 +++++++++++++++++++ .../third_party_apps_test/main_test.go | 6 +- 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/integration_test/agents/agents.go b/integration_test/agents/agents.go index d2bc9cdae3..b6780f2a8b 100644 --- a/integration_test/agents/agents.go +++ b/integration_test/agents/agents.go @@ -851,7 +851,11 @@ func CommonSetupWithExtraCreateArgumentsAndMetadata(t *testing.T, imageSpec stri t.Helper() ctx, cancel := context.WithTimeout(context.Background(), gce.SuggestedTimeout) t.Cleanup(cancel) - ctx = gce.WithGcloudConfigDir(ctx, t.TempDir()) + gcloudConfigDir := t.TempDir() + if err := gce.SetupGcloudConfigDir(ctx, gcloudConfigDir); err != nil { + t.Fatalf("Unable to set up a gcloud config directory: %v", err) + } + ctx = gce.WithGcloudConfigDir(ctx, gcloudConfigDir) logger := gce.SetupLogger(t) logger.ToMainLog().Println("Calling SetupVM(). For details, see VM_initialization.txt.") diff --git a/integration_test/gce/gce_testing.go b/integration_test/gce/gce_testing.go index 1529f43981..989299028d 100644 --- a/integration_test/gce/gce_testing.go +++ b/integration_test/gce/gce_testing.go @@ -739,6 +739,95 @@ func runCommand(ctx context.Context, logger *log.Logger, stdin io.Reader, args [ return output, err } +// getGcloudConfigDir returns the current gcloud configuration directory. +func getGcloudConfigDir(ctx context.Context) (string, error) { + null, err := os.Open(os.DevNull) + if err != nil { + panic(fmt.Sprintf("unable to open os.DevNull: %v", err)) + } + out, err := RunGcloud(ctx, log.New(null, "", 0), "", []string{"info", "--format=value[terminator=''](config.paths.global_config_dir)"}) + if err != nil { + return "", fmt.Errorf("error running gcloud info: %w", err) + } + return out.Stdout, nil +} + +// copyFile copies the contents of source into target. +func copyFile(source string, target string) error { + in, err := os.Open(source) + if err != nil { + return err + } + defer in.Close() + out, err := os.Create(target) + if err != nil { + return err + } + _, copyErr := io.Copy(out, in) + syncErr := out.Sync() + closeErr := out.Close() + return multierr.Combine(copyErr, syncErr, closeErr) +} + +// SetupGcloudConfigDir sets up a new gcloud configuration directory. +// This copies the "configurations" subdirectory of the context-specified +// configuration directory into the new directory. +func SetupGcloudConfigDir(ctx context.Context, directory string) error { + relevantFiles := []string{ + "active_config", + "access_tokens.db", + "credentials.db", + "default_configs.db", + } + currentConfigDir, err := getGcloudConfigDir(ctx) + if err != nil { + return err + } + sfi, err := os.Stat(currentConfigDir) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return fmt.Errorf("unexpected error accessing source directory %s: %w", currentConfigDir, err) + } + if !sfi.IsDir() { + return fmt.Errorf("source %s is not a directory: %s", currentConfigDir, sfi.Mode().Type()) + } + dfi, err := os.Stat(directory) + if err != nil { + return fmt.Errorf("unexpected error accessing destination directory %s: %w", directory, err) + } + if !dfi.IsDir() { + return fmt.Errorf("target %s is not a directory: %s", directory, dfi.Mode().Type()) + } + if os.SameFile(sfi, dfi) { + return nil + } + srcDirPath := filepath.Join(currentConfigDir, "configurations") + files, err := os.ReadDir(srcDirPath) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return fmt.Errorf("error listing the source configuration subdirectory: %w", err) + } + tgtDirPath := filepath.Join(directory, "configurations") + if err := os.MkdirAll(tgtDirPath, 0700); err != nil { + return fmt.Errorf("error creating the target configuration subdirectory: %w", err) + } + for _, file := range files { + if err = copyFile(filepath.Join(srcDirPath, file.Name()), filepath.Join(tgtDirPath, file.Name())); err != nil { + return fmt.Errorf("error copying %s: %w", filepath.Join("configurations", file.Name()), err) + } + } + for _, name := range relevantFiles { + if err = copyFile(filepath.Join(currentConfigDir, name), filepath.Join(directory, name)); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("error copying %s: %w", name, err) + } + } + return nil +} + const ( gcloudConfigDirKey = "__gcloud_config_dir__" ) diff --git a/integration_test/third_party_apps_test/main_test.go b/integration_test/third_party_apps_test/main_test.go index c318f2f0be..198829b26e 100644 --- a/integration_test/third_party_apps_test/main_test.go +++ b/integration_test/third_party_apps_test/main_test.go @@ -983,7 +983,11 @@ func TestThirdPartyApps(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), gce.SuggestedTimeout) defer cancel() - ctx = gce.WithGcloudConfigDir(ctx, t.TempDir()) + gcloudConfigDir := t.TempDir() + if err := gce.SetupGcloudConfigDir(ctx, gcloudConfigDir); err != nil { + t.Fatalf("Unable to set up a gcloud config directory: %v", err) + } + ctx = gce.WithGcloudConfigDir(ctx, gcloudConfigDir) var err error for attempt := 1; attempt <= 4; attempt++ { From 93402dcb4988b5bfafabdd90680826a75aa19dd0 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Mon, 29 Jul 2024 22:04:09 -0400 Subject: [PATCH 2/6] Copy the gcloud config directory in its entirety. --- integration_test/gce/gce_testing.go | 62 ++++++++++++++++------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/integration_test/gce/gce_testing.go b/integration_test/gce/gce_testing.go index 989299028d..142ee0981e 100644 --- a/integration_test/gce/gce_testing.go +++ b/integration_test/gce/gce_testing.go @@ -769,16 +769,42 @@ func copyFile(source string, target string) error { return multierr.Combine(copyErr, syncErr, closeErr) } +// copyDirectory copies the contents of the source directory into target. +func copyDirectory(source string, target string) error { + files, err := os.ReadDir(source) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + if err := os.MkdirAll(target, 0700); err != nil { + return err + } + for _, file := range files { + s := filepath.Join(source, file.Name()) + d := filepath.Join(target, file.Name()) + sfi, err := os.Stat(s) + if err != nil { + return err + } + if sfi.IsDir() { + if err := copyDirectory(s, d); err != nil { + return err + } + } else { + if err := copyFile(s, d); err != nil { + return err + } + } + } + return nil +} + // SetupGcloudConfigDir sets up a new gcloud configuration directory. // This copies the "configurations" subdirectory of the context-specified // configuration directory into the new directory. func SetupGcloudConfigDir(ctx context.Context, directory string) error { - relevantFiles := []string{ - "active_config", - "access_tokens.db", - "credentials.db", - "default_configs.db", - } currentConfigDir, err := getGcloudConfigDir(ctx) if err != nil { return err @@ -803,27 +829,9 @@ func SetupGcloudConfigDir(ctx context.Context, directory string) error { if os.SameFile(sfi, dfi) { return nil } - srcDirPath := filepath.Join(currentConfigDir, "configurations") - files, err := os.ReadDir(srcDirPath) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return fmt.Errorf("error listing the source configuration subdirectory: %w", err) - } - tgtDirPath := filepath.Join(directory, "configurations") - if err := os.MkdirAll(tgtDirPath, 0700); err != nil { - return fmt.Errorf("error creating the target configuration subdirectory: %w", err) - } - for _, file := range files { - if err = copyFile(filepath.Join(srcDirPath, file.Name()), filepath.Join(tgtDirPath, file.Name())); err != nil { - return fmt.Errorf("error copying %s: %w", filepath.Join("configurations", file.Name()), err) - } - } - for _, name := range relevantFiles { - if err = copyFile(filepath.Join(currentConfigDir, name), filepath.Join(directory, name)); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("error copying %s: %w", name, err) - } + // TODO: Replace with os.CopyFS() once available. + if err = copyDirectory(currentConfigDir, directory); err != nil { + return fmt.Errorf("error copying directory contents: %w", err) } return nil } From bd27601c6ab20cd5ca79bdb4d771366543966fa7 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Mon, 29 Jul 2024 22:24:02 -0400 Subject: [PATCH 3/6] Use io.Discard instead of Open'ing os.DevNull. --- integration_test/gce/gce_testing.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/integration_test/gce/gce_testing.go b/integration_test/gce/gce_testing.go index 142ee0981e..3cc0df36b2 100644 --- a/integration_test/gce/gce_testing.go +++ b/integration_test/gce/gce_testing.go @@ -741,11 +741,7 @@ func runCommand(ctx context.Context, logger *log.Logger, stdin io.Reader, args [ // getGcloudConfigDir returns the current gcloud configuration directory. func getGcloudConfigDir(ctx context.Context) (string, error) { - null, err := os.Open(os.DevNull) - if err != nil { - panic(fmt.Sprintf("unable to open os.DevNull: %v", err)) - } - out, err := RunGcloud(ctx, log.New(null, "", 0), "", []string{"info", "--format=value[terminator=''](config.paths.global_config_dir)"}) + out, err := RunGcloud(ctx, log.New(io.Discard, "", 0), "", []string{"info", "--format=value[terminator=''](config.paths.global_config_dir)"}) if err != nil { return "", fmt.Errorf("error running gcloud info: %w", err) } From 6cbd2f752094bdeb44a54ac7b96300dc82774e05 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Mon, 29 Jul 2024 22:39:14 -0400 Subject: [PATCH 4/6] Propagate the error properly. --- integration_test/gce/gce_testing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_test/gce/gce_testing.go b/integration_test/gce/gce_testing.go index 3cc0df36b2..b752c7e111 100644 --- a/integration_test/gce/gce_testing.go +++ b/integration_test/gce/gce_testing.go @@ -743,7 +743,7 @@ func runCommand(ctx context.Context, logger *log.Logger, stdin io.Reader, args [ func getGcloudConfigDir(ctx context.Context) (string, error) { out, err := RunGcloud(ctx, log.New(io.Discard, "", 0), "", []string{"info", "--format=value[terminator=''](config.paths.global_config_dir)"}) if err != nil { - return "", fmt.Errorf("error running gcloud info: %w", err) + return "", err } return out.Stdout, nil } From 69bde8ead40f15ad21184e4d0a95a3d6fcb11dfe Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Tue, 6 Aug 2024 21:05:54 -0400 Subject: [PATCH 5/6] Use "cp -r" to copy. --- integration_test/gce/gce_testing.go | 74 ++--------------------------- 1 file changed, 3 insertions(+), 71 deletions(-) diff --git a/integration_test/gce/gce_testing.go b/integration_test/gce/gce_testing.go index b752c7e111..2e177dd36e 100644 --- a/integration_test/gce/gce_testing.go +++ b/integration_test/gce/gce_testing.go @@ -748,86 +748,18 @@ func getGcloudConfigDir(ctx context.Context) (string, error) { return out.Stdout, nil } -// copyFile copies the contents of source into target. -func copyFile(source string, target string) error { - in, err := os.Open(source) - if err != nil { - return err - } - defer in.Close() - out, err := os.Create(target) - if err != nil { - return err - } - _, copyErr := io.Copy(out, in) - syncErr := out.Sync() - closeErr := out.Close() - return multierr.Combine(copyErr, syncErr, closeErr) -} - -// copyDirectory copies the contents of the source directory into target. -func copyDirectory(source string, target string) error { - files, err := os.ReadDir(source) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - if err := os.MkdirAll(target, 0700); err != nil { - return err - } - for _, file := range files { - s := filepath.Join(source, file.Name()) - d := filepath.Join(target, file.Name()) - sfi, err := os.Stat(s) - if err != nil { - return err - } - if sfi.IsDir() { - if err := copyDirectory(s, d); err != nil { - return err - } - } else { - if err := copyFile(s, d); err != nil { - return err - } - } - } - return nil -} - // SetupGcloudConfigDir sets up a new gcloud configuration directory. // This copies the "configurations" subdirectory of the context-specified // configuration directory into the new directory. +// This only works on Linux. func SetupGcloudConfigDir(ctx context.Context, directory string) error { currentConfigDir, err := getGcloudConfigDir(ctx) if err != nil { return err } - sfi, err := os.Stat(currentConfigDir) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return fmt.Errorf("unexpected error accessing source directory %s: %w", currentConfigDir, err) - } - if !sfi.IsDir() { - return fmt.Errorf("source %s is not a directory: %s", currentConfigDir, sfi.Mode().Type()) - } - dfi, err := os.Stat(directory) - if err != nil { - return fmt.Errorf("unexpected error accessing destination directory %s: %w", directory, err) - } - if !dfi.IsDir() { - return fmt.Errorf("target %s is not a directory: %s", directory, dfi.Mode().Type()) - } - if os.SameFile(sfi, dfi) { - return nil - } // TODO: Replace with os.CopyFS() once available. - if err = copyDirectory(currentConfigDir, directory); err != nil { - return fmt.Errorf("error copying directory contents: %w", err) + if out, err := runCommand(ctx, log.New(io.Discard, "", 0), nil, []string{"cp", "-r", filepath.Join(currentConfigDir, "."), filepath.Join(directory, ".")}, nil); err != nil { + return fmt.Errorf("error copying directory contents: %s (%w)", out.Stderr, err) } return nil } From 8881eb73192abad8660e7ca9e01b716ef5dc41e9 Mon Sep 17 00:00:00 2001 From: Igor Peshansky Date: Wed, 7 Aug 2024 18:24:10 -0400 Subject: [PATCH 6/6] Address review comments. --- integration_test/gce/gce_testing.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration_test/gce/gce_testing.go b/integration_test/gce/gce_testing.go index 2e177dd36e..b4b02780c4 100644 --- a/integration_test/gce/gce_testing.go +++ b/integration_test/gce/gce_testing.go @@ -749,8 +749,8 @@ func getGcloudConfigDir(ctx context.Context) (string, error) { } // SetupGcloudConfigDir sets up a new gcloud configuration directory. -// This copies the "configurations" subdirectory of the context-specified -// configuration directory into the new directory. +// This copies the contents of the context-specified configuration directory +// into the new directory. // This only works on Linux. func SetupGcloudConfigDir(ctx context.Context, directory string) error { currentConfigDir, err := getGcloudConfigDir(ctx) @@ -758,8 +758,8 @@ func SetupGcloudConfigDir(ctx context.Context, directory string) error { return err } // TODO: Replace with os.CopyFS() once available. - if out, err := runCommand(ctx, log.New(io.Discard, "", 0), nil, []string{"cp", "-r", filepath.Join(currentConfigDir, "."), filepath.Join(directory, ".")}, nil); err != nil { - return fmt.Errorf("error copying directory contents: %s (%w)", out.Stderr, err) + if _, err := runCommand(ctx, log.New(io.Discard, "", 0), nil, []string{"cp", "-r", filepath.Join(currentConfigDir, "."), filepath.Join(directory, ".")}, nil); err != nil { + return err } return nil }