func CreateGCSBucketWithVersioning(gcsClient *storage.Client, config *ExtendedRemoteStateConfigGCS, terragruntOptions *options.TerragruntOptions) error {
	err := CreateGCSBucket(gcsClient, config, terragruntOptions)
	if err != nil {
		return err
	}
	if err := WaitUntilGCSBucketExists(gcsClient, &config.remoteStateConfigGCS, terragruntOptions); err != nil {
		return err
	}
	if err := AddLabelsToGCSBucket(gcsClient, config, terragruntOptions); err != nil {
		return err
	}
	return nil
}
CreateGCSBucketWithVersioning creates the given GCS bucket and enables versioning for it.
package remote
import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"time"
	"cloud.google.com/go/storage"
	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/options"
	"github.com/gruntwork-io/terragrunt/shell"
	"github.com/gruntwork-io/terragrunt/util"
	"github.com/hashicorp/terraform/helper/pathorcontents"
	"github.com/mitchellh/mapstructure"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/api/option"
)
type ExtendedRemoteStateConfigGCS struct {
	remoteStateConfigGCS RemoteStateConfigGCS
	Project              string            `mapstructure:"project"`
	Location             string            `mapstructure:"location"`
	GCSBucketLabels      map[string]string `mapstructure:"gcs_bucket_labels"`
	SkipBucketVersioning bool              `mapstructure:"skip_bucket_versioning"`
	SkipBucketCreation   bool              `mapstructure:"skip_bucket_creation"`
}
var terragruntGCSOnlyConfigs = []string{
	"project",
	"location",
	"gcs_bucket_labels",
	"skip_bucket_versioning",
	"skip_bucket_creation",
}
type RemoteStateConfigGCS struct {
	Bucket        string `mapstructure:"bucket"`
	Credentials   string `mapstructure:"credentials"`
	Prefix        string `mapstructure:"prefix"`
	Path          string `mapstructure:"path"`
	EncryptionKey string `mapstructure:"encryption_key"`
}
type accountFile struct {
	PrivateKeyId string `json:"private_key_id"`
	PrivateKey   string `json:"private_key"`
	ClientEmail  string `json:"client_email"`
	ClientId     string `json:"client_id"`
}
const MAX_RETRIES_WAITING_FOR_GCS_BUCKET = 12
const SLEEP_BETWEEN_RETRIES_WAITING_FOR_GCS_BUCKET = 5 * time.Second
type GCSInitializer struct{}
func (gcsInitializer GCSInitializer) NeedsInitialization(remoteState *RemoteState, existingBackend *TerraformBackend, terragruntOptions *options.TerragruntOptions) (bool, error) {
	if remoteState.DisableInit {
		return false, nil
	}
	project := remoteState.Config["project"]
	if !gcsConfigValuesEqual(remoteState.Config, existingBackend, terragruntOptions) {
		return true, nil
	}
	if project != nil {
		remoteState.Config["project"] = project
	}
	gcsConfig, err := parseGCSConfig(remoteState.Config)
	if err != nil {
		return false, err
	}
	gcsClient, err := CreateGCSClient(*gcsConfig)
	if err != nil {
		return false, err
	}
	if !DoesGCSBucketExist(gcsClient, gcsConfig) {
		return true, nil
	}
	if project != nil {
		delete(remoteState.Config, "project")
	}
	return false, nil
}
func gcsConfigValuesEqual(config map[string]interface{}, existingBackend *TerraformBackend, terragruntOptions *options.TerragruntOptions) bool {
	if existingBackend == nil {
		return len(config) == 0
	}
	if existingBackend.Type != "gcs" {
		terragruntOptions.Logger.Printf("Backend type has changed from gcs to %s", existingBackend.Type)
		return false
	}
	if len(config) == 0 && len(existingBackend.Config) == 0 {
		return true
	}
	for key, value := range existingBackend.Config {
		if util.KindOf(existingBackend.Config[key]) == reflect.String && util.KindOf(config[key]) == reflect.Bool {
			if convertedValue, err := strconv.ParseBool(value.(string)); err == nil {
				existingBackend.Config[key] = convertedValue
			}
		}
	}
	for _, key := range terragruntGCSOnlyConfigs {
		delete(config, key)
	}
	if !terraformStateConfigEqual(existingBackend.Config, config) {
		terragruntOptions.Logger.Printf("Backend config has changed from %s to %s", existingBackend.Config, config)
		return false
	}
	return true
}
func (gcsInitializer GCSInitializer) Initialize(remoteState *RemoteState, terragruntOptions *options.TerragruntOptions) error {
	gcsConfigExtended, err := parseExtendedGCSConfig(remoteState.Config)
	if err != nil {
		return err
	}
	if err := validateGCSConfig(gcsConfigExtended, terragruntOptions); err != nil {
		return err
	}
	var gcsConfig = gcsConfigExtended.remoteStateConfigGCS
	gcsClient, err := CreateGCSClient(gcsConfig)
	if err != nil {
		return err
	}
	if !gcsConfigExtended.SkipBucketCreation && gcsConfig.Bucket != "" {
		if err := createGCSBucketIfNecessary(gcsClient, gcsConfigExtended, terragruntOptions); err != nil {
			return err
		}
	}
	if gcsConfig.Bucket != "" {
		if err := checkIfGCSVersioningEnabled(gcsClient, &gcsConfig, terragruntOptions); err != nil {
			return err
		}
	}
	return nil
}
func (gcsInitializer GCSInitializer) GetTerraformInitArgs(config map[string]interface{}) map[string]interface{} {
	var filteredConfig = make(map[string]interface{})
	for key, val := range config {
		if util.ListContainsElement(terragruntGCSOnlyConfigs, key) {
			continue
		}
		filteredConfig[key] = val
	}
	return filteredConfig
}
func parseGCSConfig(config map[string]interface{}) (*RemoteStateConfigGCS, error) {
	var gcsConfig RemoteStateConfigGCS
	if err := mapstructure.Decode(config, &gcsConfig); err != nil {
		return nil, errors.WithStackTrace(err)
	}
	return &gcsConfig, nil
}
func parseExtendedGCSConfig(config map[string]interface{}) (*ExtendedRemoteStateConfigGCS, error) {
	var gcsConfig RemoteStateConfigGCS
	var extendedConfig ExtendedRemoteStateConfigGCS
	if err := mapstructure.Decode(config, &gcsConfig); err != nil {
		return nil, errors.WithStackTrace(err)
	}
	if err := mapstructure.Decode(config, &extendedConfig); err != nil {
		return nil, errors.WithStackTrace(err)
	}
	extendedConfig.remoteStateConfigGCS = gcsConfig
	return &extendedConfig, nil
}
func validateGCSConfig(extendedConfig *ExtendedRemoteStateConfigGCS, terragruntOptions *options.TerragruntOptions) error {
	var config = extendedConfig.remoteStateConfigGCS
	if config.Prefix == "" {
		return errors.WithStackTrace(MissingRequiredGCSRemoteStateConfig("prefix"))
	}
	return nil
}
func createGCSBucketIfNecessary(gcsClient *storage.Client, config *ExtendedRemoteStateConfigGCS, terragruntOptions *options.TerragruntOptions) error {
	if !DoesGCSBucketExist(gcsClient, &config.remoteStateConfigGCS) {
		terragruntOptions.Logger.Printf("Remote state GCS bucket %s does not exist. Attempting to create it", config.remoteStateConfigGCS.Bucket)
		if config.Project == "" {
			return errors.WithStackTrace(MissingRequiredGCSRemoteStateConfig("project"))
		}
		if config.Location == "" {
			return errors.WithStackTrace(MissingRequiredGCSRemoteStateConfig("location"))
		}
		prompt := fmt.Sprintf("Remote state GCS bucket %s does not exist or you don't have permissions to access it. Would you like Terragrunt to create it?", config.remoteStateConfigGCS.Bucket)
		shouldCreateBucket, err := shell.PromptUserForYesNo(prompt, terragruntOptions)
		if err != nil {
			return err
		}
		if shouldCreateBucket {
			description := fmt.Sprintf("Create GCS bucket %s", config.remoteStateConfigGCS.Bucket)
			maxRetries := 3
			sleepBetweenRetries := 10 * time.Second
			return util.DoWithRetry(description, maxRetries, sleepBetweenRetries, terragruntOptions.Logger, func() error {
				return CreateGCSBucketWithVersioning(gcsClient, config, terragruntOptions)
			})
		}
	}
	return nil
}
func checkIfGCSVersioningEnabled(gcsClient *storage.Client, config *RemoteStateConfigGCS, terragruntOptions *options.TerragruntOptions) error {
	ctx := context.Background()
	bucket := gcsClient.Bucket(config.Bucket)
	attrs, err := bucket.Attrs(ctx)
	if err != nil {
		return errors.WithStackTrace(err)
	}
	if attrs.VersioningEnabled == false {
		terragruntOptions.Logger.Printf("WARNING: Versioning is not enabled for the remote state GCS bucket %s. We recommend enabling versioning so that you can roll back to previous versions of your Terraform state in case of error.", config.Bucket)
	}
	return nil
}