func (b *CloudSQLBroker) pollOperationUntilDone(ctx context.Context, op *googlecloudsql.Operation, projectId string) error {
	sqlService, err := b.createClient(ctx)
	if err != nil {
		return err
	}
	opsService := googlecloudsql.NewOperationsService(sqlService)
	for {
		status, err := opsService.Get(projectId, op.Name).Do()
		if err != nil {
			return err
		}
		if status.EndTime != "" {
			return nil
		}
		b.Logger.Info("waiting for operation", lager.Data{"operation": op.Name, "status": status.Status})
		time.Sleep(time.Second)
	}
}
pollOperationUntilDone loops and waits until a cloudsql operation is done, returning an error if any is encountered
XXX: note that for this function in particular, we are being explicit to return errors from the google api exactly
as we get them, because further up the stack these errors will be evaluated differently and need to be preserved
package cloudsql
import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"
	"github.com/GoogleCloudPlatform/gcp-service-broker/db_service/models"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/providers/builtin/base"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/varcontext"
	"github.com/GoogleCloudPlatform/gcp-service-broker/utils"
	"github.com/pivotal-cf/brokerapi"
	"context"
	"code.cloudfoundry.org/lager"
	googleapi "google.golang.org/api/googleapi"
	googlecloudsql "google.golang.org/api/sqladmin/v1beta4"
	sqladmin "google.golang.org/api/sqladmin/v1beta4"
)
const (
	secondGenPricingPlan         string = "PER_USE"
	postgresDefaultVersion       string = "POSTGRES_11"
	mySqlSecondGenDefaultVersion string = "MYSQL_5_7"
	mySQLURIFormat = ""
)
type CloudSQLBroker struct {
	base.BrokerBase
	uriFormat string
}
type InstanceInformation struct {
	InstanceName string `json:"instance_name"`
	DatabaseName string `json:"database_name"`
	Host         string `json:"host"`
	Region       string `json:"region"`
	LastMasterOperationId string `json:"last_master_operation_id"`
}
func (b *CloudSQLBroker) Provision(ctx context.Context, provisionContext *varcontext.VarContext) (models.ServiceInstanceDetails, error) {
	di, ii, err := createProvisionRequest(provisionContext)
	if err != nil {
		return models.ServiceInstanceDetails{}, err
	}
	sqlService, err := b.createClient(ctx)
	if err != nil {
		return models.ServiceInstanceDetails{}, err
	}
	op, err := sqlService.Instances.Insert(b.ProjectId, di).Do()
	if err != nil {
		return models.ServiceInstanceDetails{}, fmt.Errorf("Error creating new CloudSQL instance: %s", err)
	}
	b.Logger.Debug("updating details", lager.Data{"from": "{}", "to": ii})
	id := models.ServiceInstanceDetails{
		Name: di.Name,
		OperationType: models.ProvisionOperationType,
		OperationId:   op.Name,
	}
	if err := id.SetOtherDetails(ii); err != nil {
		return models.ServiceInstanceDetails{}, err
	}
	return id, nil
}
func createProvisionRequest(vars *varcontext.VarContext) (*googlecloudsql.DatabaseInstance, *InstanceInformation, error) {
	di := createInstanceRequest(vars)
	ii := InstanceInformation{
		InstanceName: di.Name,
		DatabaseName: vars.GetString("database_name"),
	}
	return di, &ii, vars.Error()
}
func varctxGetAcls(vars *varcontext.VarContext) []*googlecloudsql.AclEntry {
	openAcls := []*googlecloudsql.AclEntry{}
	authorizedNetworkCsv := vars.GetString("authorized_networks")
	if authorizedNetworkCsv == "" {
		return openAcls
	}
	for _, v := range strings.Split(authorizedNetworkCsv, ",") {
		openAcls = append(openAcls, &googlecloudsql.AclEntry{Value: v})
	}
	return openAcls
}
func createInstanceRequest(vars *varcontext.VarContext) *googlecloudsql.DatabaseInstance {
	databaseFlags := parseDatabaseFlags(vars.GetString("database_flags"))
	autoResize := vars.GetBool("auto_resize")
	ipConfiguration := googlecloudsql.IpConfiguration{
		RequireSsl:         true,
		Ipv4Enabled:        true,
		AuthorizedNetworks: varctxGetAcls(vars),
	}
	if privateNetwork := vars.GetString("private_network"); privateNetwork != "" {
		ipConfiguration = googlecloudsql.IpConfiguration{
			Ipv4Enabled:     false,
			RequireSsl:      true,
			PrivateNetwork:  privateNetwork,
			ForceSendFields: []string{"Ipv4Enabled"},
		}
	}
	return &googlecloudsql.DatabaseInstance{
		Name:            vars.GetString("instance_name"),
		DatabaseVersion: vars.GetString("version"),
		Region:          vars.GetString("region"),
		Settings: &googlecloudsql.Settings{
			AvailabilityType: vars.GetString("availability_type"),
			BackupConfiguration: &googlecloudsql.BackupConfiguration{
				Enabled:          vars.GetBool("backups_enabled"),
				StartTime:        vars.GetString("backup_start_time"),
				BinaryLogEnabled: vars.GetBool("binlog"),
			},
			DatabaseFlags:   databaseFlags,
			IpConfiguration: &ipConfiguration,
			Tier:            vars.GetString("tier"),
			DataDiskSizeGb:  int64(vars.GetInt("disk_size")),
			LocationPreference: &googlecloudsql.LocationPreference{
				Zone: vars.GetString("zone"),
			},
			DataDiskType: vars.GetString("disk_type"),
			MaintenanceWindow: &googlecloudsql.MaintenanceWindow{
				Day:             int64(vars.GetInt("maintenance_window_day")),
				Hour:            int64(vars.GetInt("maintenance_window_hour")),
				UpdateTrack:     "stable",
				ForceSendFields: []string{"Day", "Hour"},
			},
			PricingPlan:            secondGenPricingPlan,
			ActivationPolicy:       vars.GetString("activation_policy"),
			StorageAutoResize:      &autoResize,
			StorageAutoResizeLimit: int64(vars.GetInt("auto_resize_limit")),
			UserLabels:             vars.GetStringMapString("labels"),
		},
	}
}
func parseDatabaseFlags(flagsvar string) []*googlecloudsql.DatabaseFlags {
	databaseFlags := []*googlecloudsql.DatabaseFlags{}
	if flagsvar == "" {
		return databaseFlags
	}
	flags := strings.Split(flagsvar, ",")
	for _, f := range flags {
		flag := strings.Split(f, "=")
		databaseFlags = append(databaseFlags, &googlecloudsql.DatabaseFlags{
			Name:            flag[0],
			Value:           flag[1],
			ForceSendFields: []string{"Value"},
		})
	}
	return databaseFlags
}
func (b *CloudSQLBroker) Bind(ctx context.Context, vc *varcontext.VarContext) (map[string]interface{}, error) {
	combinedCreds := varcontext.Builder()
	useJdbcFormat := vc.GetBool("jdbc_uri_format")
	if err := vc.Error(); err != nil {
		return nil, err
	}
	saCreds, err := b.BrokerBase.Bind(ctx, vc)
	if err != nil {
		return saCreds, err
	}
	combinedCreds.MergeMap(saCreds)
	sqlCreds, err := b.createSqlCredentials(ctx, vc)
	if err != nil {
		return saCreds, err
	}
	combinedCreds.MergeMap(sqlCreds)
	uriPrefix := ""
	if useJdbcFormat {
		uriPrefix = "jdbc:"
	}
	combinedCreds.MergeMap(map[string]interface{}{"UriPrefix": uriPrefix})
	return combinedCreds.BuildMap()
}
func (b *CloudSQLBroker) BuildInstanceCredentials(ctx context.Context, bindRecord models.ServiceBindingCredentials, instanceRecord models.ServiceInstanceDetails) (*brokerapi.Binding, error) {
	creds, err := varcontext.Builder().
		MergeJsonObject(json.RawMessage(bindRecord.OtherDetails)).
		MergeJsonObject(json.RawMessage(instanceRecord.OtherDetails)).
		MergeEvalResult("uri", b.uriFormat, varcontext.TypeString).
		BuildMap()
	if err != nil {
		return nil, err
	}
	return &brokerapi.Binding{Credentials: creds}, nil
}
func (b *CloudSQLBroker) Unbind(ctx context.Context, instance models.ServiceInstanceDetails, binding models.ServiceBindingCredentials) error {
	if err := b.deleteSqlSslCert(ctx, binding, instance); err != nil {
		return err
	}
	if err := b.deleteSqlUserAccount(ctx, binding, instance); err != nil {
		return err
	}
	if err := b.BrokerBase.Unbind(ctx, instance, binding); err != nil {
		return err
	}
	return nil
}
func (b *CloudSQLBroker) PollInstance(ctx context.Context, instance models.ServiceInstanceDetails) (bool, error) {
	b.Logger.Info("PollInstance", lager.Data{
		"instance":       instance.Name,
		"operation_type": instance.OperationType,
		"operation_id":   instance.OperationId,
	})
	if instance.OperationType == "" {
		return false, errors.New("Couldn't find any pending operations for this CloudSQL instance.")
	}
	result, err := b.pollOperation(ctx, instance.OperationId)
	if result == false || err != nil {
		return result, err
	}
	if instance.OperationType == models.ProvisionOperationType {
		if err := b.UpdateInstanceDetails(ctx, &instance); err != nil {
			return true, err
		}
		return true, b.createDatabase(ctx, &instance)
	}
	return true, nil
}
func (b *CloudSQLBroker) UpdateInstanceDetails(ctx context.Context, instance *models.ServiceInstanceDetails) error {
	var instanceInfo InstanceInformation
	if err := instance.GetOtherDetails(&instanceInfo); err != nil {
		return err
	}
	client, err := b.createClient(ctx)
	if err != nil {
		return err
	}
	clouddb, err := googlecloudsql.NewInstancesService(client).Get(b.ProjectId, instance.Name).Do()
	if err != nil {
		return fmt.Errorf("Error getting instance from API: %s", err)
	}
	instance.Url = clouddb.SelfLink
	instance.Location = clouddb.Region
	instanceInfo.Host = clouddb.IpAddresses[0].IpAddress
	instanceInfo.Region = clouddb.Region
	return instance.SetOtherDetails(instanceInfo)
}
func (b *CloudSQLBroker) createDatabase(ctx context.Context, instance *models.ServiceInstanceDetails) error {
	var instanceInfo InstanceInformation
	if err := json.Unmarshal([]byte(instance.OtherDetails), &instanceInfo); err != nil {
		return fmt.Errorf("Error unmarshalling instance information.")
	}
	client, err := b.createClient(ctx)
	if err != nil {
		return err
	}
	d := googlecloudsql.Database{Name: instanceInfo.DatabaseName}
	op, err := client.Databases.Insert(b.ProjectId, instance.Name, &d).Do()
	if err != nil {
		return fmt.Errorf("Error creating database: %s", err)
	}
	return b.pollOperationUntilDone(ctx, op, b.ProjectId)
}
func (b *CloudSQLBroker) pollOperation(ctx context.Context, opterationId string) (bool, error) {
	client, err := b.createClient(ctx)
	if err != nil {
		return false, err
	}
	operation, err := googlecloudsql.NewOperationsService(client).Get(b.ProjectId, opterationId).Do()
	if err != nil {
		return false, err
	}
	if operation.Status == "DONE" {
		if operation.Error == nil {
			return true, nil
		} else {
			errs := ""
			for _, err := range operation.Error.Errors {
				errs += fmt.Sprintf("%s: %q; ", err.Code, err.Message)
			}
			return true, errors.New(errs)
		}
	}
	return false, nil
}