in magefile.go [3228:3381]
func authGCP(ctx context.Context) error {
// We only need the service account token to exist.
tokenPath, ok, err := getGCEServiceTokenPath()
if err != nil {
return err
}
if ok {
// exists, so nothing to do
return nil
}
// Use OS-appropriate command to find executables
execFindCmd := "which"
cliName := "gcloud"
if runtime.GOOS == "windows" {
execFindCmd = "where"
cliName += ".exe"
}
// Check if gcloud CLI is installed
cmd := exec.CommandContext(ctx, execFindCmd, cliName)
if err := cmd.Run(); err != nil {
return fmt.Errorf("%s CLI is not installed: %w", cliName, err)
}
// Check if user is already authenticated
var authList []struct {
Account string `json:"account"`
}
for authSuccess := false; !authSuccess; {
cmd = exec.CommandContext(ctx, cliName, "auth", "list", "--filter=status:ACTIVE", "--format=json")
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("unable to list authenticated accounts: %w", err)
}
if err := json.Unmarshal(output, &authList); err != nil {
return fmt.Errorf("unable to parse authenticated accounts: %w", err)
}
if len(authList) > 0 {
// We have at least one authenticated, active account. All set!
authSuccess = true
continue
}
fmt.Fprintln(os.Stderr, "❌ GCP authentication unsuccessful. Retrying...")
// Try to authenticate user
cmd = exec.CommandContext(ctx, cliName, "auth", "login")
if err := cmd.Run(); err != nil {
return fmt.Errorf("unable to authenticate user: %w", cliName, err)
}
}
// Parse env vars for
// - expected email domain (default: elastic.co)
// - expected GCP project (default: elastic-platform-ingest)
expectedEmailDomain := os.Getenv("TEST_INTEG_AUTH_EMAIL_DOMAIN")
if expectedEmailDomain == "" {
expectedEmailDomain = "elastic.co"
}
expectedProject := os.Getenv("TEST_INTEG_AUTH_GCP_PROJECT")
if expectedProject == "" {
expectedProject = "elastic-platform-ingest"
}
// Check that authenticated account's email domain name
email := authList[0].Account
parts := strings.Split(email, "@")
if len(parts) != 2 || parts[1] != expectedEmailDomain {
return fmt.Errorf("please authenticate with your @%s email address (currently authenticated with %s)", expectedEmailDomain, email)
}
// Check the authenticated account's project
cmd = exec.CommandContext(ctx, cliName, "config", "get", "core/project")
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("unable to get project: %w", err)
}
project := strings.TrimSpace(string(output))
if project != expectedProject {
// Attempt to select correct GCP project
fmt.Printf("Attempting to switch GCP project from [%s] to [%s]...\n", project, expectedProject)
cmd = exec.CommandContext(ctx, cliName, "config", "set", "core/project", expectedProject)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
if err = cmd.Run(); err != nil {
return fmt.Errorf("unable to switch project from [%s] to [%s]: %w", project, expectedProject, err)
}
project = expectedProject
}
// Check that the service account exists for the user
var svcList []struct {
Email string `json:"email"`
}
serviceAcctName := fmt.Sprintf("%s-agent-testing", strings.Replace(parts[0], ".", "-", -1))
iamAcctName := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", serviceAcctName, project)
cmd = exec.CommandContext(ctx, cliName, "iam", "service-accounts", "list", "--format=json")
output, err = cmd.Output()
if err != nil {
return fmt.Errorf("unable to list service accounts: %w", err)
}
if err := json.Unmarshal(output, &svcList); err != nil {
return fmt.Errorf("unable to parse service accounts: %w", err)
}
found := false
for _, svc := range svcList {
if svc.Email == iamAcctName {
found = true
break
}
}
if !found {
cmd = exec.CommandContext(ctx, cliName, "iam", "service-accounts", "create", serviceAcctName)
if err = cmd.Run(); err != nil {
return fmt.Errorf("unable to create service account %s: %w", serviceAcctName, err)
}
}
// Check that the service account has the required roles
cmd = exec.CommandContext(
ctx, cliName, "projects", "get-iam-policy", project,
"--flatten=bindings[].members",
fmt.Sprintf("--filter=bindings.members:serviceAccount:%s", iamAcctName),
"--format=value(bindings.role)")
output, err = cmd.Output()
if err != nil {
return fmt.Errorf("unable to get roles for service account %s: %w", serviceAcctName, err)
}
roles := strings.Split(string(output), ";")
missingRoles := gceFindMissingRoles(roles, []string{"roles/compute.admin", "roles/iam.serviceAccountUser"})
for _, role := range missingRoles {
cmd = exec.CommandContext(ctx, cliName, "projects", "add-iam-policy-binding", project,
fmt.Sprintf("--member=serviceAccount:%s", iamAcctName),
fmt.Sprintf("--role=%s", role))
if err = cmd.Run(); err != nil {
return fmt.Errorf("failed to add role %s to service account %s: %w", role, serviceAcctName, err)
}
}
// Create the key for the service account
cmd = exec.CommandContext(ctx, cliName, "iam", "service-accounts", "keys", "create", tokenPath,
fmt.Sprintf("--iam-account=%s", iamAcctName))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Run(); err != nil {
return fmt.Errorf("failed to create key %s for service account %s: %w", tokenPath, serviceAcctName, err)
}
return nil
}