diff --git a/broker/bind.go b/broker/bind.go deleted file mode 100755 index 4cecfee..0000000 --- a/broker/bind.go +++ /dev/null @@ -1,83 +0,0 @@ -package broker - -import ( - "context" - "fmt" - - "github.com/cloudfoundry-community/go-uaa" - brokerapi "github.com/pivotal-cf/brokerapi/domain" - "github.com/starkandwayne/scs-broker/broker/utilities" -) - -func (broker *SCSBroker) Bind(ctx context.Context, instanceID, bindingID string, details brokerapi.BindDetails, asyncAllowed bool) (brokerapi.Binding, error) { - binding := brokerapi.Binding{} - - broker.Logger.Info("Bind: GetUAAClient") - - api, err := broker.GetUaaClient() - if err != nil { - broker.Logger.Info("Bind: Error in getting client") - return binding, err - } - - clientId := utilities.MakeClientIdForBinding(details.ServiceID, bindingID) - password := utilities.GenClientPassword() - - client := uaa.Client{ - ClientID: clientId, - AuthorizedGrantTypes: []string{"client_credentials"}, - Authorities: []string{fmt.Sprintf("%s.%v.read", details.ServiceID, instanceID)}, - DisplayName: clientId, - ClientSecret: password, - } - - broker.Logger.Info("Bind: got client info") - broker.Logger.Info("Bind: Create Client") - _, err = api.CreateClient(client) - if err != nil { - broker.Logger.Info("Bind: Error in CreateClient") - return binding, err - } - - broker.Logger.Info("Bind: GetClient") - cfClient, err := broker.GetClient() - if err != nil { - broker.Logger.Info("Bind: Error in GetClient") - return binding, err - } - - broker.Logger.Info("Bind: Get Info") - info, _, _, err := cfClient.GetInfo() - if err != nil { - broker.Logger.Info("Bind: Error in Get Info") - - return binding, err - } - - broker.Logger.Info("Bind: GetApplicationByNameAndSpace") - - app, _, err := cfClient.GetApplicationByNameAndSpace(utilities.MakeAppName(details.ServiceID, instanceID), broker.Config.InstanceSpaceGUID) - if err != nil { - broker.Logger.Info("Bind: Error in GetApplicationByNameAndSpace") - return binding, err - } - - broker.Logger.Info("Bind: GetApplicationRoutes") - routes, _, err := cfClient.GetApplicationRoutes(app.GUID) - if err != nil { - broker.Logger.Info("Bind: Error in GetApplicationRoutes") - return binding, err - } - - broker.Logger.Info("Bind: Building binding Credentials") - binding.Credentials = map[string]string{ - "uri": fmt.Sprintf("https://%v", routes[0].URL), - "access_token_uri": fmt.Sprintf("%v/oauth/token", info.UAA()), - "client_id": clientId, - "client_secret": password, - } - - broker.Logger.Info("Bind: Return") - - return binding, nil -} diff --git a/broker/broker.go b/broker/broker.go index 586bc8d..86f7c8e 100755 --- a/broker/broker.go +++ b/broker/broker.go @@ -2,10 +2,11 @@ package broker import ( "context" + "errors" "fmt" - "code.cloudfoundry.org/lager" - brokerapi "github.com/pivotal-cf/brokerapi/domain" + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/broker/implementation" "github.com/starkandwayne/scs-broker/config" ) @@ -14,25 +15,17 @@ const ( ) type SCSBroker struct { - Config config.Config - Logger lager.Logger } -func (broker *SCSBroker) GetServiceByServiceID(serviceID string) (config.Service, error) { - for _, service := range broker.Config.Services { - if service.ServiceID == serviceID { - return service, nil - } - } - - return config.Service{}, fmt.Errorf("No valid service found for %s", serviceID) +func New() *SCSBroker { + return &SCSBroker{} } func (broker *SCSBroker) Services(ctx context.Context) ([]brokerapi.Service, error) { services := []brokerapi.Service{} - for _, service := range broker.Config.Services { + for _, service := range config.Parsed.Services { brokerService := brokerapi.Service{ ID: service.ServiceID, Name: service.ServiceName, @@ -49,7 +42,7 @@ func (broker *SCSBroker) Services(ctx context.Context) ([]brokerapi.Service, err }}, Metadata: &brokerapi.ServiceMetadata{ DisplayName: service.ServiceName, - ImageUrl: fmt.Sprintf("data:image/png;base64,%s", broker.Config.IconImage), + ImageUrl: fmt.Sprintf("data:image/png;base64,%s", config.Parsed.IconImage), }, Tags: []string{ "snw", @@ -60,5 +53,52 @@ func (broker *SCSBroker) Services(ctx context.Context) ([]brokerapi.Service, err } return services, nil +} + +func (broker *SCSBroker) Provision(ctx context.Context, instanceID string, details brokerapi.ProvisionDetails, asyncAllowed bool) (spec brokerapi.ProvisionedServiceSpec, err error) { + return implementation. + ByServiceID(details.ServiceID). + Provision(ctx, instanceID, details, asyncAllowed) +} + +func (broker *SCSBroker) Deprovision(ctx context.Context, instanceID string, details brokerapi.DeprovisionDetails, asyncAllowed bool) (brokerapi.DeprovisionServiceSpec, error) { + return implementation. + ByServiceID(details.ServiceID). + Deprovision(ctx, instanceID, details, asyncAllowed) +} + +func (broker *SCSBroker) Update(ctx context.Context, instanceID string, details brokerapi.UpdateDetails, asyncAllowed bool) (brokerapi.UpdateServiceSpec, error) { + return implementation. + ByServiceID(details.ServiceID). + Update(ctx, instanceID, details, asyncAllowed) +} + +func (broker *SCSBroker) Bind(ctx context.Context, instanceID string, bindingID string, details brokerapi.BindDetails, asyncAllowed bool) (brokerapi.Binding, error) { + return implementation. + ByServiceID(details.ServiceID). + Bind(ctx, instanceID, bindingID, details, asyncAllowed) +} + +func (broker *SCSBroker) Unbind(ctx context.Context, instanceID string, bindingID string, details brokerapi.UnbindDetails, asyncAllowed bool) (brokerapi.UnbindSpec, error) { + return implementation. + ByServiceID(details.ServiceID). + Unbind(ctx, instanceID, bindingID, details, asyncAllowed) +} + +// Here be unimplemented dragons + +func (broker *SCSBroker) LastOperation(ctx context.Context, instanceID string, details brokerapi.PollDetails) (brokerapi.LastOperation, error) { + return brokerapi.LastOperation{}, errors.New("not implemented") +} + +func (broker *SCSBroker) GetBinding(ctx context.Context, instanceID, bindingID string) (brokerapi.GetBindingSpec, error) { + return brokerapi.GetBindingSpec{}, errors.New("not implemented") +} + +func (broker *SCSBroker) GetInstance(ctx context.Context, instanceID string) (brokerapi.GetInstanceDetailsSpec, error) { + return brokerapi.GetInstanceDetailsSpec{}, errors.New("not implemented") +} +func (broker *SCSBroker) LastBindingOperation(ctx context.Context, instanceID, bindingID string, details brokerapi.PollDetails) (brokerapi.LastOperation, error) { + return brokerapi.LastOperation{}, errors.New("not implemented") } diff --git a/broker/client.go b/broker/client.go deleted file mode 100644 index 48f6253..0000000 --- a/broker/client.go +++ /dev/null @@ -1,41 +0,0 @@ -package broker - -import ( - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" - clients "github.com/cloudfoundry-community/go-cf-clients-helper" - "github.com/cloudfoundry-community/go-uaa" -) - -func (broker *SCSBroker) GetClient() (*ccv3.Client, error) { - - config := clients.Config{ - Endpoint: broker.Config.CfConfig.ApiUrl, - SkipSslValidation: broker.Config.CfConfig.SkipSslValidation, - User: broker.Config.CfConfig.CfUsername, - Password: broker.Config.CfConfig.CfPassword, - } - - session, err := clients.NewSession(config) - if err != nil { - return nil, err - } - return session.V3(), err -} - -func (broker *SCSBroker) GetUaaClient() (*uaa.API, error) { - - cf, err := broker.GetClient() - if err != nil { - return nil, err - } - info, _, _, err := cf.GetInfo() - if err != nil { - return nil, err - } - - uaaClient, err := uaa.New(info.UAA(), uaa.WithClientCredentials(broker.Config.CfConfig.UaaClientID, broker.Config.CfConfig.UaaClientSecret, uaa.JSONWebToken), uaa.WithSkipSSLValidation(broker.Config.CfConfig.SkipSslValidation)) - if err != nil { - return nil, err - } - return uaaClient, err -} diff --git a/broker/configserver/bind.go b/broker/configserver/bind.go new file mode 100755 index 0000000..5654a10 --- /dev/null +++ b/broker/configserver/bind.go @@ -0,0 +1,87 @@ +package configserver + +import ( + "context" + "fmt" + + "github.com/cloudfoundry-community/go-uaa" + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/client" + "github.com/starkandwayne/scs-broker/config" + "github.com/starkandwayne/scs-broker/logger" +) + +func (broker *Broker) Bind(ctx context.Context, instanceID, bindingID string, details brokerapi.BindDetails, asyncAllowed bool) (brokerapi.Binding, error) { + dummy := brokerapi.Binding{} + + logger.Info("Bind: GetUAAClient") + + api, err := client.GetUaaClient() + if err != nil { + logger.Info("Bind: Error in getting client") + return dummy, err + } + + clientId := utilities.MakeClientIdForBinding(details.ServiceID, bindingID) + password := utilities.GenClientPassword() + + uaaClient := uaa.Client{ + ClientID: clientId, + AuthorizedGrantTypes: []string{"client_credentials"}, + Authorities: []string{fmt.Sprintf("%s.%v.read", details.ServiceID, instanceID)}, + DisplayName: clientId, + ClientSecret: password, + } + + logger.Info("Bind: got client info") + logger.Info("Bind: Create Client") + _, err = api.CreateClient(uaaClient) + if err != nil { + logger.Info("Bind: Error in CreateClient") + return dummy, err + } + + logger.Info("Bind: GetClient") + cfClient, err := client.GetClient() + if err != nil { + logger.Info("Bind: Error in GetClient") + return dummy, err + } + + logger.Info("Bind: Get Info") + info, _, _, err := cfClient.GetInfo() + if err != nil { + logger.Info("Bind: Error in Get Info") + + return dummy, err + } + + logger.Info("Bind: GetApplicationByNameAndSpace") + + app, _, err := cfClient.GetApplicationByNameAndSpace(utilities.MakeAppName(details.ServiceID, instanceID), config.Parsed.InstanceSpaceGUID) + if err != nil { + logger.Info("Bind: Error in GetApplicationByNameAndSpace") + return dummy, err + } + + logger.Info("Bind: GetApplicationRoutes") + routes, _, err := cfClient.GetApplicationRoutes(app.GUID) + if err != nil { + logger.Info("Bind: Error in GetApplicationRoutes") + return dummy, err + } + + logger.Info("Bind: Building binding Credentials") + + creds := map[string]string{ + "uri": fmt.Sprintf("https://%v", routes[0].URL), + "access_token_uri": fmt.Sprintf("%v/oauth/token", info.UAA()), + "client_id": clientId, + "client_secret": password, + } + + logger.Info("Bind: Return") + + return brokerapi.Binding{Credentials: creds}, nil +} diff --git a/broker/configserver/broker.go b/broker/configserver/broker.go new file mode 100755 index 0000000..1cc29a4 --- /dev/null +++ b/broker/configserver/broker.go @@ -0,0 +1,7 @@ +package configserver + +type Broker struct{} + +func New() *Broker { + return &Broker{} +} diff --git a/broker/create_config_server_instance.go b/broker/configserver/create_instance.go similarity index 62% rename from broker/create_config_server_instance.go rename to broker/configserver/create_instance.go index b40267e..f677d5e 100755 --- a/broker/create_config_server_instance.go +++ b/broker/configserver/create_instance.go @@ -1,30 +1,33 @@ -package broker +package configserver import ( "errors" "fmt" - "os" - "path" "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/client" + "github.com/starkandwayne/scs-broker/config" + "github.com/starkandwayne/scs-broker/fs" + "github.com/starkandwayne/scs-broker/logger" + "github.com/starkandwayne/scs-broker/poll" ) -func (broker *SCSBroker) createConfigServerInstance(serviceId string, instanceId string, jsonparams string, params map[string]string) (string, error) { +func (broker *Broker) createInstance(serviceId string, instanceId string, jsonparams string, params map[string]string) (string, error) { - service, err := broker.GetServiceByServiceID(serviceId) + service, err := config.GetServiceByServiceID(serviceId) if err != nil { return "", err } - cfClient, err := broker.GetClient() + cfClient, err := client.GetClient() if err != nil { return "", errors.New("Couldn't start session: " + err.Error()) } appName := utilities.MakeAppName(serviceId, instanceId) - spaceGUID := broker.Config.InstanceSpaceGUID + spaceGUID := config.Parsed.InstanceSpaceGUID - broker.Logger.Info("Creating Application") + logger.Info("Creating Application") app, _, err := cfClient.CreateApplication( ccv3.Application{ Name: appName, @@ -44,14 +47,14 @@ func (broker *SCSBroker) createConfigServerInstance(serviceId string, instanceId return "", err } - broker.Logger.Info("Updating Environment") + logger.Info("Updating Environment") err = broker.UpdateAppEnvironment(cfClient, &app, &info, serviceId, instanceId, jsonparams, params) if err != nil { return "", err } - broker.Logger.Info("Creating Package") + logger.Info("Creating Package") pkg, _, err := cfClient.CreatePackage( ccv3.Package{ Type: constant.PackageTypeBits, @@ -63,53 +66,50 @@ func (broker *SCSBroker) createConfigServerInstance(serviceId string, instanceId return "", err } - broker.Logger.Info("Uploading Package") + logger.Info("Uploading Package") - jarname := path.Base(service.ServiceDownloadURI) - artifact := broker.Config.ArtifactsDir + "/" + jarname - - fi, err := os.Stat(artifact) + artifact, fi, err := fs.ArtifactStat(service.ServiceDownloadURI) if err != nil { return "", err } - broker.Logger.Info(fmt.Sprintf("Uploadinlsg: %s from %s size(%d)", fi.Name(), artifact, fi.Size())) + logger.Info(fmt.Sprintf("Uploadinlsg: %s from %s size(%d)", fi.Name(), artifact, fi.Size())) upkg, uwarnings, err := cfClient.UploadPackage(pkg, artifact) - broker.showWarnings(uwarnings, upkg) + logger.ShowWarnings(uwarnings, upkg) if err != nil { return "", err } - broker.Logger.Info("Polling Package") - pkg, pwarnings, err := broker.pollPackage(pkg) - broker.showWarnings(pwarnings, pkg) + logger.Info("Polling Package") + pkg, pwarnings, err := poll.Package(pkg) + logger.ShowWarnings(pwarnings, pkg) if err != nil { return "", err } - broker.Logger.Info("Creating Build") + logger.Info("Creating Build") build, cwarnings, err := cfClient.CreateBuild(ccv3.Build{PackageGUID: pkg.GUID}) - broker.showWarnings(cwarnings, build) + logger.ShowWarnings(cwarnings, build) if err != nil { return "", err } - broker.Logger.Info("polling build") - droplet, pbwarnings, err := broker.pollBuild(build.GUID, appName) - broker.showWarnings(pbwarnings, droplet) + logger.Info("polling build") + droplet, pbwarnings, err := poll.Build(build.GUID, appName) + logger.ShowWarnings(pbwarnings, droplet) if err != nil { return "", err } - broker.Logger.Info("set application droplet") + logger.Info("set application droplet") _, _, err = cfClient.SetApplicationDroplet(app.GUID, droplet.GUID) if err != nil { return "", err } domains, _, err := cfClient.GetDomains( - ccv3.Query{Key: ccv3.NameFilter, Values: []string{broker.Config.InstanceDomain}}, + ccv3.Query{Key: ccv3.NameFilter, Values: []string{config.Parsed.InstanceDomain}}, ) if err != nil { return "", err @@ -136,7 +136,7 @@ func (broker *SCSBroker) createConfigServerInstance(serviceId string, instanceId return "", err } - broker.Logger.Info(route.URL) + logger.Info(route.URL) return route.URL, nil } diff --git a/broker/deprovision.go b/broker/configserver/deprovision.go similarity index 61% rename from broker/deprovision.go rename to broker/configserver/deprovision.go index 45df4ee..7d65863 100755 --- a/broker/deprovision.go +++ b/broker/configserver/deprovision.go @@ -1,25 +1,28 @@ -package broker +package configserver import ( "context" "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" - brokerapi "github.com/pivotal-cf/brokerapi/domain" + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/client" + "github.com/starkandwayne/scs-broker/config" + "github.com/starkandwayne/scs-broker/logger" ) -func (broker *SCSBroker) Deprovision(ctx context.Context, instanceID string, details brokerapi.DeprovisionDetails, asyncAllowed bool) (brokerapi.DeprovisionServiceSpec, error) { +func (broker *Broker) Deprovision(ctx context.Context, instanceID string, details brokerapi.DeprovisionDetails, asyncAllowed bool) (brokerapi.DeprovisionServiceSpec, error) { spec := brokerapi.DeprovisionServiceSpec{} - cfClient, err := broker.GetClient() + cfClient, err := client.GetClient() if err != nil { return spec, err } appName := utilities.MakeAppName(details.ServiceID, instanceID) - app, _, err := cfClient.GetApplicationByNameAndSpace(appName, broker.Config.InstanceSpaceGUID) + app, _, err := cfClient.GetApplicationByNameAndSpace(appName, config.Parsed.InstanceSpaceGUID) appNotFound := ccerror.ApplicationNotFoundError{Name: appName} if err == appNotFound { - broker.Logger.Info("app-not-found") + logger.Info("app-not-found") return spec, nil } diff --git a/broker/configserver/provision.go b/broker/configserver/provision.go new file mode 100755 index 0000000..1c1c34d --- /dev/null +++ b/broker/configserver/provision.go @@ -0,0 +1,34 @@ +package configserver + +import ( + "context" + "fmt" + + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/logger" + scsccparser "github.com/starkandwayne/spring-cloud-services-cli-config-parser" +) + +func (broker *Broker) Provision(ctx context.Context, instanceID string, details brokerapi.ProvisionDetails, asyncAllowed bool) (spec brokerapi.ProvisionedServiceSpec, err error) { + logger.Info(fmt.Sprintf("Got these details: %s", details)) + envsetup := scsccparser.EnvironmentSetup{} + + raw := details.RawParameters + if len(raw) == 0 { + raw = []byte("{}") + } + + mapparams, err := envsetup.ParseEnvironmentFromRaw(raw) + if err != nil { + return brokerapi.ProvisionedServiceSpec{}, err + } + + logger.Info("Provisioning a " + details.ServiceID + " service instance") + + url, err := broker.createInstance(details.ServiceID, instanceID, string(details.RawParameters), mapparams) + if err != nil { + return brokerapi.ProvisionedServiceSpec{}, err + } + + return brokerapi.ProvisionedServiceSpec{DashboardURL: url}, nil +} diff --git a/broker/configserver/unbind.go b/broker/configserver/unbind.go new file mode 100755 index 0000000..a02103d --- /dev/null +++ b/broker/configserver/unbind.go @@ -0,0 +1,34 @@ +package configserver + +import ( + "context" + "fmt" + + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/client" + "github.com/starkandwayne/scs-broker/logger" +) + +func (broker *Broker) Unbind(ctx context.Context, instanceID, bindingID string, details brokerapi.UnbindDetails, asyncAllowed bool) (brokerapi.UnbindSpec, error) { + unbind := brokerapi.UnbindSpec{} + + logger.Info("UnBind: GetUAAClient") + api, err := client.GetUaaClient() + if err != nil { + logger.Info("UnBind: Error in GetUAAClient") + return unbind, err + } + + logger.Info("UnBind: makeClientIdForBinding") + clientId := utilities.MakeClientIdForBinding(details.ServiceID, bindingID) + + logger.Info(fmt.Sprintf("UnBind: DeleteClient bindingID:%s clientid %s", bindingID, clientId)) + _, err = api.DeleteClient(clientId) + if err != nil { + logger.Error("UnBind: Error in DeleteClient - will attempt to remove anyway", err) + return unbind, nil + } + logger.Info("UnBind: Return") + return unbind, nil +} diff --git a/broker/update_config_server_instance.go b/broker/configserver/update.go similarity index 62% rename from broker/update_config_server_instance.go rename to broker/configserver/update.go index 2ac1b13..7513b4c 100755 --- a/broker/update_config_server_instance.go +++ b/broker/configserver/update.go @@ -1,24 +1,27 @@ -package broker +package configserver import ( "context" "errors" "code.cloudfoundry.org/lager" - brokerapi "github.com/pivotal-cf/brokerapi/domain" + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/client" + "github.com/starkandwayne/scs-broker/config" + "github.com/starkandwayne/scs-broker/logger" scsccparser "github.com/starkandwayne/spring-cloud-services-cli-config-parser" ) -func (broker *SCSBroker) updateConfigServerInstance(cxt context.Context, instanceID string, details brokerapi.UpdateDetails, asyncAllowed bool) (brokerapi.UpdateServiceSpec, error) { +func (broker *Broker) Update(cxt context.Context, instanceID string, details brokerapi.UpdateDetails, asyncAllowed bool) (brokerapi.UpdateServiceSpec, error) { spec := brokerapi.UpdateServiceSpec{} appName := utilities.MakeAppName(details.ServiceID, instanceID) - spaceGUID := broker.Config.InstanceSpaceGUID + spaceGUID := config.Parsed.InstanceSpaceGUID - broker.Logger.Info("update-service-instance", lager.Data{"plan-id": details.PlanID, "service-id": details.ServiceID}) + logger.Info("update-service-instance", lager.Data{"plan-id": details.PlanID, "service-id": details.ServiceID}) envsetup := scsccparser.EnvironmentSetup{} - cfClient, err := broker.GetClient() + cfClient, err := client.GetClient() if err != nil { return spec, errors.New("Couldn't start session: " + err.Error()) @@ -39,13 +42,13 @@ func (broker *SCSBroker) updateConfigServerInstance(cxt context.Context, instanc return spec, err } - broker.Logger.Info("Updating Environment") + logger.Info("Updating Environment") err = broker.UpdateAppEnvironment(cfClient, &app, &info, details.ServiceID, instanceID, string(details.RawParameters), mapparams) if err != nil { return spec, err } - _, _, err = cfClient.UpdateApplication(app) + _, _, err = cfClient.UpdateApplication(utilities.SafeApp(app)) if err != nil { return spec, err } diff --git a/broker/update_app_environment.go b/broker/configserver/update_app_environment.go similarity index 83% rename from broker/update_app_environment.go rename to broker/configserver/update_app_environment.go index 5436155..07c704f 100755 --- a/broker/update_app_environment.go +++ b/broker/configserver/update_app_environment.go @@ -1,4 +1,4 @@ -package broker +package configserver import ( "fmt" @@ -7,10 +7,11 @@ import ( "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" "code.cloudfoundry.org/cli/types" + "github.com/starkandwayne/scs-broker/config" ) // Updates the app enviornment variables for creating or updating an instance. -func (broker *SCSBroker) UpdateAppEnvironment(cfClient *ccv3.Client, app *ccv3.Application, info *ccv3.Info, kind string, instanceId string, jsonparams string, params map[string]string) error { +func (broker *Broker) UpdateAppEnvironment(cfClient *ccv3.Client, app *ccv3.Application, info *ccv3.Info, kind string, instanceId string, jsonparams string, params map[string]string) error { var profiles []string for key, value := range params { @@ -51,7 +52,7 @@ func (broker *SCSBroker) UpdateAppEnvironment(cfClient *ccv3.Client, app *ccv3.A _, _, err := cfClient.UpdateApplicationEnvironmentVariables(app.GUID, ccv3.EnvironmentVariables{ "SPRING_APPLICATION_JSON": *types.NewFilteredString(jsonparams), "JWK_SET_URI": *types.NewFilteredString(fmt.Sprintf("%v/token_keys", info.UAA())), - "SKIP_SSL_VALIDATION": *types.NewFilteredString(strconv.FormatBool(broker.Config.CfConfig.SkipSslValidation)), + "SKIP_SSL_VALIDATION": *types.NewFilteredString(strconv.FormatBool(config.Parsed.CfConfig.SkipSslValidation)), "REQUIRED_AUDIENCE": *types.NewFilteredString(fmt.Sprintf("%s.%v", kind, instanceId)), "SPRING_PROFILES_ACTIVE": *types.NewFilteredString(profileString.String()), }) diff --git a/broker/create_registry_server_instance.go b/broker/create_registry_server_instance.go deleted file mode 100755 index 207a9c8..0000000 --- a/broker/create_registry_server_instance.go +++ /dev/null @@ -1,229 +0,0 @@ -package broker - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "path" - "strings" - - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" - "github.com/starkandwayne/scs-broker/broker/utilities" -) - -func (broker *SCSBroker) createRegistryServerInstance(serviceId string, instanceId string, jsonparams string, params map[string]string) (string, error) { - service, err := broker.GetServiceByServiceID(serviceId) - if err != nil { - return "", err - } - - cfClient, err := broker.GetClient() - if err != nil { - return "", errors.New("Couldn't start session: " + err.Error()) - } - appName := utilities.MakeAppName(serviceId, instanceId) - spaceGUID := broker.Config.InstanceSpaceGUID - - broker.Logger.Info("Creating Application") - app, _, err := cfClient.CreateApplication( - ccv3.Application{ - Name: appName, - LifecycleType: constant.AppLifecycleTypeBuildpack, - State: constant.ApplicationStopped, - Relationships: ccv3.Relationships{ - constant.RelationshipTypeSpace: ccv3.Relationship{GUID: spaceGUID}, - }, - }, - ) - if err != nil { - return "", err - } - - info, _, _, err := cfClient.GetInfo() - if err != nil { - return "", err - } - - broker.Logger.Info("Updating Environment") - err = broker.UpdateAppEnvironment(cfClient, &app, &info, serviceId, instanceId, jsonparams, params) - - if err != nil { - return "", err - } - - broker.Logger.Info("Creating Package") - pkg, _, err := cfClient.CreatePackage( - ccv3.Package{ - Type: constant.PackageTypeBits, - Relationships: ccv3.Relationships{ - constant.RelationshipTypeApplication: ccv3.Relationship{GUID: app.GUID}, - }, - }) - if err != nil { - return "", err - } - - broker.Logger.Info("Uploading Package") - - jarname := path.Base(service.ServiceDownloadURI) - artifact := broker.Config.ArtifactsDir + "/" + jarname - - fi, err := os.Stat(artifact) - if err != nil { - return "", err - } - - broker.Logger.Info(fmt.Sprintf("Uploading: %s from %s size(%d)", fi.Name(), artifact, fi.Size())) - - upkg, uwarnings, err := cfClient.UploadPackage(pkg, artifact) - broker.showWarnings(uwarnings, upkg) - if err != nil { - return "", err - } - - broker.Logger.Info("Polling Package") - pkg, pwarnings, err := broker.pollPackage(pkg) - broker.showWarnings(pwarnings, pkg) - if err != nil { - - return "", err - } - - broker.Logger.Info("Creating Build") - build, cwarnings, err := cfClient.CreateBuild(ccv3.Build{PackageGUID: pkg.GUID}) - broker.showWarnings(cwarnings, build) - if err != nil { - return "", err - } - - broker.Logger.Info("polling build") - droplet, pbwarnings, err := broker.pollBuild(build.GUID, appName) - broker.showWarnings(pbwarnings, droplet) - if err != nil { - return "", err - } - - broker.Logger.Info("set application droplet") - _, _, err = cfClient.SetApplicationDroplet(app.GUID, droplet.GUID) - if err != nil { - return "", err - } - domains, _, err := cfClient.GetDomains( - ccv3.Query{Key: ccv3.NameFilter, Values: []string{broker.Config.InstanceDomain}}, - ) - if err != nil { - return "", err - } - - if len(domains) == 0 { - return "", errors.New("no domains found for this instance") - } - - route, _, err := cfClient.CreateRoute(ccv3.Route{ - SpaceGUID: spaceGUID, - DomainGUID: domains[0].GUID, - Host: appName, - }) - if err != nil { - return "", err - } - _, err = cfClient.MapRoute(route.GUID, app.GUID) - if err != nil { - return "", err - } - - broker.Logger.Info("handle node count") - // handle the node count - rc := ®istryConfig{} - broker.Logger.Info("jsonparams == " + jsonparams) - rp, err := utilities.ExtractRegistryParams(jsonparams) - if err != nil { - return "", err - } - - if count, found := rp["count"]; found { - if c, ok := count.(int); ok { - if c > 1 { - rc.Clustered() - broker.Logger.Info(fmt.Sprintf("scaling to %d", c)) - err = broker.scaleRegistryServer(cfClient, &app, c, rc) - if err != nil { - return "", err - } - } else { - rc.Standalone() - } - } else { - rc.Standalone() - } - } - - broker.Logger.Info("Updating Environment") - err = broker.UpdateAppEnvironment(cfClient, &app, &info, serviceId, instanceId, rc.String(), params) - - if err != nil { - return "", err - } - - app, _, err = cfClient.UpdateApplicationRestart(app.GUID) - if err != nil { - return "", err - } - - broker.Logger.Info(route.URL) - - return route.URL, nil -} - -type registryConfig struct { - Mode string - Peers []string -} - -func (rc *registryConfig) AddPeer(peer string) { - rc.Peers = append(rc.Peers, peer) -} - -func (rc *registryConfig) Standalone() { - rc.Mode = "standalone" -} - -func (rc *registryConfig) Clustered() { - rc.Mode = "clustered" -} - -func (rc *registryConfig) String() string { - return string(rc.Bytes()) -} - -func (rc *registryConfig) Bytes() []byte { - client := make(map[string]interface{}) - - if rc.Mode == "standalone" { - client["registerWithEureka"] = false - client["fetchRegistry"] = false - } - - if len(rc.Peers) > 0 { - serviceUrl := make(map[string]interface{}) - defaultZone := strings.Join(rc.Peers, ",") - serviceUrl["defaultZone"] = defaultZone - client["serviceUrl"] = serviceUrl - } - - eureka := make(map[string]interface{}) - eureka["client"] = client - - data := make(map[string]interface{}) - data["eureka"] = eureka - - output, err := json.Marshal(data) - if err != nil { - return []byte("{}") - } - - return output - -} diff --git a/broker/implementation/failsafe.go b/broker/implementation/failsafe.go new file mode 100644 index 0000000..8078114 --- /dev/null +++ b/broker/implementation/failsafe.go @@ -0,0 +1,34 @@ +package implementation + +import ( + "context" + "fmt" + + "github.com/pivotal-cf/brokerapi/v7/domain" +) + +func unimplemented(serviceID string) error { + return fmt.Errorf("no broker implemented for service %s", serviceID) +} + +type failsafe struct{} + +func (broker *failsafe) Provision(ctx context.Context, instanceID string, details domain.ProvisionDetails, asyncAllowed bool) (domain.ProvisionedServiceSpec, error) { + return domain.ProvisionedServiceSpec{}, unimplemented(details.ServiceID) +} + +func (broker *failsafe) Deprovision(ctx context.Context, instanceID string, details domain.DeprovisionDetails, asyncAllowed bool) (domain.DeprovisionServiceSpec, error) { + return domain.DeprovisionServiceSpec{}, unimplemented(details.ServiceID) +} + +func (broker *failsafe) Update(ctx context.Context, instanceID string, details domain.UpdateDetails, asyncAllowed bool) (domain.UpdateServiceSpec, error) { + return domain.UpdateServiceSpec{}, unimplemented(details.ServiceID) +} + +func (broker *failsafe) Bind(ctx context.Context, instanceID string, bindingID string, details domain.BindDetails, asyncAllowed bool) (domain.Binding, error) { + return domain.Binding{}, unimplemented(details.ServiceID) +} + +func (broker *failsafe) Unbind(ctx context.Context, instanceID string, bindingID string, details domain.UnbindDetails, asyncAllowed bool) (domain.UnbindSpec, error) { + return domain.UnbindSpec{}, unimplemented(details.ServiceID) +} diff --git a/broker/implementation/implementation.go b/broker/implementation/implementation.go new file mode 100644 index 0000000..28422cd --- /dev/null +++ b/broker/implementation/implementation.go @@ -0,0 +1,46 @@ +package implementation + +import ( + "context" + + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/config" +) + +type Implementation interface { + Provision(context.Context, string, domain.ProvisionDetails, bool) (domain.ProvisionedServiceSpec, error) + Deprovision(context.Context, string, domain.DeprovisionDetails, bool) (domain.DeprovisionServiceSpec, error) + Update(context.Context, string, domain.UpdateDetails, bool) (domain.UpdateServiceSpec, error) + Bind(context.Context, string, string, domain.BindDetails, bool) (domain.Binding, error) + Unbind(context.Context, string, string, domain.UnbindDetails, bool) (domain.UnbindSpec, error) +} + +var implementations map[string]Implementation +var unknown Implementation + +func init() { + implementations = make(map[string]Implementation) + unknown = &failsafe{} +} + +func Register(topic string, imp Implementation) { + implementations[topic] = imp +} + +func For(topic string) Implementation { + if imp, ok := implementations[topic]; ok { + return imp + } + + return unknown +} + +func ByServiceID(serviceID string) Implementation { + for key, service := range config.Parsed.Services { + if service.ServiceID == serviceID { + return For(key) + } + } + + return unknown +} diff --git a/broker/poll_build.go b/broker/poll_build.go deleted file mode 100755 index aa636c3..0000000 --- a/broker/poll_build.go +++ /dev/null @@ -1,56 +0,0 @@ -package broker - -import ( - "errors" - "time" - - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" - "code.cloudfoundry.org/cli/util/configv3" -) - -func (broker *SCSBroker) pollBuild(buildGUID string, appName string) (ccv3.Droplet, ccv3.Warnings, error) { - var allWarnings ccv3.Warnings - - timeout := time.After(configv3.DefaultStagingTimeout) - interval := time.NewTimer(0) - - cfClient, err := broker.GetClient() - if err != nil { - return ccv3.Droplet{}, nil, errors.New("couldn't start session: " + err.Error()) - } - - for { - select { - case <-interval.C: - build, warnings, err := cfClient.GetBuild(buildGUID) - allWarnings = append(allWarnings, warnings...) - if err != nil { - return ccv3.Droplet{}, allWarnings, err - } - - switch build.State { - case constant.BuildFailed: - return ccv3.Droplet{}, allWarnings, errors.New(build.Error) - - case constant.BuildStaged: - droplet, warnings, err := cfClient.GetDroplet(build.DropletGUID) - allWarnings = append(allWarnings, warnings...) - if err != nil { - return ccv3.Droplet{}, allWarnings, err - } - - return ccv3.Droplet{ - GUID: droplet.GUID, - State: droplet.State, - CreatedAt: droplet.CreatedAt, - }, allWarnings, nil - } - - interval.Reset(configv3.DefaultPollingInterval) - - case <-timeout: - return ccv3.Droplet{}, allWarnings, errors.New("staging timed out") - } - } -} diff --git a/broker/poll_package.go b/broker/poll_package.go deleted file mode 100755 index a02e331..0000000 --- a/broker/poll_package.go +++ /dev/null @@ -1,56 +0,0 @@ -package broker - -import ( - "errors" - "fmt" - "time" - - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" - "code.cloudfoundry.org/lager" -) - -func (broker *SCSBroker) pollPackage(pkg ccv3.Package) (ccv3.Package, ccv3.Warnings, error) { - var allWarnings ccv3.Warnings - cfClient, err := broker.GetClient() - if err != nil { - return ccv3.Package{}, nil, errors.New("Couldn't start session: " + err.Error()) - } - - var pkgCache ccv3.Package - - for pkg.State != constant.PackageReady && pkg.State != constant.PackageFailed && pkg.State != constant.PackageExpired { - time.Sleep(1000000000) - ccPkg, warnings, err := cfClient.GetPackage(pkg.GUID) - broker.Logger.Info("polling package state", lager.Data{ - "package_guid": pkg.GUID, - "state": pkg.State, - }) - - broker.showWarnings(warnings, ccPkg) - - allWarnings = append(allWarnings, warnings...) - if err != nil { - return ccv3.Package{}, allWarnings, err - } - pkgCache = pkg - pkg = ccv3.Package(ccPkg) - } - - broker.Logger.Info("polling package final state:", lager.Data{ - "package_guid": pkg.GUID, - "state": pkg.State, - }) - - if pkg.State == constant.PackageFailed { - err := errors.New("package failed") - broker.Logger.Error(fmt.Sprintf("Service Package Error: Package State %s", pkg.State), err, lager.Data{"Orignal Package": pkgCache, "Checked Package": pkg}) - return ccv3.Package{}, allWarnings, err - } else if pkg.State == constant.PackageExpired { - err := errors.New("package expired") - broker.Logger.Error(fmt.Sprintf("Service Package Error: Package State %s", pkg.State), err, lager.Data{"Orignal Package": pkgCache, "Checked Package": pkg}) - return ccv3.Package{}, allWarnings, err - } - - return pkg, allWarnings, nil -} diff --git a/broker/provision.go b/broker/provision.go deleted file mode 100755 index 28d75af..0000000 --- a/broker/provision.go +++ /dev/null @@ -1,43 +0,0 @@ -package broker - -import ( - "context" - "fmt" - - brokerapi "github.com/pivotal-cf/brokerapi/domain" - scsccparser "github.com/starkandwayne/spring-cloud-services-cli-config-parser" -) - -func (broker *SCSBroker) Provision(ctx context.Context, instanceID string, details brokerapi.ProvisionDetails, asyncAllowed bool) (spec brokerapi.ProvisionedServiceSpec, err error) { - broker.Logger.Info(fmt.Sprintf("Got these details: %s", details)) - spec = brokerapi.ProvisionedServiceSpec{} - envsetup := scsccparser.EnvironmentSetup{} - raw := details.RawParameters - if len(raw) == 0 { - raw = []byte("{}") - } - mapparams, err := envsetup.ParseEnvironmentFromRaw(raw) - if err != nil { - return spec, err - } - - broker.Logger.Info("Provisioning a " + details.ServiceID + " service instance") - - var provisioner func(string, string, string, map[string]string) (string, error) - - switch details.ServiceID { - case "service-registry": - provisioner = broker.createRegistryServerInstance - case "config-server": - provisioner = broker.createConfigServerInstance - - } - - url, err := provisioner(details.ServiceID, instanceID, string(details.RawParameters), mapparams) - if err != nil { - return spec, err - } - - spec.DashboardURL = url - return spec, nil -} diff --git a/broker/scale_registry_server.go b/broker/scale_registry_server.go deleted file mode 100755 index c4ee44c..0000000 --- a/broker/scale_registry_server.go +++ /dev/null @@ -1,19 +0,0 @@ -package broker - -import ( - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" - "code.cloudfoundry.org/cli/types" -) - -func (broker *SCSBroker) scaleRegistryServer(cfClient *ccv3.Client, app *ccv3.Application, count int, rc *registryConfig) error { - p := ccv3.Process{ - Type: "web", - Instances: types.NullInt{Value: count, IsSet: true}, - MemoryInMB: types.NullUint64{Value: 0, IsSet: false}, - //DiskInDB: types.NullUint64{Value: 0, IsSet: false}, - } - - _, _, err := cfClient.CreateApplicationProcessScale(app.GUID, p) - - return err -} diff --git a/broker/serviceregistry/bind.go b/broker/serviceregistry/bind.go new file mode 100755 index 0000000..18504bf --- /dev/null +++ b/broker/serviceregistry/bind.go @@ -0,0 +1,37 @@ +package serviceregistry + +import ( + "context" + "fmt" + "strings" + + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/config" + "github.com/starkandwayne/scs-broker/logger" +) + +func (broker *Broker) Bind(ctx context.Context, instanceID, bindingID string, details brokerapi.BindDetails, asyncAllowed bool) (brokerapi.Binding, error) { + dummy := brokerapi.Binding{} + + apps, err := broker.loadNodes(config.Parsed.InstanceSpaceGUID, instanceID) + if err != nil { + return dummy, err + } + + peers := make([]string, 0) + for _, node := range apps { + peers = append(peers, fmt.Sprintf("https://%s", node.Route.URL)) + } + + url := strings.Join(peers, ",") + + logger.Info("Bind: Building binding Credentials") + creds := map[string]string{ + "url": url, + "uri": url, + } + + logger.Info("Bind: Return") + + return brokerapi.Binding{Credentials: creds}, nil +} diff --git a/broker/serviceregistry/broker.go b/broker/serviceregistry/broker.go new file mode 100644 index 0000000..7b4b07b --- /dev/null +++ b/broker/serviceregistry/broker.go @@ -0,0 +1,7 @@ +package serviceregistry + +type Broker struct{} + +func New() *Broker { + return &Broker{} +} diff --git a/broker/serviceregistry/create_instance.go b/broker/serviceregistry/create_instance.go new file mode 100755 index 0000000..bacdb00 --- /dev/null +++ b/broker/serviceregistry/create_instance.go @@ -0,0 +1,53 @@ +package serviceregistry + +import ( + "fmt" + + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/config" +) + +func (broker *Broker) createInstance(serviceId string, instanceId string, jsonparams string, params map[string]string) (string, error) { + rp, err := utilities.ExtractRegistryParams(string(jsonparams)) + if err != nil { + return "", err + } + + desiredCount, err := rp.Count() + if err != nil { + return "", err + } + + rc := utilities.NewRegistryConfig() + + //_, err = broker.deployRegistry(space, serviceId, desiredCount) + deployed, err := broker.deployRegistry(serviceId, instanceId, desiredCount) + if err != nil { + return "", err + } + + // update all apps with a proper config + if desiredCount > 1 { + rc.Clustered() + + for _, pushApp := range deployed { + rc.AddPeer("https", pushApp.Node.Route.URL) + } + } else { + rc.Standalone() + } + + _, err = broker.updateRegistry(deployed.Nodes()) + if err != nil { + return "", err + } + + // restart all apps + + //err := broker.restartRegistry(updated) + //if err != nil { + //return "", err + //} + + return fmt.Sprintf("service-registry-%s.%s", instanceId, config.Parsed.InstanceDomain), nil +} diff --git a/broker/serviceregistry/create_registry_space.go b/broker/serviceregistry/create_registry_space.go new file mode 100644 index 0000000..1d2dcf1 --- /dev/null +++ b/broker/serviceregistry/create_registry_space.go @@ -0,0 +1,29 @@ +package serviceregistry + +import ( + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" + "github.com/starkandwayne/scs-broker/client" +) + +func (broker *Broker) createRegistrySpace(org ccv3.Organization, instanceID string) (ccv3.Space, error) { + api, err := client.GetClient() + if err != nil { + return ccv3.Space{}, err + } + + space, _, err := api.CreateSpace( + ccv3.Space{ + Name: "eureka-" + instanceID, + Relationships: ccv3.Relationships{ + constant.RelationshipTypeOrganization: ccv3.Relationship{GUID: org.GUID}, + }, + }, + ) + + if err != nil { + return space, err + } + + return space, nil +} diff --git a/broker/serviceregistry/delete_node.go b/broker/serviceregistry/delete_node.go new file mode 100644 index 0000000..5e9c663 --- /dev/null +++ b/broker/serviceregistry/delete_node.go @@ -0,0 +1,39 @@ +package serviceregistry + +import ( + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/result" + "github.com/starkandwayne/scs-broker/client" +) + +func (broker *Broker) deleteNode(node *registry.Node, pipeline chan<- *result.DeleteApp) { + spec := brokerapi.DeprovisionServiceSpec{} + + cfClient, err := client.GetClient() + if err != nil { + pipeline <- result.NewDeleteApp().WithError(err) + return + } + + app := node.App + + _, _, err = cfClient.UpdateApplicationStop(app.GUID) + if err != nil { + pipeline <- result.NewDeleteApp().WithError(err) + return + } + + _, _, err = cfClient.DeleteRoute(node.Route.GUID) + if err != nil { + pipeline <- result.NewDeleteApp().WithError(err) + } + + _, _, err = cfClient.DeleteApplication(app.GUID) + if err != nil { + pipeline <- result.NewDeleteApp().WithError(err) + return + } + + pipeline <- result.NewDeleteApp().WithNode(node).WithSpec(spec) +} diff --git a/broker/serviceregistry/deploy_node.go b/broker/serviceregistry/deploy_node.go new file mode 100644 index 0000000..f456936 --- /dev/null +++ b/broker/serviceregistry/deploy_node.go @@ -0,0 +1,150 @@ +package serviceregistry + +import ( + "errors" + "fmt" + + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/result" + "github.com/starkandwayne/scs-broker/client" + "github.com/starkandwayne/scs-broker/config" + "github.com/starkandwayne/scs-broker/fs" + "github.com/starkandwayne/scs-broker/logger" + "github.com/starkandwayne/scs-broker/poll" +) + +func (broker *Broker) deployNode(serviceId string, appName string, pipeline chan<- *result.PushApp) { + service, err := config.GetServiceByServiceID(serviceId) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + cfClient, err := client.GetClient() + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + spaceGUID := config.Parsed.InstanceSpaceGUID + + broker.logDeployNodeInfo("Creating Application", appName, spaceGUID) + app, _, err := cfClient.CreateApplication( + ccv3.Application{ + Name: appName, + LifecycleType: constant.AppLifecycleTypeBuildpack, + State: constant.ApplicationStopped, + Relationships: ccv3.Relationships{ + constant.RelationshipTypeSpace: ccv3.Relationship{GUID: spaceGUID}, + }, + }, + ) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + broker.logDeployNodeInfo("Updating Environment", appName, spaceGUID) + + broker.logDeployNodeInfo("Creating Package", appName, spaceGUID) + pkg, _, err := cfClient.CreatePackage( + ccv3.Package{ + Type: constant.PackageTypeBits, + Relationships: ccv3.Relationships{ + constant.RelationshipTypeApplication: ccv3.Relationship{GUID: app.GUID}, + }, + }) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + broker.logDeployNodeInfo("Uploading Package", appName, spaceGUID) + + artifact, fi, err := fs.ArtifactStat(service.ServiceDownloadURI) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + broker.logDeployNodeInfo( + fmt.Sprintf("Uploading: %s from %s size(%d)", fi.Name(), artifact, fi.Size()), + appName, + spaceGUID, + ) + + upkg, uwarnings, err := cfClient.UploadPackage(pkg, artifact) + logger.ShowWarnings(uwarnings, upkg) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + broker.logDeployNodeInfo("Rolling Package", appName, spaceGUID) + pkg, pwarnings, err := poll.Package(pkg) + logger.ShowWarnings(pwarnings, pkg) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + broker.logDeployNodeInfo("Creating Build", appName, spaceGUID) + build, cwarnings, err := cfClient.CreateBuild(ccv3.Build{PackageGUID: pkg.GUID}) + logger.ShowWarnings(cwarnings, build) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + broker.logDeployNodeInfo("polling build", appName, spaceGUID) + droplet, pbwarnings, err := poll.Build(build.GUID, appName) + logger.ShowWarnings(pbwarnings, droplet) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + broker.logDeployNodeInfo("set application droplet", appName, spaceGUID) + _, _, err = cfClient.SetApplicationDroplet(app.GUID, droplet.GUID) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + domains, _, err := cfClient.GetDomains( + ccv3.Query{Key: ccv3.NameFilter, Values: []string{config.Parsed.InstanceDomain}}, + ) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + if len(domains) == 0 { + pipeline <- result.NewPushApp().WithError(errors.New("no domains found for this instance")) + return + } + + route, _, err := cfClient.CreateRoute(ccv3.Route{ + SpaceGUID: spaceGUID, + DomainGUID: domains[0].GUID, + Host: appName, + }) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + _, err = cfClient.MapRoute(route.GUID, app.GUID) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + app, _, err = cfClient.UpdateApplicationRestart(app.GUID) + if err != nil { + pipeline <- result.NewPushApp().WithError(err) + return + } + + pipeline <- result.NewPushApp().WithApp(app).WithRoute(route) +} diff --git a/broker/serviceregistry/deploy_registry.go b/broker/serviceregistry/deploy_registry.go new file mode 100644 index 0000000..e2f8661 --- /dev/null +++ b/broker/serviceregistry/deploy_registry.go @@ -0,0 +1,75 @@ +package serviceregistry + +import ( + "errors" + "strings" + + "code.cloudfoundry.org/lager" + "github.com/google/uuid" + + //"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/result" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/worker" + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/logger" +) + +type node struct { + Name string + ServiceID string +} + +// func (broker *Broker) deployRegistry(space ccv3.Space, serviceId string, desired int) ([]*result.PushApp, error) { +func (broker *Broker) deployRegistry(serviceId string, instanceId string, desired int) (result.PushAppCollection, error) { + pool := worker.New() + defer pool.StopWait() + + pushedApps := make([]*result.PushApp, 0) + + pipeline := make(chan *result.PushApp, desired) + nodes := make(chan *node, desired) + + for _, name := range utilities.NodeNames(instanceId, desired) { + nodes <- &node{Name: name, ServiceID: serviceId} + } + + for i := 0; i < desired; i++ { + jobID, _ := uuid.NewRandom() + + pool.Submit(func() { + node := <-nodes + logger.Info( + "deploying node", + lager.Data{"job-id": jobID, "node-name": node.Name}, + ) + + //broker.deployNode(space, serviceId, nodeName, pipeline) + broker.deployNode(node.ServiceID, node.Name, pipeline) + }) + + } + + for len(pushedApps) < desired { + pushedApps = append(pushedApps, <-pipeline) + } + + errorsPresent := false + for _, p := range pushedApps { + if p.Error != nil { + errorsPresent = true + } + } + + if errorsPresent { + errs := make([]string, 0) + for _, p := range pushedApps { + if p.Error != nil { + errs = append(errs, p.Error.Error()) + } + } + + return pushedApps, errors.New("errors happened while deploying registry nodes: " + strings.Join(errs, ", ")) + } + + return pushedApps, nil +} diff --git a/broker/serviceregistry/deprovision.go b/broker/serviceregistry/deprovision.go new file mode 100755 index 0000000..947a81a --- /dev/null +++ b/broker/serviceregistry/deprovision.go @@ -0,0 +1,21 @@ +package serviceregistry + +import ( + "context" + + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/config" +) + +func (broker *Broker) Deprovision(ctx context.Context, instanceID string, details brokerapi.DeprovisionDetails, asyncAllowed bool) (brokerapi.DeprovisionServiceSpec, error) { + spec := brokerapi.DeprovisionServiceSpec{} + + deployed, err := broker.loadNodes(config.Parsed.InstanceSpaceGUID, instanceID) + if err != nil { + return spec, err + } + + // so, we could do a whole thing where we explicitly delete all of the + // nodes ... or we could just scale to zero + return spec, broker.scaleRegistry(details.ServiceID, instanceID, deployed, 0) +} diff --git a/broker/serviceregistry/load_nodes.go b/broker/serviceregistry/load_nodes.go new file mode 100644 index 0000000..fe79920 --- /dev/null +++ b/broker/serviceregistry/load_nodes.go @@ -0,0 +1,45 @@ +package serviceregistry + +import ( + "fmt" + "strings" + + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" + "github.com/starkandwayne/scs-broker/client" +) + +func (broker *Broker) loadNodes(spaceGUID string, instanceID string) ([]*registry.Node, error) { + output := make([]*registry.Node, 0) + + client, err := client.GetClient() + if err != nil { + return output, err + } + + candidates, _, err := client.GetApplications( + ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}}, + ) + if err != nil { + return output, err + } + + routeErrors := make([]string, 0) + + prefix := "service-registry-" + instanceID + "-" + for _, prospect := range candidates { + if strings.HasPrefix(prospect.Name, prefix) { + routes, _, err := client.GetApplicationRoutes(prospect.GUID) + if err != nil { + routeErrors = append(routeErrors, fmt.Sprintf("error loading routes for %s - %s", prospect.Name, err.Error())) + } + output = append(output, ®istry.Node{App: prospect, Route: routes[0]}) + } + } + + if len(routeErrors) > 0 { + err = fmt.Errorf("got errors loading nodes: %s", strings.Join(routeErrors, ", ")) + } + + return output, err +} diff --git a/broker/serviceregistry/logging.go b/broker/serviceregistry/logging.go new file mode 100644 index 0000000..fbdf1eb --- /dev/null +++ b/broker/serviceregistry/logging.go @@ -0,0 +1,14 @@ +package serviceregistry + +import ( + "code.cloudfoundry.org/lager" + "github.com/starkandwayne/scs-broker/logger" +) + +func (broker *Broker) logWorkflowError(msg string, workflow string, err error) { + logger.Info(msg, lager.Data{"workflow": workflow, "error": err.Error()}) +} + +func (broker *Broker) logDeployNodeInfo(msg string, app string, space string) { + logger.Info(msg, lager.Data{"app": app, "space": space}) +} diff --git a/broker/serviceregistry/provision.go b/broker/serviceregistry/provision.go new file mode 100755 index 0000000..5eeb2a0 --- /dev/null +++ b/broker/serviceregistry/provision.go @@ -0,0 +1,34 @@ +package serviceregistry + +import ( + "context" + "fmt" + + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/logger" + scsccparser "github.com/starkandwayne/spring-cloud-services-cli-config-parser" +) + +func (broker *Broker) Provision(ctx context.Context, instanceID string, details brokerapi.ProvisionDetails, asyncAllowed bool) (spec brokerapi.ProvisionedServiceSpec, err error) { + logger.Info(fmt.Sprintf("Got these details: %s", details)) + spec = brokerapi.ProvisionedServiceSpec{} + envsetup := scsccparser.EnvironmentSetup{} + raw := details.RawParameters + if len(raw) == 0 { + raw = []byte("{}") + } + mapparams, err := envsetup.ParseEnvironmentFromRaw(raw) + if err != nil { + return spec, err + } + + logger.Info("Provisioning a " + details.ServiceID + " service instance") + + url, err := broker.createInstance(details.ServiceID, instanceID, string(details.RawParameters), mapparams) + if err != nil { + return spec, err + } + + spec.DashboardURL = url + return spec, nil +} diff --git a/broker/serviceregistry/registry/node.go b/broker/serviceregistry/registry/node.go new file mode 100644 index 0000000..34c62b0 --- /dev/null +++ b/broker/serviceregistry/registry/node.go @@ -0,0 +1,8 @@ +package registry + +import "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + +type Node struct { + App ccv3.Application + Route ccv3.Route +} diff --git a/broker/serviceregistry/result/delete_app.go b/broker/serviceregistry/result/delete_app.go new file mode 100644 index 0000000..40cb3af --- /dev/null +++ b/broker/serviceregistry/result/delete_app.go @@ -0,0 +1,55 @@ +package result + +import ( + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" +) + +type DeleteApp struct { + Node *registry.Node + Spec brokerapi.DeprovisionServiceSpec + Error error +} + +func NewDeleteApp() *DeleteApp { + return &DeleteApp{ + Node: ®istry.Node{}, + Spec: brokerapi.DeprovisionServiceSpec{}, + Error: nil, + } +} + +func (result *DeleteApp) WithApp(app ccv3.Application) *DeleteApp { + result.Node.App = app + + return result +} + +func (result *DeleteApp) WithRoute(route ccv3.Route) *DeleteApp { + result.Node.Route = route + + return result +} + +func (result *DeleteApp) WithNode(node *registry.Node) *DeleteApp { + result.Node = node + + return result +} + +func (result *DeleteApp) WithSpec(spec brokerapi.DeprovisionServiceSpec) *DeleteApp { + result.Spec = spec + + return result +} + +func (result *DeleteApp) WithError(err error) *DeleteApp { + result.Error = err + + return result +} + +func (result *DeleteApp) Failure() error { + return result.Error +} diff --git a/broker/serviceregistry/result/push_app.go b/broker/serviceregistry/result/push_app.go new file mode 100644 index 0000000..b96bbf9 --- /dev/null +++ b/broker/serviceregistry/result/push_app.go @@ -0,0 +1,58 @@ +package result + +import ( + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" +) + +type PushApp struct { + Node *registry.Node + Error error +} + +func NewPushApp() *PushApp { + return &PushApp{ + Node: ®istry.Node{}, + Error: nil, + } +} + +func (result *PushApp) WithApp(app ccv3.Application) *PushApp { + result.Node.App = app + + return result +} + +func (result *PushApp) WithRoute(rte ccv3.Route) *PushApp { + result.Node.Route = rte + + return result +} + +func (result *PushApp) WithNode(node *registry.Node) *PushApp { + result.Node = node + + return result +} + +func (result *PushApp) WithError(err error) *PushApp { + result.Error = err + + return result +} + +func (result *PushApp) Failure() error { + return result.Error +} + +type PushAppCollection []*PushApp + +func (c PushAppCollection) Nodes() []*registry.Node { + nodes := make([]*registry.Node, 0) + + for _, p := range c { + nodes = append(nodes, p.Node) + } + + return nodes +} diff --git a/broker/serviceregistry/result/update_app.go b/broker/serviceregistry/result/update_app.go new file mode 100644 index 0000000..65464d3 --- /dev/null +++ b/broker/serviceregistry/result/update_app.go @@ -0,0 +1,33 @@ +package result + +import ( + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" +) + +type UpdateApp struct { + App ccv3.Application + Error error +} + +func NewUpdateApp() *UpdateApp { + return &UpdateApp{ + App: ccv3.Application{}, + Error: nil, + } +} + +func (result *UpdateApp) WithApp(app ccv3.Application) *UpdateApp { + result.App = app + + return result +} + +func (result *UpdateApp) WithError(err error) *UpdateApp { + result.Error = err + + return result +} + +func (result *UpdateApp) Failure() error { + return result.Failure() +} diff --git a/broker/serviceregistry/scale_down.go b/broker/serviceregistry/scale_down.go new file mode 100644 index 0000000..ce37665 --- /dev/null +++ b/broker/serviceregistry/scale_down.go @@ -0,0 +1,64 @@ +package serviceregistry + +import ( + "errors" + "strings" + + "github.com/google/uuid" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/result" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/worker" + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/logger" +) + +func (broker *Broker) scaleDown(serviceID string, instanceID string, existing []*registry.Node, desiredCount int) ([]*registry.Node, error) { + if desiredCount < 0 { + desiredCount = 0 + } + + toDelete := utilities.CalculateDeleteable(existing, desiredCount) + + pool := worker.New() + defer pool.StopWait() + + deletedApps := make([]*result.DeleteApp, 0) + + pipeline := make(chan *result.DeleteApp, len(toDelete)) + deletionQueue := make(chan *registry.Node, len(toDelete)) + + for _, app := range toDelete { + deletionQueue <- app + } + + for i := 0; i < len(toDelete); i++ { + jobID, _ := uuid.NewRandom() + + pool.Submit(func() { + node := <-deletionQueue + logger.JobInfo("deleteing node", jobID.String(), node.App.Name) + + broker.deleteNode(node, pipeline) + }) + + } + + for len(deletedApps) < len(toDelete) { + deletedApps = append(deletedApps, <-pipeline) + } + + errs := make([]string, 0) + for _, p := range deletedApps { + if p.Error != nil { + errs = append(errs, p.Error.Error()) + } + } + + if len(errs) > 0 { + return existing, errors.New("errors happened while deleting registry nodes: " + strings.Join(errs, ", ")) + } + + remainder := existing[:len(existing)-(1+len(toDelete))] + + return remainder, nil +} diff --git a/broker/serviceregistry/scale_registry.go b/broker/serviceregistry/scale_registry.go new file mode 100644 index 0000000..f905caa --- /dev/null +++ b/broker/serviceregistry/scale_registry.go @@ -0,0 +1,45 @@ +package serviceregistry + +import ( + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" +) + +func (broker *Broker) scaleRegistry(serviceID string, instanceID string, existing []*registry.Node, desiredCount int) error { + currentCount := len(existing) + + var nodes []*registry.Node + var err error + + //if desiredCount > currentCount { + //// scale up + //nodes, err := broker.scaleUp(serviceID, instanceID, existing, desiredCount) + //if err != nil { + //return err + //} + //} + + if desiredCount < currentCount { + // scale down + nodes, err = broker.scaleDown(serviceID, instanceID, existing, desiredCount) + if err != nil { + return err + } + } + + if len(nodes) > 0 { + // the node count changed, so we need to update and restart + + _, err = broker.updateRegistry(nodes) + if err != nil { + return err + } + + //_, err = broker.restartRegistry() + //if err != nil { + //return err + //} + } + + // the desired count of nodes already exists, don't scale + return nil +} diff --git a/broker/serviceregistry/scale_up.go b/broker/serviceregistry/scale_up.go new file mode 100644 index 0000000..6ebdaf3 --- /dev/null +++ b/broker/serviceregistry/scale_up.go @@ -0,0 +1 @@ +package serviceregistry diff --git a/broker/serviceregistry/scs_service_instance.go b/broker/serviceregistry/scs_service_instance.go new file mode 100644 index 0000000..6db69d2 --- /dev/null +++ b/broker/serviceregistry/scs_service_instance.go @@ -0,0 +1,9 @@ +package serviceregistry + +type ServiceRegistryInstance struct { + Broker *Broker +} + +type scs_instance interface { + CreateServerInstance(string, string, string, map[string]string) (string, error) +} diff --git a/broker/serviceregistry/unbind.go b/broker/serviceregistry/unbind.go new file mode 100755 index 0000000..4ce1ee0 --- /dev/null +++ b/broker/serviceregistry/unbind.go @@ -0,0 +1,15 @@ +package serviceregistry + +import ( + "context" + + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/logger" +) + +func (broker *Broker) Unbind(ctx context.Context, instanceID, bindingID string, details brokerapi.UnbindDetails, asyncAllowed bool) (brokerapi.UnbindSpec, error) { + unbind := brokerapi.UnbindSpec{} + + logger.Info("unbindRegistryServer: nothing to clean up") + return unbind, nil +} diff --git a/broker/serviceregistry/unbind_registry_server_instance.go b/broker/serviceregistry/unbind_registry_server_instance.go new file mode 100755 index 0000000..97f0af1 --- /dev/null +++ b/broker/serviceregistry/unbind_registry_server_instance.go @@ -0,0 +1,15 @@ +package serviceregistry + +import ( + "context" + + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/logger" +) + +func (broker *Broker) unbindRegistryServerInstance(ctx context.Context, instanceID, bindingID string, details brokerapi.UnbindDetails, asyncAllowed bool) (brokerapi.UnbindSpec, error) { + unbind := brokerapi.UnbindSpec{} + + logger.Info("unbindRegistryServer: nothing to clean up") + return unbind, nil +} diff --git a/broker/serviceregistry/update.go b/broker/serviceregistry/update.go new file mode 100755 index 0000000..63be5b1 --- /dev/null +++ b/broker/serviceregistry/update.go @@ -0,0 +1,30 @@ +package serviceregistry + +import ( + "context" + + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/config" +) + +func (broker *Broker) Update(cxt context.Context, instanceID string, details brokerapi.UpdateDetails, asyncAllowed bool) (brokerapi.UpdateServiceSpec, error) { + spec := brokerapi.UpdateServiceSpec{} + + rp, err := utilities.ExtractRegistryParams(string(details.RawParameters)) + if err != nil { + return spec, err + } + + desiredCount, err := rp.Count() + if err != nil { + return spec, err + } + + deployed, err := broker.loadNodes(config.Parsed.InstanceSpaceGUID, instanceID) + if err != nil { + return spec, err + } + + return spec, broker.scaleRegistry(details.ServiceID, instanceID, deployed, desiredCount) +} diff --git a/broker/serviceregistry/update_app_environment.go b/broker/serviceregistry/update_app_environment.go new file mode 100755 index 0000000..e8ac35d --- /dev/null +++ b/broker/serviceregistry/update_app_environment.go @@ -0,0 +1,64 @@ +package serviceregistry + +import ( + "fmt" + "strconv" + "strings" + + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "code.cloudfoundry.org/cli/types" + "github.com/starkandwayne/scs-broker/config" +) + +// Updates the app enviornment variables for creating or updating an instance. +func (broker *Broker) UpdateAppEnvironment(cfClient *ccv3.Client, app *ccv3.Application, info *ccv3.Info, kind string, instanceId string, jsonparams string, params map[string]string) error { + + var profiles []string + for key, value := range params { + _, _, err := cfClient.UpdateApplicationEnvironmentVariables(app.GUID, ccv3.EnvironmentVariables{ + key: *types.NewFilteredString(value), + }) + + if key == "SPRING_CLOUD_CONFIG_SERVER_GIT_URI" { + profiles = append(profiles, "git") + } + + if key == "SPRING_CLOUD_CONFIG_SERVER_VAULT_HOST" { + profiles = append(profiles, "vault") + } + + if key == "SPRING_CLOUD_CONFIG_SERVER_COMPOSIT" { + profiles = append(profiles, "composit") + } + + if key == "SPRING_CLOUD_CONFIG_SERVER_CREDHUB" { + profiles = append(profiles, "credhub") + } + + if err != nil { + return err + } + } + + var profileString strings.Builder + for index, profile := range profiles { + profileString.WriteString(profile) + + if index < len(profiles)-1 { + profileString.WriteString(", ") + } + } + + _, _, err := cfClient.UpdateApplicationEnvironmentVariables(app.GUID, ccv3.EnvironmentVariables{ + "SPRING_APPLICATION_JSON": *types.NewFilteredString(jsonparams), + "JWK_SET_URI": *types.NewFilteredString(fmt.Sprintf("%v/token_keys", info.UAA())), + "SKIP_SSL_VALIDATION": *types.NewFilteredString(strconv.FormatBool(config.Parsed.CfConfig.SkipSslValidation)), + "REQUIRED_AUDIENCE": *types.NewFilteredString(fmt.Sprintf("%s.%v", kind, instanceId)), + "SPRING_PROFILES_ACTIVE": *types.NewFilteredString(profileString.String()), + }) + if err != nil { + return err + } + + return nil +} diff --git a/broker/serviceregistry/update_node.go b/broker/serviceregistry/update_node.go new file mode 100644 index 0000000..0b52a64 --- /dev/null +++ b/broker/serviceregistry/update_node.go @@ -0,0 +1,44 @@ +package serviceregistry + +import ( + "strconv" + "strings" + + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "code.cloudfoundry.org/cli/types" + + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/result" + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/client" + "github.com/starkandwayne/scs-broker/config" +) + +func (broker *Broker) updateNode(node *registry.Node, rc *utilities.RegistryConfig, pipeline chan<- *result.UpdateApp) { + cfClient, err := client.GetClient() + if err != nil { + pipeline <- result.NewUpdateApp().WithError(err) + return + } + + appJSON := rc.ForNode(node.Route.URL) + trusted := make([]string, 0) + + for _, peer := range rc.Peers { + trusted = append(trusted, peer.Host) + } + + _, _, err = cfClient.UpdateApplicationEnvironmentVariables(node.App.GUID, ccv3.EnvironmentVariables{ + "SKIP_SSL_VALIDATION": *types.NewFilteredString(strconv.FormatBool(config.Parsed.CfConfig.SkipSslValidation)), + //"REQUIRED_AUDIENCE": *types.NewFilteredString(fmt.Sprintf("%s.%v", kind, instanceId)), + //"SPRING_PROFILES_ACTIVE": *types.NewFilteredString(profileString.String()), + "SPRING_APPLICATION_JSON": *types.NewFilteredString(appJSON), + "TRUST_CERTS": *types.NewFilteredString(strings.Join(trusted, ",")), + }) + if err != nil { + pipeline <- result.NewUpdateApp().WithError(err) + return + } + + pipeline <- result.NewUpdateApp().WithApp(node.App) +} diff --git a/broker/serviceregistry/update_registry.go b/broker/serviceregistry/update_registry.go new file mode 100755 index 0000000..9b73ea1 --- /dev/null +++ b/broker/serviceregistry/update_registry.go @@ -0,0 +1,82 @@ +package serviceregistry + +import ( + "errors" + "strings" + + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + + "github.com/google/uuid" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/result" + "github.com/starkandwayne/scs-broker/broker/serviceregistry/worker" + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/logger" +) + +type unupdatedNode struct { + App ccv3.Application + URL string +} + +func (broker *Broker) updateRegistry(deployed []*registry.Node) ([]*result.UpdateApp, error) { + rc := utilities.NewRegistryConfig() + + if len(deployed) > 1 { + rc.Clustered() + + for _, node := range deployed { + rc.AddPeer("https", node.Route.URL) + } + } else { + rc.Standalone() + } + + pool := worker.New() + defer pool.StopWait() + updatedApps := make([]*result.UpdateApp, 0) + + pipeline := make(chan *result.UpdateApp, len(deployed)) + nodes := make(chan *registry.Node, len(deployed)) + + for _, node := range deployed { + nodes <- node + } + + //for i := 0; i < len(deployed); i++ { + for _, _ = range deployed { + jobID, _ := uuid.NewRandom() + + pool.Submit(func() { + node := <-nodes + logger.JobInfo("updating node", jobID.String(), node.App.Name) + + //broker.deployNode(space, serviceId, nodeName, pipeline) + broker.updateNode(node, rc, pipeline) + }) + } + + for len(updatedApps) < len(deployed) { + updatedApps = append(updatedApps, <-pipeline) + } + + errorsPresent := false + for _, p := range updatedApps { + if p.Error != nil { + errorsPresent = true + } + } + + if errorsPresent { + errs := make([]string, 0) + for _, p := range updatedApps { + if p.Error != nil { + errs = append(errs, p.Error.Error()) + } + } + + return updatedApps, errors.New("errors happened while updating registry nodes: " + strings.Join(errs, ", ")) + } + + return updatedApps, nil +} diff --git a/broker/serviceregistry/update_registry_environment.go b/broker/serviceregistry/update_registry_environment.go new file mode 100755 index 0000000..50761a2 --- /dev/null +++ b/broker/serviceregistry/update_registry_environment.go @@ -0,0 +1,55 @@ +package serviceregistry + +import ( + "encoding/json" + "fmt" + + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "github.com/ess/hype" + + "github.com/starkandwayne/scs-broker/broker/utilities" + "github.com/starkandwayne/scs-broker/client" + "github.com/starkandwayne/scs-broker/logger" +) + +func (broker *Broker) UpdateRegistryEnvironment(app *ccv3.Application, url string, rc *utilities.RegistryConfig) error { + client, err := client.GetClient() + if err != nil { + return err + } + + routes, _, err := client.GetApplicationRoutes(app.GUID) + if err != nil { + return err + } + + peers, err := json.Marshal(rc.Peers) + if err != nil { + return err + } + + logger.Info("update registry environment got these peers: " + string(peers)) + + beast, err := hype.New(fmt.Sprintf("https://%s", routes[0].URL)) + if err != nil { + return err + } + + logger.Info("setting the fucking peers") + + for _, _ = range rc.Peers { + resp := beast. + WithoutTLSVerification(). + Post("cf-config/peers", nil, peers). + WithHeader(hype.NewHeader("Accept", "application/json")). + WithHeader(hype.NewHeader("Content-Type", "application/json")). + //WithHeader(hype.NewHeader("X-Cf-App-Instance", fmt.Sprintf("%s:%d", app.GUID, peer.Index))). + Response() + + if !resp.Okay() { + return resp.Error() + } + } + + return nil +} diff --git a/broker/serviceregistry/worker/worker.go b/broker/serviceregistry/worker/worker.go new file mode 100644 index 0000000..ff5165e --- /dev/null +++ b/broker/serviceregistry/worker/worker.go @@ -0,0 +1,9 @@ +package worker + +import ( + "github.com/gammazero/workerpool" +) + +func New() *workerpool.WorkerPool { + return workerpool.New(5) +} diff --git a/broker/show_warnings.go b/broker/show_warnings.go deleted file mode 100755 index ec03816..0000000 --- a/broker/show_warnings.go +++ /dev/null @@ -1,17 +0,0 @@ -package broker - -import ( - "fmt" - - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" - "code.cloudfoundry.org/lager" -) - -func (broker *SCSBroker) showWarnings(warnings ccv3.Warnings, subject interface{}) { - broker.Logger.Info(fmt.Sprintf("NOTICE: %d warning(s) were detected!", len(warnings)), lager.Data{"Subject": subject}) - - for warn := range warnings { - w := warnings[warn] - broker.Logger.Info(fmt.Sprintf("Warning(#%d): %s ", warn+1, w)) - } -} diff --git a/broker/unbind.go b/broker/unbind.go deleted file mode 100755 index a0caf97..0000000 --- a/broker/unbind.go +++ /dev/null @@ -1,32 +0,0 @@ -package broker - -import ( - "context" - "fmt" - - brokerapi "github.com/pivotal-cf/brokerapi/domain" - "github.com/starkandwayne/scs-broker/broker/utilities" -) - -func (broker *SCSBroker) Unbind(ctx context.Context, instanceID, bindingID string, details brokerapi.UnbindDetails, asyncAllowed bool) (brokerapi.UnbindSpec, error) { - unbind := brokerapi.UnbindSpec{} - - broker.Logger.Info("UnBind: GetUAAClient") - api, err := broker.GetUaaClient() - if err != nil { - broker.Logger.Info("UnBind: Error in GetUAAClient") - return unbind, err - } - - broker.Logger.Info("UnBind: makeClientIdForBinding") - clientId := utilities.MakeClientIdForBinding(details.ServiceID, bindingID) - - broker.Logger.Info(fmt.Sprintf("UnBind: DeleteClient bindingID:%s clientid %s", bindingID, clientId)) - _, err = api.DeleteClient(clientId) - if err != nil { - broker.Logger.Error("UnBind: Error in DeleteClient - will attempt to remove anyway", err) - return unbind, nil - } - broker.Logger.Info("UnBind: Return") - return unbind, nil -} diff --git a/broker/unimplemented.go b/broker/unimplemented.go deleted file mode 100755 index a186ea9..0000000 --- a/broker/unimplemented.go +++ /dev/null @@ -1,24 +0,0 @@ -package broker - -import ( - "context" - "errors" - - brokerapi "github.com/pivotal-cf/brokerapi/domain" -) - -func (broker *SCSBroker) LastOperation(ctx context.Context, instanceID string, details brokerapi.PollDetails) (brokerapi.LastOperation, error) { - return brokerapi.LastOperation{}, errors.New("not implemented") -} - -func (broker *SCSBroker) GetBinding(ctx context.Context, instanceID, bindingID string) (brokerapi.GetBindingSpec, error) { - return brokerapi.GetBindingSpec{}, errors.New("not implemented") -} - -func (broker *SCSBroker) GetInstance(ctx context.Context, instanceID string) (brokerapi.GetInstanceDetailsSpec, error) { - return brokerapi.GetInstanceDetailsSpec{}, errors.New("not implemented") -} - -func (broker *SCSBroker) LastBindingOperation(ctx context.Context, instanceID, bindingID string, details brokerapi.PollDetails) (brokerapi.LastOperation, error) { - return brokerapi.LastOperation{}, errors.New("not implemented") -} diff --git a/broker/update.go b/broker/update.go deleted file mode 100755 index 1f0b1a9..0000000 --- a/broker/update.go +++ /dev/null @@ -1,21 +0,0 @@ -package broker - -import ( - "context" - - brokerapi "github.com/pivotal-cf/brokerapi/domain" -) - -func (broker *SCSBroker) Update(cxt context.Context, instanceID string, details brokerapi.UpdateDetails, asyncAllowed bool) (brokerapi.UpdateServiceSpec, error) { - - var updater func(context.Context, string, brokerapi.UpdateDetails, bool) (brokerapi.UpdateServiceSpec, error) - - switch details.ServiceID { - case "service-registry": - updater = broker.updateRegistryServerInstance - case "config-server": - updater = broker.updateConfigServerInstance - } - - return updater(cxt, instanceID, details, asyncAllowed) -} diff --git a/broker/update_registry_server_instance.go b/broker/update_registry_server_instance.go deleted file mode 100755 index 8df4d47..0000000 --- a/broker/update_registry_server_instance.go +++ /dev/null @@ -1,91 +0,0 @@ -package broker - -import ( - "context" - "errors" - - "code.cloudfoundry.org/lager" - brokerapi "github.com/pivotal-cf/brokerapi/domain" - "github.com/starkandwayne/scs-broker/broker/utilities" - scsccparser "github.com/starkandwayne/spring-cloud-services-cli-config-parser" -) - -func (broker *SCSBroker) updateRegistryServerInstance(cxt context.Context, instanceID string, details brokerapi.UpdateDetails, asyncAllowed bool) (brokerapi.UpdateServiceSpec, error) { - spec := brokerapi.UpdateServiceSpec{} - - appName := utilities.MakeAppName(details.ServiceID, instanceID) - spaceGUID := broker.Config.InstanceSpaceGUID - - broker.Logger.Info("update-service-instance", lager.Data{"plan-id": details.PlanID, "service-id": details.ServiceID}) - envsetup := scsccparser.EnvironmentSetup{} - cfClient, err := broker.GetClient() - - if err != nil { - return spec, errors.New("Couldn't start session: " + err.Error()) - } - - info, _, _, err := cfClient.GetInfo() - if err != nil { - return spec, err - } - - app, _, err := cfClient.GetApplicationByNameAndSpace(appName, spaceGUID) - if err != nil { - return spec, errors.New("Couldn't find app session: " + err.Error()) - } - - mapparams, err := envsetup.ParseEnvironmentFromRaw(details.RawParameters) - if err != nil { - return spec, err - } - - broker.Logger.Info("Updating Environment") - err = broker.UpdateAppEnvironment(cfClient, &app, &info, details.ServiceID, instanceID, string(details.RawParameters), mapparams) - if err != nil { - return spec, err - } - - broker.Logger.Info("Updating application") - _, _, err = cfClient.UpdateApplication(app) - if err != nil { - return spec, err - } - - broker.Logger.Info("handling node count") - // handle the node count - rc := ®istryConfig{} - rp, err := utilities.ExtractRegistryParams(string(details.RawParameters)) - if err != nil { - return spec, err - } - - if count, found := rp["count"]; found { - if c, ok := count.(int); ok { - if c > 1 { - rc.Clustered() - err = broker.scaleRegistryServer(cfClient, &app, c, rc) - if err != nil { - return spec, err - } - } else { - rc.Standalone() - } - } else { - rc.Standalone() - } - } - - broker.Logger.Info("Updating Environment") - err = broker.UpdateAppEnvironment(cfClient, &app, &info, details.ServiceID, instanceID, rc.String(), mapparams) - - if err != nil { - return spec, err - } - - _, _, err = cfClient.UpdateApplicationRestart(app.GUID) - if err != nil { - return spec, err - } - - return spec, nil -} diff --git a/broker/utilities/calculate_deletable_nodes.go b/broker/utilities/calculate_deletable_nodes.go new file mode 100644 index 0000000..bcb24c9 --- /dev/null +++ b/broker/utilities/calculate_deletable_nodes.go @@ -0,0 +1,15 @@ +package utilities + +import ( + "github.com/starkandwayne/scs-broker/broker/serviceregistry/registry" +) + +func CalculateDeleteable(existing []*registry.Node, desired int) []*registry.Node { + toDelete := make([]*registry.Node, 0) + + for len(existing)-len(toDelete) > desired { + toDelete = append(toDelete, existing[len(existing)-(1+len(toDelete))]) + } + + return toDelete +} diff --git a/broker/utilities/collected_errors.go b/broker/utilities/collected_errors.go new file mode 100644 index 0000000..ad850af --- /dev/null +++ b/broker/utilities/collected_errors.go @@ -0,0 +1,27 @@ +package utilities + +import ( + "fmt" + "strings" +) + +type fallable interface { + Failure() error +} + +func CollectedErrors(nodes []fallable, msg string) error { + c := make([]string, 0) + + for _, node := range nodes { + err := node.Failure() + if err != nil { + c = append(c, err.Error()) + } + } + + if len(c) > 0 { + return fmt.Errorf("%s: %s", msg, strings.Join(c, ",")) + } + + return nil +} diff --git a/broker/utilities/manifest.go b/broker/utilities/manifest.go new file mode 100644 index 0000000..7e1531d --- /dev/null +++ b/broker/utilities/manifest.go @@ -0,0 +1,31 @@ +package utilities + +import "gopkg.in/yaml.v2" + +func NewManifest(appName string, webCount uint) *Manifest { + application := &ManifestApplication{ + Name: appName, + Instances: webCount, + DefaultRoute: false, + } + + return &Manifest{ + Applications: []*ManifestApplication{ + application, + }, + } +} + +type Manifest struct { + Applications []*ManifestApplication `yaml:"applications"` +} + +func (m *Manifest) Bytes() ([]byte, error) { + return yaml.Marshal(m) +} + +type ManifestApplication struct { + Name string `yaml:"name"` + Instances uint `yaml:"instances"` + DefaultRoute bool `yaml:"default_route"` +} diff --git a/broker/utilities/node_names.go b/broker/utilities/node_names.go new file mode 100644 index 0000000..7eec2dc --- /dev/null +++ b/broker/utilities/node_names.go @@ -0,0 +1,15 @@ +package utilities + +import ( + "fmt" +) + +func NodeNames(instanceID string, desiredCount int) []string { + names := make([]string, 0) + + for len(names) < desiredCount { + names = append(names, fmt.Sprintf("service-registry-%s-%05d", instanceID, len(names))) + } + + return names +} diff --git a/broker/utilities/registry_config.go b/broker/utilities/registry_config.go new file mode 100644 index 0000000..2f280e3 --- /dev/null +++ b/broker/utilities/registry_config.go @@ -0,0 +1,78 @@ +package utilities + +import ( + "encoding/json" + "fmt" + "strings" +) + +func NewRegistryConfig() *RegistryConfig { + rc := &RegistryConfig{} + rc.Standalone() + + return rc +} + +type RegistryPeer struct { + //Index int `json:"index"` + Scheme string `json:"scheme"` + Host string `json:"host"` + //Port int `json:"port"` +} + +func (peer *RegistryPeer) String() string { + return fmt.Sprintf("%s://%s", peer.Scheme, peer.Host) +} + +type RegistryConfig struct { + Mode string + Peers []*RegistryPeer +} + +func (rc *RegistryConfig) AddPeer(scheme string, host string) { + rc.Peers = append(rc.Peers, &RegistryPeer{Scheme: scheme, Host: host}) +} + +func (rc *RegistryConfig) Standalone() { + rc.Mode = "standalone" +} + +func (rc *RegistryConfig) Clustered() { + rc.Mode = "clustered" +} + +func (rc *RegistryConfig) ForNode(node string) string { + client := make(map[string]interface{}) + m := rc.Mode == "clustered" + + client["registerWithEureka"] = m + client["fetchRegistry"] = m + + if m { + peers := make([]string, 0) + for _, peer := range rc.Peers { + if peer.Host != node { + peers = append(peers, peer.String()) + } + } + + serviceUrl := make(map[string]interface{}) + defaultZone := strings.Join(peers, ",") + serviceUrl["defaultZone"] = defaultZone + client["serviceUrl"] = serviceUrl + } + + eureka := make(map[string]interface{}) + eureka["client"] = client + + data := make(map[string]interface{}) + data["eureka"] = eureka + + output, err := json.Marshal(data) + if err != nil { + return "{}" + } + + return string(output) + +} diff --git a/broker/utilities/registry_params.go b/broker/utilities/registry_params.go new file mode 100644 index 0000000..74bc12d --- /dev/null +++ b/broker/utilities/registry_params.go @@ -0,0 +1,40 @@ +package utilities + +import ( + "encoding/json" + "fmt" +) + +func ExtractRegistryParams(details string) (*RegistryParams, error) { + // just in case we got an empty payload + if len(details) == 0 { + details = `{"count" : 1}` + } + + rp := NewRegistryParams() + + if err := json.Unmarshal([]byte(details), rp); err != nil { + return nil, err + } + + return rp, nil +} + +func NewRegistryParams() *RegistryParams { + return &RegistryParams{RawCount: 1} +} + +type RegistryParams struct { + RawCount int `json:"count"` + ApplicationSecurityGroup string `json:"application_security_group"` +} + +func (rp *RegistryParams) Count() (int, error) { + var err error = nil + + if rp.RawCount < 1 { + err = fmt.Errorf("invalid node count: %d", rp.RawCount) + } + + return rp.RawCount, err +} diff --git a/broker/utilities/safe_app.go b/broker/utilities/safe_app.go new file mode 100644 index 0000000..b7b7fe2 --- /dev/null +++ b/broker/utilities/safe_app.go @@ -0,0 +1,15 @@ +package utilities + +import "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + +func SafeApp(app ccv3.Application) ccv3.Application { + return ccv3.Application{ + GUID: app.GUID, + StackName: app.StackName, + LifecycleBuildpacks: app.LifecycleBuildpacks, + LifecycleType: app.LifecycleType, + Metadata: app.Metadata, + Name: app.Name, + State: app.State, + } +} diff --git a/broker/utilities/utilities.go b/broker/utilities/utilities.go index 3c0163c..8a3afe2 100644 --- a/broker/utilities/utilities.go +++ b/broker/utilities/utilities.go @@ -1,14 +1,12 @@ package utilities import ( - "encoding/json" "errors" - "fmt" "math/rand" "strings" "time" - brokerapi "github.com/pivotal-cf/brokerapi/domain" + brokerapi "github.com/pivotal-cf/brokerapi/v7/domain" ) const ( @@ -72,30 +70,3 @@ func MakeClientIdForBinding(serviceId string, bindingId string) string { func MakeAppName(serviceId string, instanceId string) string { return serviceId + "-" + instanceId } - -func ExtractRegistryParams(details string) (map[string]interface{}, error) { - // decode the raw params - decoded := make(map[string]interface{}) - if err := json.Unmarshal([]byte(details), &decoded); err != nil { - return nil, err - } - - // get the registry-specific params that affect broker operations - rp := RegistryParams{} - - rp.Merge("count", decoded) - rp.Merge("application-security-groups", decoded) - for key, _ := range rp { - fmt.Println(key) - } - - return rp, nil -} - -type RegistryParams map[string]interface{} - -func (rp RegistryParams) Merge(key string, other map[string]interface{}) { - if value, found := other[key]; found { - rp[key] = value - } -} diff --git a/cf/broker_config.yml b/cf/broker_config.yml index ff4b13f..5973399 100644 --- a/cf/broker_config.yml +++ b/cf/broker_config.yml @@ -9,13 +9,15 @@ instance_domain: (( param "Please override instance_domain" )) artifacts_directory: "/app/artifacts" services: - - service_name: config-server + configserver: + service_name: config-server service_id: config-server service_plan_id: default service_plan_name: default service_description: Broker to create config-servers service_download_uri: (( param "Please override config_server_download_uri" )) - - service_name: service-registry + serviceregistry: + service_name: service-registry service_id: service-registry service_plan_id: default service_plan_name: default @@ -27,4 +29,4 @@ cf_username: (( param "Please override uaa_config.username" )) cf_password: (( param "Please override uaa_config.password" )) uaa_client_id: (( param "Please override uaa_config.client_id" )) - uaa_client_secret: (( param "Please override uaa_config.client_secret" )) \ No newline at end of file + uaa_client_secret: (( param "Please override uaa_config.client_secret" )) diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..a547312 --- /dev/null +++ b/client/client.go @@ -0,0 +1,54 @@ +package client + +import ( + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + clients "github.com/cloudfoundry-community/go-cf-clients-helper" + cf "github.com/cloudfoundry-community/go-cfclient" + "github.com/cloudfoundry-community/go-uaa" + "github.com/starkandwayne/scs-broker/config" +) + +func GetClient() (*ccv3.Client, error) { + + config := clients.Config{ + Endpoint: config.Parsed.CfConfig.ApiUrl, + SkipSslValidation: config.Parsed.CfConfig.SkipSslValidation, + User: config.Parsed.CfConfig.CfUsername, + Password: config.Parsed.CfConfig.CfPassword, + } + + session, err := clients.NewSession(config) + if err != nil { + return nil, err + } + return session.V3(), err +} + +func GetCommunity() (*cf.Client, error) { + config := &cf.Config{ + ApiAddress: config.Parsed.CfConfig.ApiUrl, + SkipSslValidation: config.Parsed.CfConfig.SkipSslValidation, + Username: config.Parsed.CfConfig.CfUsername, + Password: config.Parsed.CfConfig.CfPassword, + } + + return cf.NewClient(config) +} + +func GetUaaClient() (*uaa.API, error) { + + cf, err := GetClient() + if err != nil { + return nil, err + } + info, _, _, err := cf.GetInfo() + if err != nil { + return nil, err + } + + uaaClient, err := uaa.New(info.UAA(), uaa.WithClientCredentials(config.Parsed.CfConfig.UaaClientID, config.Parsed.CfConfig.UaaClientSecret, uaa.JSONWebToken), uaa.WithSkipSSLValidation(config.Parsed.CfConfig.SkipSslValidation)) + if err != nil { + return nil, err + } + return uaaClient, err +} diff --git a/config/config.go b/config/config.go index 04c6b3e..aaba371 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "os" "gopkg.in/yaml.v3" @@ -9,21 +10,21 @@ import ( const ConfigEnvVarName string = "SCS_BROKER_CONFIG" type Config struct { - Auth Auth `yaml:"broker_auth"` - BrokerName string `yaml:"broker_name"` - BrokerID string `yaml:"broker_id"` - ArtifactsDir string `yaml:"artifacts_directory"` - CfConfig CfConfig `yaml:"cloud_foundry_config"` - Description string `yaml:"description"` - LongDescription string `yaml:"long_description"` - ProviderDisplayName string `yaml:"provider_display_name"` - DocumentationURL string `yaml:"documentation_url"` - SupportURL string `yaml:"support_url"` - DisplayName string `yaml:"display_name"` - IconImage string `yaml:"icon_image"` - InstanceSpaceGUID string `yaml:"instance_space_guid"` - InstanceDomain string `yaml:"instance_domain"` - Services []Service `yaml:"services"` + Auth *Auth `yaml:"broker_auth"` + BrokerName string `yaml:"broker_name"` + BrokerID string `yaml:"broker_id"` + ArtifactsDir string `yaml:"artifacts_directory"` + CfConfig *CfConfig `yaml:"cloud_foundry_config"` + Description string `yaml:"description"` + LongDescription string `yaml:"long_description"` + ProviderDisplayName string `yaml:"provider_display_name"` + DocumentationURL string `yaml:"documentation_url"` + SupportURL string `yaml:"support_url"` + DisplayName string `yaml:"display_name"` + IconImage string `yaml:"icon_image"` + InstanceSpaceGUID string `yaml:"instance_space_guid"` + InstanceDomain string `yaml:"instance_domain"` + Services map[string]*Service `yaml:"services"` } type Auth struct { @@ -32,12 +33,14 @@ type Auth struct { } type Service struct { - ServiceName string `yaml:"service_name"` - ServiceID string `yaml:"service_id"` - ServicePlanID string `yaml:"service_plan_id"` - ServicePlanName string `yaml:"service_plan_name"` - ServiceDescription string `yaml:"service_description"` - ServiceDownloadURI string `yaml:"service_download_uri"` + ServiceName string `yaml:"service_name"` + ServiceID string `yaml:"service_id"` + ServicePlanID string `yaml:"service_plan_id"` + ServicePlanName string `yaml:"service_plan_name"` + ServiceDescription string `yaml:"service_description"` + ServiceDownloadURI string `yaml:"service_download_uri"` + ServiceOrganizationGUID string `yaml:"service_organization_guid"` + ServiceImplementation string `yaml:"service_implementation"` } type CfConfig struct { @@ -49,12 +52,23 @@ type CfConfig struct { UaaClientSecret string `yaml:"uaa_client_secret"` } -func ParseConfig() (Config, error) { +var Parsed *Config = &Config{Services: make(map[string]*Service)} + +func ParseConfig() error { configJson := os.Getenv(ConfigEnvVarName) if configJson == "" { - panic(ConfigEnvVarName + " not set") + return fmt.Errorf("%s not set", ConfigEnvVarName) + } + + return yaml.Unmarshal([]byte(configJson), Parsed) +} + +func GetServiceByServiceID(serviceID string) (*Service, error) { + for _, service := range Parsed.Services { + if service.ServiceID == serviceID { + return service, nil + } } - var config Config - return config, yaml.Unmarshal([]byte(configJson), &config) + return nil, fmt.Errorf("no valid service found for %s", serviceID) } diff --git a/fs/paths.go b/fs/paths.go new file mode 100644 index 0000000..e9bbf63 --- /dev/null +++ b/fs/paths.go @@ -0,0 +1,17 @@ +package fs + +import ( + "os" + "path" + "path/filepath" + + "github.com/starkandwayne/scs-broker/config" +) + +func ArtifactStat(filename string) (string, os.FileInfo, error) { + artifact := filepath.Join(config.Parsed.ArtifactsDir, path.Base(filename)) + + stat, err := os.Stat(artifact) + + return artifact, stat, err +} diff --git a/go.mod b/go.mod index 90e832e..f042665 100644 --- a/go.mod +++ b/go.mod @@ -16,19 +16,17 @@ require ( code.cloudfoundry.org/cli v6.51.0+incompatible code.cloudfoundry.org/lager v2.0.0+incompatible github.com/cloudfoundry-community/go-cf-clients-helper v1.0.1 + github.com/cloudfoundry-community/go-cfclient v0.0.0-20220930021109-9c4e6c59ccf1 github.com/cloudfoundry-community/go-uaa v0.3.1 - github.com/drewolson/testflight v1.0.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.8 // indirect - github.com/gorilla/mux v1.7.4 // indirect + github.com/ess/hype v1.1.5 + github.com/gammazero/workerpool v1.1.2 + github.com/google/uuid v1.1.0 github.com/mattn/go-colorable v0.1.11 // indirect github.com/pborman/uuid v1.2.0 // indirect - github.com/pivotal-cf/brokerapi v6.4.2+incompatible + github.com/pivotal-cf/brokerapi/v7 v7.4.0 github.com/starkandwayne/spring-cloud-services-cli-config-parser v1.0.2 github.com/stretchr/testify v1.7.0 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect - google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index e8664a7..809f3bb 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,7 @@ code.cloudfoundry.org/cli-plugin-repo v0.0.0-20200603234120-1005fc12b512 h1:D96D code.cloudfoundry.org/cli-plugin-repo v0.0.0-20200603234120-1005fc12b512/go.mod h1:R1EiyOAr7lW0l/YkZNqItUNZ01Q/dYUfbTn4X4Z+82M= code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk= code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI= +code.cloudfoundry.org/lager v1.1.1-0.20191008172124-a9afc05ee5be/go.mod h1:O2sS7gKP3HM2iemG+EnwvyNQK7pTSC6Foi4QiMp9sSk= code.cloudfoundry.org/lager v2.0.0+incompatible h1:WZwDKDB2PLd/oL+USK4b4aEjUymIej9My2nUQ9oWEwQ= code.cloudfoundry.org/lager v2.0.0+incompatible/go.mod h1:O2sS7gKP3HM2iemG+EnwvyNQK7pTSC6Foi4QiMp9sSk= code.cloudfoundry.org/tlsconfig v0.0.0-20200131000646-bbe0f8da39b3 h1:2Qal+q+tw/DmDOoJBWwDCPE3lIJNj/1o7oMkkb2c5SI= @@ -17,6 +18,8 @@ code.cloudfoundry.org/tlsconfig v0.0.0-20200131000646-bbe0f8da39b3/go.mod h1:eTb code.cloudfoundry.org/ykk v0.0.0-20170424192843-e4df4ce2fd4d h1:M+zXqtXJqcsmpL76aU0tdl1ho23eYa4axYoM4gD62UA= code.cloudfoundry.org/ykk v0.0.0-20170424192843-e4df4ce2fd4d/go.mod h1:YUJiVOr5xl0N/RjMxM1tHmgSpBbi5UM+KoVR5AoejO0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/SermoDigital/jose v0.9.2-0.20161205224733-f6df55f235c2 h1:koK7z0nSsRiRiBWwa+E714Puh+DO+ZRdIyAXiXzL+lg= github.com/SermoDigital/jose v0.9.2-0.20161205224733-f6df55f235c2/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= github.com/apoydence/eachers v0.0.0-20181020210610-23942921fe77 h1:afT88tB6u9JCKQZVAAaa9ICz/uGn5Uw9ekn6P22mYKM= @@ -35,6 +38,8 @@ github.com/charlievieth/fs v0.0.0-20170613215519-7dc373669fa1/go.mod h1:sAoA1zHC github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudfoundry-community/go-cf-clients-helper v1.0.1 h1:4WEwQc8/EiqzsARhw2H4W06HG4K9MmON31gESLe8eWc= github.com/cloudfoundry-community/go-cf-clients-helper v1.0.1/go.mod h1:JTpxaM6wDIrsw2VTWUwUdRU7EuRIVie58+EPGeOM1oY= +github.com/cloudfoundry-community/go-cfclient v0.0.0-20220930021109-9c4e6c59ccf1 h1:ef0OsiQjSQggHrLFAMDRiu6DfkVSElA5jfG1/Nkyu6c= +github.com/cloudfoundry-community/go-cfclient v0.0.0-20220930021109-9c4e6c59ccf1/go.mod h1:sgaEj3tRn0hwe7GPdEUwxrdOqjBzyjyvyOCGf1OQyZY= github.com/cloudfoundry-community/go-uaa v0.3.1 h1:qmiTqE8Be3zJoL2wcwddVnbRNMDhOrURZbWOUQ2ibJQ= github.com/cloudfoundry-community/go-uaa v0.3.1/go.mod h1:m3JOryy7cx+7QLxuwB+bXuAx5AUJ3W9XhRUBu6Eih0Q= github.com/cloudfoundry/bosh-cli v6.2.1+incompatible h1:pkQSRxwpiVhyDmq+zA4fUW6BK65gKoS+aLNTpuxoxnw= @@ -45,6 +50,8 @@ github.com/cloudfoundry/noaa v2.1.0+incompatible h1:hr6VnM5VlYRN3YD+NmAedQLW8686 github.com/cloudfoundry/noaa v2.1.0+incompatible/go.mod h1:5LmacnptvxzrTvMfL9+EJhgkUfIgcwI61BVSTh47ECo= github.com/cloudfoundry/sonde-go v0.0.0-20171206171820-b33733203bb4 h1:cWfya7mo/zbnwYVio6eWGsFJHqYw4/k/uhwIJ1eqRPI= github.com/cloudfoundry/sonde-go v0.0.0-20171206171820-b33733203bb4/go.mod h1:GS0pCHd7onIsewbw8Ue9qa9pZPv2V88cUZDttK6KzgI= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= github.com/cppforlife/go-patch v0.2.0 h1:Y14MnCQjDlbw7WXT4k+u6DPAA9XnygN4BfrSpI/19RU= github.com/cppforlife/go-patch v0.2.0/go.mod h1:67a7aIi94FHDZdoeGSJRRFDp66l9MhaAG1yGxpUoFD8= @@ -62,11 +69,22 @@ github.com/elazarl/goproxy/ext v0.0.0-20200426045556-49ad98f6dac1 h1:nCR2gi9ueTf github.com/elazarl/goproxy/ext v0.0.0-20200426045556-49ad98f6dac1/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ess/debuggable v1.0.0 h1:tMxRJ5kercQkPGa8uvtBou9gisaFgK+iURNSTFEjzdA= +github.com/ess/debuggable v1.0.0/go.mod h1:xffN9MmBxD0x0wMIXAT7R+jUNZt33fV2v1d9i8v6aS8= +github.com/ess/hype v1.1.5 h1:1FPXP//FD2acM9ViuVmLRcLHEeGAVmuk6UiYfM+GuZ8= +github.com/ess/hype v1.1.5/go.mod h1:InBwoV1C0c/amJlBisBAwf6bdTwuQY2DkJJNXynsjTE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gammazero/deque v0.1.0 h1:f9LnNmq66VDeuAlSAapemq/U7hJ2jpIWa4c09q8Dlik= +github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= +github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= +github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -93,16 +111,22 @@ github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20170926144705-f88afde2fa19/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= @@ -119,6 +143,8 @@ github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+L github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57 h1:qhv1ir3dIyOFmFU+5KqG4dF3zSQTA4nn1DFhu2NQC44= github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 h1:YFh+sjyJTMQSYjKwM4dFKhJPJC/wfo98tPUc17HdoYw= +github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -132,22 +158,32 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.3/go.mod h1:1ftk08SazyElaaNvmqAfZWGwJzshjCfBXDLoQtPAMNk= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v0.0.0-20171031171758-652e15c9a27e/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= +github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20171105031654-1eecca0ba8e6/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= +github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= +github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pivotal-cf/brokerapi v6.4.2+incompatible h1:TqOte2wNUUB7t/+Pt9vjviCsT9wlQtO2OUPyuZ67DeE= -github.com/pivotal-cf/brokerapi v6.4.2+incompatible/go.mod h1:P+oA8NvkCTkq2t4DohBiyqQo69Ub15RKGcm/vKNP0gg= +github.com/pivotal-cf/brokerapi/v7 v7.4.0 h1:ByvIIilMfUiaqGOZ3EQKvwOMxPUhbpE43qAxoUaOhYY= +github.com/pivotal-cf/brokerapi/v7 v7.4.0/go.mod h1:5ew1ejTeTqeJchGP2U2xxVEI7Z25Ca4g85NByKgqIx4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -157,10 +193,17 @@ github.com/poy/eachers v0.0.0-20181020210610-23942921fe77 h1:SNdqPRvRsVmYR0gKqFv github.com/poy/eachers v0.0.0-20181020210610-23942921fe77/go.mod h1:x1vqpbcMW9T/KRcQ4b48diSiSVtYgvwQ5xzDByEg4WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= -github.com/sclevine/spec v1.3.0 h1:iTB51CYlnju5oRh0/l67fg1+RlQ2nqmFecwdvN+5TrI= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sclevine/spec v1.3.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= +github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= +github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/square/certstrap v1.2.0 h1:ecgyABrbFLr8jSbOC6oTBmBek0t/HqtgrMUZCPuyfdw= github.com/square/certstrap v1.2.0/go.mod h1:CUHqV+fxJW0Y5UQFnnbYwQ7bpKXO1AKbic9g73799yw= github.com/starkandwayne/spring-cloud-services-cli-config-parser v1.0.2 h1:3r4jVyuR9ycvepUtwb79o7iq/KRbHn4wEln/00h5Mpg= @@ -177,8 +220,10 @@ github.com/vito/go-interact v1.0.0 h1:niLW3NjGoMWOayoR6iQ8AxWVM1Q4rR8VGZ1mt6uK3B github.com/vito/go-interact v1.0.0/go.mod h1:W1mz+UVUZScRM3eUjQhEQiLDnQ+yLnXkB2rjBfGPrXg= golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -186,6 +231,8 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180418062111-d41e8174641f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -197,11 +244,15 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -218,11 +269,15 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -243,9 +298,13 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200301222351-066e0c02454c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -276,7 +335,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -292,3 +351,4 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= diff --git a/httpartifacttransport/filetransport.go b/httpartifacttransport/filetransport.go deleted file mode 100644 index de4c796..0000000 --- a/httpartifacttransport/filetransport.go +++ /dev/null @@ -1,76 +0,0 @@ -package httpartifacttransport - -import ( - "fmt" - "io" - "net/http" - "os" - - "code.cloudfoundry.org/lager" - "github.com/starkandwayne/scs-broker/broker" - "github.com/starkandwayne/scs-broker/config" -) - -type HttpArtifactTransport struct { - Config config.Config - Logger lager.Logger - Client *http.Client -} - -func NewHttpArtifactTransport(config config.Config, logger lager.Logger) HttpArtifactTransport { - return HttpArtifactTransport{ - Config: config, - Logger: logger, - } -} - -func (transport *HttpArtifactTransport) EnableHttpFileTransport() { - transport.Logger.Info("detected file transfer protocol") - t := &http.Transport{} - os.Mkdir("./"+broker.ArtifactsDir, 0777) - t.RegisterProtocol("file", http.NewFileTransport(http.Dir("./"+broker.ArtifactsDir))) - transport.Client = &http.Client{Transport: t} -} - -func (transport *HttpArtifactTransport) DownloadArtifact(filename string, url string) error { - - if transport.Client == nil { - transport.Logger.Info("standard http protocol detected") - transport.Client = &http.Client{} - } - - transport.Logger.Info(fmt.Sprintf("Downloading from URI: %s ", url)) - resp, err := transport.Client.Get(url) - - transport.Logger.Info(fmt.Sprintf("HTTP STATUS CODE: %s ", resp.Status)) - - if err != nil { - return err - } - defer resp.Body.Close() - - os.Mkdir("./"+broker.ArtifactsDir, 0777) - // Create the file - out, err := os.Create("./" + broker.ArtifactsDir + "/" + filename) - if err != nil { - return err - } - defer out.Close() - - // Write the body to file - num, err := io.Copy(out, resp.Body) - if err != nil { - return err - } - - transport.Logger.Info(fmt.Sprintf("Wrote: %d bytes", num)) - - fi, err := os.Stat("./" + broker.ArtifactsDir + "/" + filename) - if err != nil { - return err - } - - transport.Logger.Info(fmt.Sprintf("Filename: %s Size: %d", fi.Name(), fi.Size())) - - return err -} diff --git a/logger/job_info.go b/logger/job_info.go new file mode 100644 index 0000000..8d90699 --- /dev/null +++ b/logger/job_info.go @@ -0,0 +1,7 @@ +package logger + +import "code.cloudfoundry.org/lager" + +func JobInfo(msg string, jobID string, nodeName string) { + Info(msg, lager.Data{"job-id": jobID, "node-name": nodeName}) +} diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..2c819d7 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,31 @@ +package logger + +import "code.cloudfoundry.org/lager" + +var singleton Logger = &null{} + +type Logger interface { + Info(string, ...lager.Data) + Error(string, error, ...lager.Data) + Fatal(string, error, ...lager.Data) +} + +func Setup(l Logger) { + singleton = l +} + +func Info(msg string, data ...lager.Data) { + singleton.Info(msg, data...) +} + +func Error(msg string, err error, data ...lager.Data) { + singleton.Error(msg, err, data...) +} + +func Fatal(msg string, err error, data ...lager.Data) { + singleton.Fatal(msg, err, data...) +} + +func Singleton() Logger { + return singleton +} diff --git a/logger/null.go b/logger/null.go new file mode 100644 index 0000000..32613a2 --- /dev/null +++ b/logger/null.go @@ -0,0 +1,16 @@ +package logger + +import "code.cloudfoundry.org/lager" + +type null struct{} + +func (logger *null) Info(msg string, data ...lager.Data) {} + +func (logger *null) Error(msg string, err error, data ...lager.Data) {} + +func (logger *null) Fatal(msg string, err error, data ...lager.Data) { + // the conventional expectation across logging suites is that a fatal + // log does a panic. since control flow may rely on this, we're doing + // it here. + panic("(null logger) " + msg + " - " + err.Error()) +} diff --git a/logger/show_warnings.go b/logger/show_warnings.go new file mode 100755 index 0000000..b0fcf06 --- /dev/null +++ b/logger/show_warnings.go @@ -0,0 +1,23 @@ +package logger + +import ( + "fmt" + + "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" + "code.cloudfoundry.org/lager" +) + +func ShowWarnings(warnings ccv3.Warnings, subject interface{}) { + Info( + fmt.Sprintf( + "NOTICE: %d warning(s) were detected!", + len(warnings), + ), + lager.Data{"Subject": subject}, + ) + + for warn := range warnings { + w := warnings[warn] + Info(fmt.Sprintf("Warning(#%d): %s ", warn+1, w)) + } +} diff --git a/logger/workflow_error.go b/logger/workflow_error.go new file mode 100644 index 0000000..2012a0a --- /dev/null +++ b/logger/workflow_error.go @@ -0,0 +1,7 @@ +package logger + +import "code.cloudfoundry.org/lager" + +func WorkflowError(msg string, workflow string, err error) { + Error(msg, err, lager.Data{"workflow": workflow}) +} diff --git a/main.go b/main.go index 17e5e3a..8350c07 100644 --- a/main.go +++ b/main.go @@ -5,38 +5,41 @@ import ( "os" "code.cloudfoundry.org/lager" - "github.com/pivotal-cf/brokerapi" + "github.com/pivotal-cf/brokerapi/v7" "github.com/starkandwayne/scs-broker/broker" + "github.com/starkandwayne/scs-broker/broker/configserver" + "github.com/starkandwayne/scs-broker/broker/implementation" + "github.com/starkandwayne/scs-broker/broker/serviceregistry" "github.com/starkandwayne/scs-broker/config" - "github.com/starkandwayne/scs-broker/httpartifacttransport" + "github.com/starkandwayne/scs-broker/logger" ) var brokerLogger lager.Logger -var httpTransport httpartifacttransport.HttpArtifactTransport func main() { - brokerLogger = lager.NewLogger("scs-broker") brokerLogger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.DEBUG)) brokerLogger.RegisterSink(lager.NewWriterSink(os.Stderr, lager.ERROR)) - brokerConf, err := config.ParseConfig() + logger.Setup(brokerLogger) + + err := config.ParseConfig() if err != nil { - brokerLogger.Fatal("Reading config from env", err, lager.Data{ + logger.Fatal("Reading config from env", err, lager.Data{ "broker-config-environment-variable": config.ConfigEnvVarName, }) } - brokerLogger.Info("starting") + logger.Info("starting") - serviceBroker := &broker.SCSBroker{ - Config: brokerConf, - Logger: brokerLogger, - } + implementation.Register("configserver", configserver.New()) + implementation.Register("serviceregistry", serviceregistry.New()) + + serviceBroker := broker.New() brokerCredentials := brokerapi.BrokerCredentials{ - Username: brokerConf.Auth.Username, - Password: brokerConf.Auth.Password, + Username: config.Parsed.Auth.Username, + Password: config.Parsed.Auth.Password, } brokerAPI := brokerapi.New(serviceBroker, brokerLogger, brokerCredentials) @@ -46,6 +49,6 @@ func main() { if port == "" { port = "8080" } - brokerLogger.Info("listening", lager.Data{"port": port}) - brokerLogger.Fatal("http-listen", http.ListenAndServe("0.0.0.0:"+port, nil)) + logger.Info("listening", lager.Data{"port": port}) + logger.Fatal("http-listen", http.ListenAndServe("0.0.0.0:"+port, nil)) } diff --git a/vendor/github.com/Masterminds/semver/.travis.yml b/vendor/github.com/Masterminds/semver/.travis.yml new file mode 100644 index 0000000..3d9ebad --- /dev/null +++ b/vendor/github.com/Masterminds/semver/.travis.yml @@ -0,0 +1,27 @@ +language: go + +go: + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - tip + +# Setting sudo access to false will let Travis CI use containers rather than +# VMs to run the tests. For more details see: +# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/ +# - http://docs.travis-ci.com/user/workers/standard-infrastructure/ +sudo: false + +script: + - make setup + - make test + +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/06e3328629952dabe3e0 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always diff --git a/vendor/github.com/Masterminds/semver/CHANGELOG.md b/vendor/github.com/Masterminds/semver/CHANGELOG.md new file mode 100644 index 0000000..b888e20 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/CHANGELOG.md @@ -0,0 +1,86 @@ +# 1.4.2 (2018-04-10) + +## Changed +- #72: Updated the docs to point to vert for a console appliaction +- #71: Update the docs on pre-release comparator handling + +## Fixed +- #70: Fix the handling of pre-releases and the 0.0.0 release edge case + +# 1.4.1 (2018-04-02) + +## Fixed +- Fixed #64: Fix pre-release precedence issue (thanks @uudashr) + +# 1.4.0 (2017-10-04) + +## Changed +- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill) + +# 1.3.1 (2017-07-10) + +## Fixed +- Fixed #57: number comparisons in prerelease sometimes inaccurate + +# 1.3.0 (2017-05-02) + +## Added +- #45: Added json (un)marshaling support (thanks @mh-cbon) +- Stability marker. See https://masterminds.github.io/stability/ + +## Fixed +- #51: Fix handling of single digit tilde constraint (thanks @dgodd) + +## Changed +- #55: The godoc icon moved from png to svg + +# 1.2.3 (2017-04-03) + +## Fixed +- #46: Fixed 0.x.x and 0.0.x in constraints being treated as * + +# Release 1.2.2 (2016-12-13) + +## Fixed +- #34: Fixed issue where hyphen range was not working with pre-release parsing. + +# Release 1.2.1 (2016-11-28) + +## Fixed +- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha" + properly. + +# Release 1.2.0 (2016-11-04) + +## Added +- #20: Added MustParse function for versions (thanks @adamreese) +- #15: Added increment methods on versions (thanks @mh-cbon) + +## Fixed +- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and + might not satisfy the intended compatibility. The change here ignores pre-releases + on constraint checks (e.g., ~ or ^) when a pre-release is not part of the + constraint. For example, `^1.2.3` will ignore pre-releases while + `^1.2.3-alpha` will include them. + +# Release 1.1.1 (2016-06-30) + +## Changed +- Issue #9: Speed up version comparison performance (thanks @sdboyer) +- Issue #8: Added benchmarks (thanks @sdboyer) +- Updated Go Report Card URL to new location +- Updated Readme to add code snippet formatting (thanks @mh-cbon) +- Updating tagging to v[SemVer] structure for compatibility with other tools. + +# Release 1.1.0 (2016-03-11) + +- Issue #2: Implemented validation to provide reasons a versions failed a + constraint. + +# Release 1.0.1 (2015-12-31) + +- Fixed #1: * constraint failing on valid versions. + +# Release 1.0.0 (2015-10-20) + +- Initial release diff --git a/vendor/github.com/Masterminds/semver/LICENSE.txt b/vendor/github.com/Masterminds/semver/LICENSE.txt new file mode 100644 index 0000000..0da4aea --- /dev/null +++ b/vendor/github.com/Masterminds/semver/LICENSE.txt @@ -0,0 +1,20 @@ +The Masterminds +Copyright (C) 2014-2015, Matt Butcher and Matt Farina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/Masterminds/semver/Makefile b/vendor/github.com/Masterminds/semver/Makefile new file mode 100644 index 0000000..a7a1b4e --- /dev/null +++ b/vendor/github.com/Masterminds/semver/Makefile @@ -0,0 +1,36 @@ +.PHONY: setup +setup: + go get -u gopkg.in/alecthomas/gometalinter.v1 + gometalinter.v1 --install + +.PHONY: test +test: validate lint + @echo "==> Running tests" + go test -v + +.PHONY: validate +validate: + @echo "==> Running static validations" + @gometalinter.v1 \ + --disable-all \ + --enable deadcode \ + --severity deadcode:error \ + --enable gofmt \ + --enable gosimple \ + --enable ineffassign \ + --enable misspell \ + --enable vet \ + --tests \ + --vendor \ + --deadline 60s \ + ./... || exit_code=1 + +.PHONY: lint +lint: + @echo "==> Running linters" + @gometalinter.v1 \ + --disable-all \ + --enable golint \ + --vendor \ + --deadline 60s \ + ./... || : diff --git a/vendor/github.com/Masterminds/semver/README.md b/vendor/github.com/Masterminds/semver/README.md new file mode 100644 index 0000000..3e934ed --- /dev/null +++ b/vendor/github.com/Masterminds/semver/README.md @@ -0,0 +1,165 @@ +# SemVer + +The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to: + +* Parse semantic versions +* Sort semantic versions +* Check if a semantic version fits within a set of constraints +* Optionally work with a `v` prefix + +[![Stability: +Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html) +[![Build Status](https://travis-ci.org/Masterminds/semver.svg)](https://travis-ci.org/Masterminds/semver) [![Build status](https://ci.appveyor.com/api/projects/status/jfk66lib7hb985k8/branch/master?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [![GoDoc](https://godoc.org/github.com/Masterminds/semver?status.svg)](https://godoc.org/github.com/Masterminds/semver) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver) + +## Parsing Semantic Versions + +To parse a semantic version use the `NewVersion` function. For example, + +```go + v, err := semver.NewVersion("1.2.3-beta.1+build345") +``` + +If there is an error the version wasn't parseable. The version object has methods +to get the parts of the version, compare it to other versions, convert the +version back into a string, and get the original string. For more details +please see the [documentation](https://godoc.org/github.com/Masterminds/semver). + +## Sorting Semantic Versions + +A set of versions can be sorted using the [`sort`](https://golang.org/pkg/sort/) +package from the standard library. For example, + +```go + raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} + vs := make([]*semver.Version, len(raw)) + for i, r := range raw { + v, err := semver.NewVersion(r) + if err != nil { + t.Errorf("Error parsing version: %s", err) + } + + vs[i] = v + } + + sort.Sort(semver.Collection(vs)) +``` + +## Checking Version Constraints + +Checking a version against version constraints is one of the most featureful +parts of the package. + +```go + c, err := semver.NewConstraint(">= 1.2.3") + if err != nil { + // Handle constraint not being parseable. + } + + v, _ := semver.NewVersion("1.3") + if err != nil { + // Handle version not being parseable. + } + // Check if the version meets the constraints. The a variable will be true. + a := c.Check(v) +``` + +## Basic Comparisons + +There are two elements to the comparisons. First, a comparison string is a list +of comma separated and comparisons. These are then separated by || separated or +comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a +comparison that's greater than or equal to 1.2 and less than 3.0.0 or is +greater than or equal to 4.2.3. + +The basic comparisons are: + +* `=`: equal (aliased to no operator) +* `!=`: not equal +* `>`: greater than +* `<`: less than +* `>=`: greater than or equal to +* `<=`: less than or equal to + +_Note, according to the Semantic Version specification pre-releases may not be +API compliant with their release counterpart. It says,_ + +> _A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version._ + +_SemVer comparisons without a pre-release value will skip pre-release versions. +For example, `>1.2.3` will skip pre-releases when looking at a list of values +while `>1.2.3-alpha.1` will evaluate pre-releases._ + +## Hyphen Range Comparisons + +There are multiple methods to handle ranges and the first is hyphens ranges. +These look like: + +* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` +* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5` + +## Wildcards In Comparisons + +The `x`, `X`, and `*` characters can be used as a wildcard character. This works +for all comparison operators. When used on the `=` operator it falls +back to the pack level comparison (see tilde below). For example, + +* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` +* `>= 1.2.x` is equivalent to `>= 1.2.0` +* `<= 2.x` is equivalent to `<= 3` +* `*` is equivalent to `>= 0.0.0` + +## Tilde Range Comparisons (Patch) + +The tilde (`~`) comparison operator is for patch level ranges when a minor +version is specified and major level changes when the minor number is missing. +For example, + +* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` +* `~1` is equivalent to `>= 1, < 2` +* `~2.3` is equivalent to `>= 2.3, < 2.4` +* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` +* `~1.x` is equivalent to `>= 1, < 2` + +## Caret Range Comparisons (Major) + +The caret (`^`) comparison operator is for major level changes. This is useful +when comparisons of API versions as a major change is API breaking. For example, + +* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` +* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` +* `^2.3` is equivalent to `>= 2.3, < 3` +* `^2.x` is equivalent to `>= 2.0.0, < 3` + +# Validation + +In addition to testing a version against a constraint, a version can be validated +against a constraint. When validation fails a slice of errors containing why a +version didn't meet the constraint is returned. For example, + +```go + c, err := semver.NewConstraint("<= 1.2.3, >= 1.4") + if err != nil { + // Handle constraint not being parseable. + } + + v, _ := semver.NewVersion("1.3") + if err != nil { + // Handle version not being parseable. + } + + // Validate a version against a constraint. + a, msgs := c.Validate(v) + // a is false + for _, m := range msgs { + fmt.Println(m) + + // Loops over the errors which would read + // "1.3 is greater than 1.2.3" + // "1.3 is less than 1.4" + } +``` + +# Contribute + +If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues) +or [create a pull request](https://github.com/Masterminds/semver/pulls). diff --git a/vendor/github.com/Masterminds/semver/appveyor.yml b/vendor/github.com/Masterminds/semver/appveyor.yml new file mode 100644 index 0000000..b2778df --- /dev/null +++ b/vendor/github.com/Masterminds/semver/appveyor.yml @@ -0,0 +1,44 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\Masterminds\semver +shallow_clone: true + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +install: + - go version + - go env + - go get -u gopkg.in/alecthomas/gometalinter.v1 + - set PATH=%PATH%;%GOPATH%\bin + - gometalinter.v1.exe --install + +build_script: + - go install -v ./... + +test_script: + - "gometalinter.v1 \ + --disable-all \ + --enable deadcode \ + --severity deadcode:error \ + --enable gofmt \ + --enable gosimple \ + --enable ineffassign \ + --enable misspell \ + --enable vet \ + --tests \ + --vendor \ + --deadline 60s \ + ./... || exit_code=1" + - "gometalinter.v1 \ + --disable-all \ + --enable golint \ + --vendor \ + --deadline 60s \ + ./... || :" + - go test -v + +deploy: off diff --git a/vendor/github.com/Masterminds/semver/collection.go b/vendor/github.com/Masterminds/semver/collection.go new file mode 100644 index 0000000..a782358 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/collection.go @@ -0,0 +1,24 @@ +package semver + +// Collection is a collection of Version instances and implements the sort +// interface. See the sort package for more details. +// https://golang.org/pkg/sort/ +type Collection []*Version + +// Len returns the length of a collection. The number of Version instances +// on the slice. +func (c Collection) Len() int { + return len(c) +} + +// Less is needed for the sort interface to compare two Version objects on the +// slice. If checks if one is less than the other. +func (c Collection) Less(i, j int) bool { + return c[i].LessThan(c[j]) +} + +// Swap is needed for the sort interface to replace the Version objects +// at two different positions in the slice. +func (c Collection) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} diff --git a/vendor/github.com/Masterminds/semver/constraints.go b/vendor/github.com/Masterminds/semver/constraints.go new file mode 100644 index 0000000..a41a6a7 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/constraints.go @@ -0,0 +1,426 @@ +package semver + +import ( + "errors" + "fmt" + "regexp" + "strings" +) + +// Constraints is one or more constraint that a semantic version can be +// checked against. +type Constraints struct { + constraints [][]*constraint +} + +// NewConstraint returns a Constraints instance that a Version instance can +// be checked against. If there is a parse error it will be returned. +func NewConstraint(c string) (*Constraints, error) { + + // Rewrite - ranges into a comparison operation. + c = rewriteRange(c) + + ors := strings.Split(c, "||") + or := make([][]*constraint, len(ors)) + for k, v := range ors { + cs := strings.Split(v, ",") + result := make([]*constraint, len(cs)) + for i, s := range cs { + pc, err := parseConstraint(s) + if err != nil { + return nil, err + } + + result[i] = pc + } + or[k] = result + } + + o := &Constraints{constraints: or} + return o, nil +} + +// Check tests if a version satisfies the constraints. +func (cs Constraints) Check(v *Version) bool { + // loop over the ORs and check the inner ANDs + for _, o := range cs.constraints { + joy := true + for _, c := range o { + if !c.check(v) { + joy = false + break + } + } + + if joy { + return true + } + } + + return false +} + +// Validate checks if a version satisfies a constraint. If not a slice of +// reasons for the failure are returned in addition to a bool. +func (cs Constraints) Validate(v *Version) (bool, []error) { + // loop over the ORs and check the inner ANDs + var e []error + for _, o := range cs.constraints { + joy := true + for _, c := range o { + if !c.check(v) { + em := fmt.Errorf(c.msg, v, c.orig) + e = append(e, em) + joy = false + } + } + + if joy { + return true, []error{} + } + } + + return false, e +} + +var constraintOps map[string]cfunc +var constraintMsg map[string]string +var constraintRegex *regexp.Regexp + +func init() { + constraintOps = map[string]cfunc{ + "": constraintTildeOrEqual, + "=": constraintTildeOrEqual, + "!=": constraintNotEqual, + ">": constraintGreaterThan, + "<": constraintLessThan, + ">=": constraintGreaterThanEqual, + "=>": constraintGreaterThanEqual, + "<=": constraintLessThanEqual, + "=<": constraintLessThanEqual, + "~": constraintTilde, + "~>": constraintTilde, + "^": constraintCaret, + } + + constraintMsg = map[string]string{ + "": "%s is not equal to %s", + "=": "%s is not equal to %s", + "!=": "%s is equal to %s", + ">": "%s is less than or equal to %s", + "<": "%s is greater than or equal to %s", + ">=": "%s is less than %s", + "=>": "%s is less than %s", + "<=": "%s is greater than %s", + "=<": "%s is greater than %s", + "~": "%s does not have same major and minor version as %s", + "~>": "%s does not have same major and minor version as %s", + "^": "%s does not have same major version as %s", + } + + ops := make([]string, 0, len(constraintOps)) + for k := range constraintOps { + ops = append(ops, regexp.QuoteMeta(k)) + } + + constraintRegex = regexp.MustCompile(fmt.Sprintf( + `^\s*(%s)\s*(%s)\s*$`, + strings.Join(ops, "|"), + cvRegex)) + + constraintRangeRegex = regexp.MustCompile(fmt.Sprintf( + `\s*(%s)\s+-\s+(%s)\s*`, + cvRegex, cvRegex)) +} + +// An individual constraint +type constraint struct { + // The callback function for the restraint. It performs the logic for + // the constraint. + function cfunc + + msg string + + // The version used in the constraint check. For example, if a constraint + // is '<= 2.0.0' the con a version instance representing 2.0.0. + con *Version + + // The original parsed version (e.g., 4.x from != 4.x) + orig string + + // When an x is used as part of the version (e.g., 1.x) + minorDirty bool + dirty bool + patchDirty bool +} + +// Check if a version meets the constraint +func (c *constraint) check(v *Version) bool { + return c.function(v, c) +} + +type cfunc func(v *Version, c *constraint) bool + +func parseConstraint(c string) (*constraint, error) { + m := constraintRegex.FindStringSubmatch(c) + if m == nil { + return nil, fmt.Errorf("improper constraint: %s", c) + } + + ver := m[2] + orig := ver + minorDirty := false + patchDirty := false + dirty := false + if isX(m[3]) { + ver = "0.0.0" + dirty = true + } else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" { + minorDirty = true + dirty = true + ver = fmt.Sprintf("%s.0.0%s", m[3], m[6]) + } else if isX(strings.TrimPrefix(m[5], ".")) { + dirty = true + patchDirty = true + ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6]) + } + + con, err := NewVersion(ver) + if err != nil { + + // The constraintRegex should catch any regex parsing errors. So, + // we should never get here. + return nil, errors.New("constraint Parser Error") + } + + cs := &constraint{ + function: constraintOps[m[1]], + msg: constraintMsg[m[1]], + con: con, + orig: orig, + minorDirty: minorDirty, + patchDirty: patchDirty, + dirty: dirty, + } + return cs, nil +} + +// Constraint functions +func constraintNotEqual(v *Version, c *constraint) bool { + if c.dirty { + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if c.con.Major() != v.Major() { + return true + } + if c.con.Minor() != v.Minor() && !c.minorDirty { + return true + } else if c.minorDirty { + return false + } + + return false + } + + return !v.Equal(c.con) +} + +func constraintGreaterThan(v *Version, c *constraint) bool { + + // An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease + // exists. This that case. + if !isNonZero(c.con) && isNonZero(v) { + return true + } + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + return v.Compare(c.con) == 1 +} + +func constraintLessThan(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if !c.dirty { + return v.Compare(c.con) < 0 + } + + if v.Major() > c.con.Major() { + return false + } else if v.Minor() > c.con.Minor() && !c.minorDirty { + return false + } + + return true +} + +func constraintGreaterThanEqual(v *Version, c *constraint) bool { + // An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease + // exists. This that case. + if !isNonZero(c.con) && isNonZero(v) { + return true + } + + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + return v.Compare(c.con) >= 0 +} + +func constraintLessThanEqual(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if !c.dirty { + return v.Compare(c.con) <= 0 + } + + if v.Major() > c.con.Major() { + return false + } else if v.Minor() > c.con.Minor() && !c.minorDirty { + return false + } + + return true +} + +// ~*, ~>* --> >= 0.0.0 (any) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0 +func constraintTilde(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if v.LessThan(c.con) { + return false + } + + // ~0.0.0 is a special case where all constraints are accepted. It's + // equivalent to >= 0.0.0. + if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 && + !c.minorDirty && !c.patchDirty { + return true + } + + if v.Major() != c.con.Major() { + return false + } + + if v.Minor() != c.con.Minor() && !c.minorDirty { + return false + } + + return true +} + +// When there is a .x (dirty) status it automatically opts in to ~. Otherwise +// it's a straight = +func constraintTildeOrEqual(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if c.dirty { + c.msg = constraintMsg["~"] + return constraintTilde(v, c) + } + + return v.Equal(c.con) +} + +// ^* --> (any) +// ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0, <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0, <2.0.0 +// ^1.2.3 --> >=1.2.3, <2.0.0 +// ^1.2.0 --> >=1.2.0, <2.0.0 +func constraintCaret(v *Version, c *constraint) bool { + // If there is a pre-release on the version but the constraint isn't looking + // for them assume that pre-releases are not compatible. See issue 21 for + // more details. + if v.Prerelease() != "" && c.con.Prerelease() == "" { + return false + } + + if v.LessThan(c.con) { + return false + } + + if v.Major() != c.con.Major() { + return false + } + + return true +} + +var constraintRangeRegex *regexp.Regexp + +const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` + + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + +func isX(x string) bool { + switch x { + case "x", "*", "X": + return true + default: + return false + } +} + +func rewriteRange(i string) string { + m := constraintRangeRegex.FindAllStringSubmatch(i, -1) + if m == nil { + return i + } + o := i + for _, v := range m { + t := fmt.Sprintf(">= %s, <= %s", v[1], v[11]) + o = strings.Replace(o, v[0], t, 1) + } + + return o +} + +// Detect if a version is not zero (0.0.0) +func isNonZero(v *Version) bool { + if v.Major() != 0 || v.Minor() != 0 || v.Patch() != 0 || v.Prerelease() != "" { + return true + } + + return false +} diff --git a/vendor/github.com/Masterminds/semver/doc.go b/vendor/github.com/Masterminds/semver/doc.go new file mode 100644 index 0000000..e00f65e --- /dev/null +++ b/vendor/github.com/Masterminds/semver/doc.go @@ -0,0 +1,115 @@ +/* +Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go. + +Specifically it provides the ability to: + + * Parse semantic versions + * Sort semantic versions + * Check if a semantic version fits within a set of constraints + * Optionally work with a `v` prefix + +Parsing Semantic Versions + +To parse a semantic version use the `NewVersion` function. For example, + + v, err := semver.NewVersion("1.2.3-beta.1+build345") + +If there is an error the version wasn't parseable. The version object has methods +to get the parts of the version, compare it to other versions, convert the +version back into a string, and get the original string. For more details +please see the documentation at https://godoc.org/github.com/Masterminds/semver. + +Sorting Semantic Versions + +A set of versions can be sorted using the `sort` package from the standard library. +For example, + + raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",} + vs := make([]*semver.Version, len(raw)) + for i, r := range raw { + v, err := semver.NewVersion(r) + if err != nil { + t.Errorf("Error parsing version: %s", err) + } + + vs[i] = v + } + + sort.Sort(semver.Collection(vs)) + +Checking Version Constraints + +Checking a version against version constraints is one of the most featureful +parts of the package. + + c, err := semver.NewConstraint(">= 1.2.3") + if err != nil { + // Handle constraint not being parseable. + } + + v, _ := semver.NewVersion("1.3") + if err != nil { + // Handle version not being parseable. + } + // Check if the version meets the constraints. The a variable will be true. + a := c.Check(v) + +Basic Comparisons + +There are two elements to the comparisons. First, a comparison string is a list +of comma separated and comparisons. These are then separated by || separated or +comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a +comparison that's greater than or equal to 1.2 and less than 3.0.0 or is +greater than or equal to 4.2.3. + +The basic comparisons are: + + * `=`: equal (aliased to no operator) + * `!=`: not equal + * `>`: greater than + * `<`: less than + * `>=`: greater than or equal to + * `<=`: less than or equal to + +Hyphen Range Comparisons + +There are multiple methods to handle ranges and the first is hyphens ranges. +These look like: + + * `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5` + * `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5` + +Wildcards In Comparisons + +The `x`, `X`, and `*` characters can be used as a wildcard character. This works +for all comparison operators. When used on the `=` operator it falls +back to the pack level comparison (see tilde below). For example, + + * `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` + * `>= 1.2.x` is equivalent to `>= 1.2.0` + * `<= 2.x` is equivalent to `<= 3` + * `*` is equivalent to `>= 0.0.0` + +Tilde Range Comparisons (Patch) + +The tilde (`~`) comparison operator is for patch level ranges when a minor +version is specified and major level changes when the minor number is missing. +For example, + + * `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0` + * `~1` is equivalent to `>= 1, < 2` + * `~2.3` is equivalent to `>= 2.3, < 2.4` + * `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0` + * `~1.x` is equivalent to `>= 1, < 2` + +Caret Range Comparisons (Major) + +The caret (`^`) comparison operator is for major level changes. This is useful +when comparisons of API versions as a major change is API breaking. For example, + + * `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0` + * `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0` + * `^2.3` is equivalent to `>= 2.3, < 3` + * `^2.x` is equivalent to `>= 2.0.0, < 3` +*/ +package semver diff --git a/vendor/github.com/Masterminds/semver/version.go b/vendor/github.com/Masterminds/semver/version.go new file mode 100644 index 0000000..9d22ea6 --- /dev/null +++ b/vendor/github.com/Masterminds/semver/version.go @@ -0,0 +1,421 @@ +package semver + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "regexp" + "strconv" + "strings" +) + +// The compiled version of the regex created at init() is cached here so it +// only needs to be created once. +var versionRegex *regexp.Regexp +var validPrereleaseRegex *regexp.Regexp + +var ( + // ErrInvalidSemVer is returned a version is found to be invalid when + // being parsed. + ErrInvalidSemVer = errors.New("Invalid Semantic Version") + + // ErrInvalidMetadata is returned when the metadata is an invalid format + ErrInvalidMetadata = errors.New("Invalid Metadata string") + + // ErrInvalidPrerelease is returned when the pre-release is an invalid format + ErrInvalidPrerelease = errors.New("Invalid Prerelease string") +) + +// SemVerRegex is the regular expression used to parse a semantic version. +const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` + + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + +// ValidPrerelease is the regular expression which validates +// both prerelease and metadata values. +const ValidPrerelease string = `^([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)` + +// Version represents a single semantic version. +type Version struct { + major, minor, patch int64 + pre string + metadata string + original string +} + +func init() { + versionRegex = regexp.MustCompile("^" + SemVerRegex + "$") + validPrereleaseRegex = regexp.MustCompile(ValidPrerelease) +} + +// NewVersion parses a given version and returns an instance of Version or +// an error if unable to parse the version. +func NewVersion(v string) (*Version, error) { + m := versionRegex.FindStringSubmatch(v) + if m == nil { + return nil, ErrInvalidSemVer + } + + sv := &Version{ + metadata: m[8], + pre: m[5], + original: v, + } + + var temp int64 + temp, err := strconv.ParseInt(m[1], 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + sv.major = temp + + if m[2] != "" { + temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + sv.minor = temp + } else { + sv.minor = 0 + } + + if m[3] != "" { + temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 64) + if err != nil { + return nil, fmt.Errorf("Error parsing version segment: %s", err) + } + sv.patch = temp + } else { + sv.patch = 0 + } + + return sv, nil +} + +// MustParse parses a given version and panics on error. +func MustParse(v string) *Version { + sv, err := NewVersion(v) + if err != nil { + panic(err) + } + return sv +} + +// String converts a Version object to a string. +// Note, if the original version contained a leading v this version will not. +// See the Original() method to retrieve the original value. Semantic Versions +// don't contain a leading v per the spec. Instead it's optional on +// impelementation. +func (v *Version) String() string { + var buf bytes.Buffer + + fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch) + if v.pre != "" { + fmt.Fprintf(&buf, "-%s", v.pre) + } + if v.metadata != "" { + fmt.Fprintf(&buf, "+%s", v.metadata) + } + + return buf.String() +} + +// Original returns the original value passed in to be parsed. +func (v *Version) Original() string { + return v.original +} + +// Major returns the major version. +func (v *Version) Major() int64 { + return v.major +} + +// Minor returns the minor version. +func (v *Version) Minor() int64 { + return v.minor +} + +// Patch returns the patch version. +func (v *Version) Patch() int64 { + return v.patch +} + +// Prerelease returns the pre-release version. +func (v *Version) Prerelease() string { + return v.pre +} + +// Metadata returns the metadata on the version. +func (v *Version) Metadata() string { + return v.metadata +} + +// originalVPrefix returns the original 'v' prefix if any. +func (v *Version) originalVPrefix() string { + + // Note, only lowercase v is supported as a prefix by the parser. + if v.original != "" && v.original[:1] == "v" { + return v.original[:1] + } + return "" +} + +// IncPatch produces the next patch version. +// If the current version does not have prerelease/metadata information, +// it unsets metadata and prerelease values, increments patch number. +// If the current version has any of prerelease or metadata information, +// it unsets both values and keeps curent patch value +func (v Version) IncPatch() Version { + vNext := v + // according to http://semver.org/#spec-item-9 + // Pre-release versions have a lower precedence than the associated normal version. + // according to http://semver.org/#spec-item-10 + // Build metadata SHOULD be ignored when determining version precedence. + if v.pre != "" { + vNext.metadata = "" + vNext.pre = "" + } else { + vNext.metadata = "" + vNext.pre = "" + vNext.patch = v.patch + 1 + } + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// IncMinor produces the next minor version. +// Sets patch to 0. +// Increments minor number. +// Unsets metadata. +// Unsets prerelease status. +func (v Version) IncMinor() Version { + vNext := v + vNext.metadata = "" + vNext.pre = "" + vNext.patch = 0 + vNext.minor = v.minor + 1 + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// IncMajor produces the next major version. +// Sets patch to 0. +// Sets minor to 0. +// Increments major number. +// Unsets metadata. +// Unsets prerelease status. +func (v Version) IncMajor() Version { + vNext := v + vNext.metadata = "" + vNext.pre = "" + vNext.patch = 0 + vNext.minor = 0 + vNext.major = v.major + 1 + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext +} + +// SetPrerelease defines the prerelease value. +// Value must not include the required 'hypen' prefix. +func (v Version) SetPrerelease(prerelease string) (Version, error) { + vNext := v + if len(prerelease) > 0 && !validPrereleaseRegex.MatchString(prerelease) { + return vNext, ErrInvalidPrerelease + } + vNext.pre = prerelease + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext, nil +} + +// SetMetadata defines metadata value. +// Value must not include the required 'plus' prefix. +func (v Version) SetMetadata(metadata string) (Version, error) { + vNext := v + if len(metadata) > 0 && !validPrereleaseRegex.MatchString(metadata) { + return vNext, ErrInvalidMetadata + } + vNext.metadata = metadata + vNext.original = v.originalVPrefix() + "" + vNext.String() + return vNext, nil +} + +// LessThan tests if one version is less than another one. +func (v *Version) LessThan(o *Version) bool { + return v.Compare(o) < 0 +} + +// GreaterThan tests if one version is greater than another one. +func (v *Version) GreaterThan(o *Version) bool { + return v.Compare(o) > 0 +} + +// Equal tests if two versions are equal to each other. +// Note, versions can be equal with different metadata since metadata +// is not considered part of the comparable version. +func (v *Version) Equal(o *Version) bool { + return v.Compare(o) == 0 +} + +// Compare compares this version to another one. It returns -1, 0, or 1 if +// the version smaller, equal, or larger than the other version. +// +// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is +// lower than the version without a prerelease. +func (v *Version) Compare(o *Version) int { + // Compare the major, minor, and patch version for differences. If a + // difference is found return the comparison. + if d := compareSegment(v.Major(), o.Major()); d != 0 { + return d + } + if d := compareSegment(v.Minor(), o.Minor()); d != 0 { + return d + } + if d := compareSegment(v.Patch(), o.Patch()); d != 0 { + return d + } + + // At this point the major, minor, and patch versions are the same. + ps := v.pre + po := o.Prerelease() + + if ps == "" && po == "" { + return 0 + } + if ps == "" { + return 1 + } + if po == "" { + return -1 + } + + return comparePrerelease(ps, po) +} + +// UnmarshalJSON implements JSON.Unmarshaler interface. +func (v *Version) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + temp, err := NewVersion(s) + if err != nil { + return err + } + v.major = temp.major + v.minor = temp.minor + v.patch = temp.patch + v.pre = temp.pre + v.metadata = temp.metadata + v.original = temp.original + temp = nil + return nil +} + +// MarshalJSON implements JSON.Marshaler interface. +func (v *Version) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +func compareSegment(v, o int64) int { + if v < o { + return -1 + } + if v > o { + return 1 + } + + return 0 +} + +func comparePrerelease(v, o string) int { + + // split the prelease versions by their part. The separator, per the spec, + // is a . + sparts := strings.Split(v, ".") + oparts := strings.Split(o, ".") + + // Find the longer length of the parts to know how many loop iterations to + // go through. + slen := len(sparts) + olen := len(oparts) + + l := slen + if olen > slen { + l = olen + } + + // Iterate over each part of the prereleases to compare the differences. + for i := 0; i < l; i++ { + // Since the lentgh of the parts can be different we need to create + // a placeholder. This is to avoid out of bounds issues. + stemp := "" + if i < slen { + stemp = sparts[i] + } + + otemp := "" + if i < olen { + otemp = oparts[i] + } + + d := comparePrePart(stemp, otemp) + if d != 0 { + return d + } + } + + // Reaching here means two versions are of equal value but have different + // metadata (the part following a +). They are not identical in string form + // but the version comparison finds them to be equal. + return 0 +} + +func comparePrePart(s, o string) int { + // Fastpath if they are equal + if s == o { + return 0 + } + + // When s or o are empty we can use the other in an attempt to determine + // the response. + if s == "" { + if o != "" { + return -1 + } + return 1 + } + + if o == "" { + if s != "" { + return 1 + } + return -1 + } + + // When comparing strings "99" is greater than "103". To handle + // cases like this we need to detect numbers and compare them. + + oi, n1 := strconv.ParseInt(o, 10, 64) + si, n2 := strconv.ParseInt(s, 10, 64) + + // The case where both are strings compare the strings + if n1 != nil && n2 != nil { + if s > o { + return 1 + } + return -1 + } else if n1 != nil { + // o is a string and s is a number + return -1 + } else if n2 != nil { + // s is a string and o is a number + return 1 + } + // Both are numbers + if si > oi { + return 1 + } + return -1 + +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/.gitignore b/vendor/github.com/cloudfoundry-community/go-cfclient/.gitignore new file mode 100644 index 0000000..06e1eff --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/.gitignore @@ -0,0 +1,30 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test +_workspace + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +vendor + +# GoLand +.idea \ No newline at end of file diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/LICENSE b/vendor/github.com/cloudfoundry-community/go-cfclient/LICENSE new file mode 100644 index 0000000..cb2ec6c --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2017 Long Nguyen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/Makefile b/vendor/github.com/cloudfoundry-community/go-cfclient/Makefile new file mode 100644 index 0000000..b7412e5 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/Makefile @@ -0,0 +1,42 @@ +GOLANG_CI_LINT_VERSION := $(shell golangci-lint --version 2>/dev/null) + +.PHONY: all +all: test lint + +.PHONY: clean +clean: ## Clean testcache and delete build output + go clean -testcache + +.PHONY: test +test: ## Run the unit tests + go test -v -race + +.PHONY: generate +generate: ## Generate fakes + go generate + +.PHONY: lint-prepare +lint-prepare: +ifdef GOLANG_CI_LINT_VERSION + @echo "Found golangci-lint $(GOLANG_CI_LINT_VERSION)" +else + @echo "Installing golangci-lint" + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s latest + @echo "[OK] golangci-lint installed" +endif + +.PHONY: lint +lint: lint-prepare ## Run the golangci linter + golangci-lint run + +.PHONY: tidy +tidy: ## Remove unused dependencies + go mod tidy + +.PHONY: list +list: ## Print the current module's dependencies. + go list -m all + +# Absolutely awesome: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html +help: ## Print help for each make target + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' \ No newline at end of file diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/README.md b/vendor/github.com/cloudfoundry-community/go-cfclient/README.md new file mode 100644 index 0000000..357a274 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/README.md @@ -0,0 +1,113 @@ +# go-cfclient + +[![build workflow](https://github.com/cloudfoundry-community/go-cfclient/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/cloudfoundry-community/go-cfclient/actions/workflows/build.yml) +[![GoDoc](https://godoc.org/github.com/cloudfoundry-community/go-cfclient?status.svg)](http://godoc.org/github.com/cloudfoundry-community/go-cfclient) +[![Report card](https://goreportcard.com/badge/github.com/cloudfoundry-community/go-cfclient)](https://goreportcard.com/report/github.com/cloudfoundry-community/go-cfclient) + +## Overview + +`cfclient` is a package to assist you in writing apps that need to interact with [Cloud Foundry](http://cloudfoundry.org). +It provides functions and structures to retrieve and update + + +## Usage + +``` +go get github.com/cloudfoundry-community/go-cfclient +``` + +NOTE: Currently this project is not versioning its releases and so breaking changes might be introduced. +Whilst hopefully notifications of breaking changes are made via commit messages, ideally your project will use a local +vendoring system to lock in a version of `go-cfclient` that is known to work for you. +This will allow you to control the timing and maintenance of upgrades to newer versions of this library. + +Some example code: + +```go +package main + +import ( + "fmt" + + "github.com/cloudfoundry-community/go-cfclient" +) + +func main() { + c := &cfclient.Config{ + ApiAddress: "https://api.10.244.0.34.xip.io", + Username: "admin", + Password: "secret", + } + client, _ := cfclient.NewClient(c) + apps, _ := client.ListApps() + fmt.Println(apps) +} +``` + +### Paging Results + +The API supports paging results via query string parameters. All of the v3 ListV3*ByQuery functions support paging. Only a subset of v2 function calls support paging the results: + +- ListSpacesByQuery +- ListOrgsByQuery +- ListAppsByQuery +- ListServiceInstancesByQuery +- ListUsersByQuery + +You can iterate over the results page-by-page using a function similar to this one: + +```go +func processSpacesOnePageAtATime(client *cfclient.Client) error { + page := 1 + pageSize := 50 + + q := url.Values{} + q.Add("results-per-page", strconv.Itoa(pageSize)) + + for { + // get the current page of spaces + q.Set("page", strconv.Itoa(page)) + spaces, err := client.ListSpacesByQuery(q) + if err != nil { + fmt.Printf("Error getting spaces by query: %s", err) + return err + } + + // do something with each space + fmt.Printf("Page %d:\n", page) + for _, s := range spaces { + fmt.Println(" " + s.Name) + } + + // if we hit an empty page or partial page, that means we're done + if len(spaces) < pageSize { + break + } + + // next page + page++ + } + return nil +} +``` + +## Development + +```shell +make all +``` + +### Errors + +If the Cloud Foundry error definitions change at +then the error predicate functions in this package need to be regenerated. + +To do this, simply use Go to regenerate the code: + +```shell +make generate +``` + +## Contributing + +Pull requests welcome. Please ensure you run all the unit tests, go fmt the code, and golangci-lint via `make all` diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/app_update.go b/vendor/github.com/cloudfoundry-community/go-cfclient/app_update.go new file mode 100644 index 0000000..458b366 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/app_update.go @@ -0,0 +1,128 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/pkg/errors" +) + +type UpdateResponse struct { + Metadata Meta `json:"metadata"` + Entity UpdateResponseEntity `json:"entity"` +} + +type AppUpdateResource struct { + Name string `json:"name,omitempty"` + Memory int `json:"memory,omitempty"` + Instances int `json:"instances,omitempty"` + DiskQuota int `json:"disk_quota,omitempty"` + SpaceGuid string `json:"space_guid,omitempty"` + StackGuid string `json:"stack_guid,omitempty"` + State AppState `json:"state,omitempty"` + Command string `json:"command,omitempty"` + Buildpack string `json:"buildpack,omitempty"` + HealthCheckHttpEndpoint string `json:"health_check_http_endpoint,omitempty"` + HealthCheckType string `json:"health_check_type,omitempty"` + HealthCheckTimeout int `json:"health_check_timeout,omitempty"` + Diego bool `json:"diego,omitempty"` + EnableSSH bool `json:"enable_ssh,omitempty"` + DockerImage string `json:"docker_image,omitempty"` + DockerCredentials map[string]interface{} `json:"docker_credentials_json,omitempty"` + Environment map[string]interface{} `json:"environment_json,omitempty"` + StagingFailedReason string `json:"staging_failed_reason,omitempty"` + StagingFailedDescription string `json:"staging_failed_description,omitempty"` + Ports []int `json:"ports,omitempty"` +} + +type UpdateResponseEntity struct { + Name string `json:"name"` + Production bool `json:"production"` + SpaceGuid string `json:"space_guid"` + StackGuid string `json:"stack_guid"` + Buildpack string `json:"buildpack"` + DetectedBuildpack string `json:"detected_buildpack"` + DetectedBuildpackGuid string `json:"detected_buildpack_guid"` + Environment map[string]interface{} `json:"environment_json"` + Memory int `json:"memory"` + Instances int `json:"instances"` + DiskQuota int `json:"disk_quota"` + State string `json:"state"` + Version string `json:"version"` + Command string `json:"command"` + Console bool `json:"console"` + Debug string `json:"debug"` + StagingTaskId string `json:"staging_task_id"` + PackageState string `json:"package_state"` + HealthCheckHttpEndpoint string `json:"health_check_http_endpoint"` + HealthCheckType string `json:"health_check_type"` + HealthCheckTimeout int `json:"health_check_timeout"` + StagingFailedReason string `json:"staging_failed_reason"` + StagingFailedDescription string `json:"staging_failed_description"` + Diego bool `json:"diego,omitempty"` + DockerImage string `json:"docker_image"` + DockerCredentials struct { + Username string `json:"username"` + Password string `json:"password"` + } `json:"docker_credentials"` + PackageUpdatedAt string `json:"package_updated_at"` + DetectedStartCommand string `json:"detected_start_command"` + EnableSSH bool `json:"enable_ssh"` + Ports []int `json:"ports"` + SpaceURL string `json:"space_url"` + StackURL string `json:"stack_url"` + RoutesURL string `json:"routes_url"` + EventsURL string `json:"events_url"` + ServiceBindingsUrl string `json:"service_bindings_url"` + RouteMappingsUrl string `json:"route_mappings_url"` +} + +func (c *Client) UpdateApp(guid string, aur AppUpdateResource) (UpdateResponse, error) { + var updateResponse UpdateResponse + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(aur) + if err != nil { + return UpdateResponse{}, err + } + req := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/apps/%s", guid), buf) + resp, err := c.DoRequest(req) + if err != nil { + return UpdateResponse{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return UpdateResponse{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return UpdateResponse{}, err + } + err = json.Unmarshal(body, &updateResponse) + if err != nil { + return UpdateResponse{}, err + } + return updateResponse, nil +} + +func (c *Client) RestageApp(guid string) (UpdateResponse, error) { + var result UpdateResponse + + req := c.NewRequest("POST", "/v2/apps/"+guid+"/restage") + resp, err := c.DoRequest(req) + if err != nil { + return result, errors.Wrap(err, "Error restaging app:") + } + + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return result, err + } + + return result, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/app_usage_events.go b/vendor/github.com/cloudfoundry-community/go-cfclient/app_usage_events.go new file mode 100644 index 0000000..227c4fe --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/app_usage_events.go @@ -0,0 +1,80 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/url" + + "github.com/pkg/errors" +) + +type AppUsageEvent struct { + GUID string `json:"guid"` + CreatedAt string `json:"created_at"` + State string `json:"state"` + PreviousState string `json:"previous_state"` + MemoryInMbPerInstance int `json:"memory_in_mb_per_instance"` + PreviousMemoryInMbPerInstance int `json:"previous_memory_in_mb_per_instance"` + InstanceCount int `json:"instance_count"` + PreviousInstanceCount int `json:"previous_instance_count"` + AppGUID string `json:"app_guid"` + SpaceGUID string `json:"space_guid"` + SpaceName string `json:"space_name"` + OrgGUID string `json:"org_guid"` + BuildpackGUID string `json:"buildpack_guid"` + BuildpackName string `json:"buildpack_name"` + PackageState string `json:"package_state"` + PreviousPackageState string `json:"previous_package_state"` + ParentAppGUID string `json:"parent_app_guid"` + ParentAppName string `json:"parent_app_name"` + ProcessType string `json:"process_type"` + TaskName string `json:"task_name"` + TaskGUID string `json:"task_guid"` + c *Client +} + +type AppUsageEventsResponse struct { + TotalResults int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []AppUsageEventResource `json:"resources"` +} + +type AppUsageEventResource struct { + Meta Meta `json:"metadata"` + Entity AppUsageEvent `json:"entity"` +} + +// ListAppUsageEventsByQuery lists all events matching the provided query. +func (c *Client) ListAppUsageEventsByQuery(query url.Values) ([]AppUsageEvent, error) { + var appUsageEvents []AppUsageEvent + requestURL := fmt.Sprintf("/v2/app_usage_events?%s", query.Encode()) + for { + var appUsageEventsResponse AppUsageEventsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "error requesting events") + } + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&appUsageEventsResponse); err != nil { + return nil, errors.Wrap(err, "error unmarshaling events") + } + for _, e := range appUsageEventsResponse.Resources { + e.Entity.GUID = e.Meta.Guid + e.Entity.CreatedAt = e.Meta.CreatedAt + e.Entity.c = c + appUsageEvents = append(appUsageEvents, e.Entity) + } + requestURL = appUsageEventsResponse.NextURL + if requestURL == "" { + break + } + } + return appUsageEvents, nil +} + +// ListAppUsageEvents lists all unfiltered events. +func (c *Client) ListAppUsageEvents() ([]AppUsageEvent, error) { + return c.ListAppUsageEventsByQuery(nil) +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/appevents.go b/vendor/github.com/cloudfoundry-community/go-cfclient/appevents.go new file mode 100644 index 0000000..1f6c769 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/appevents.go @@ -0,0 +1,174 @@ +package cfclient + +import ( + "encoding/json" + "io/ioutil" + "time" + + "github.com/pkg/errors" +) + +// Exported event constants +const ( + AppCrash = "app.crash" + AppStart = "audit.app.start" + AppStop = "audit.app.stop" + AppUpdate = "audit.app.update" + AppCreate = "audit.app.create" + AppDelete = "audit.app.delete-request" + AppSSHAuth = "audit.app.ssh-authorized" + AppSSHUnauth = "audit.app.ssh-unauthorized" + AppRestage = "audit.app.restage" + AppMapRoute = "audit.app.map-route" + AppUnmapRoute = "audit.app.unmap-route" +) + +// Exported filter constants +const ( + FilterTimestamp = "timestamp" + FilterActee = "actee" +) + +// ValidOperators global variable for all valid operators in a query +var ValidOperators = []string{":", ">=", "<=", "<", ">", "IN"} + +// AppEventResponse the entire response +type AppEventResponse struct { + Results int `json:"total_results"` + Pages int `json:"total_pages"` + PrevURL string `json:"prev_url"` + NextURL string `json:"next_url"` + Resources []AppEventResource `json:"resources"` +} + +// AppEventResource the event resources +type AppEventResource struct { + Meta Meta `json:"metadata"` + Entity AppEventEntity `json:"entity"` +} + +// AppEventQuery a struct for defining queries like 'q=filter>value' or 'q=filter IN a,b,c' +type AppEventQuery struct { + Filter string + Operator string + Value string +} + +// The AppEventEntity the actual app event body +type AppEventEntity struct { + // EventTypes are app.crash, audit.app.start, audit.app.stop, audit.app.update, audit.app.create, audit.app.delete-request + EventType string `json:"type"` + // The GUID of the actor. + Actor string `json:"actor"` + // The actor type, user or app + ActorType string `json:"actor_type"` + // The name of the actor. + ActorName string `json:"actor_name"` + // The GUID of the actee. + Actee string `json:"actee"` + // The actee type, space, app or v3-app + ActeeType string `json:"actee_type"` + // The name of the actee. + ActeeName string `json:"actee_name"` + // Timestamp format "2016-02-26T13:29:44Z". The event creation time. + Timestamp time.Time `json:"timestamp"` + MetaData struct { + // app.crash event fields + ExitDescription string `json:"exit_description,omitempty"` + ExitReason string `json:"reason,omitempty"` + ExitStatus string `json:"exit_status,omitempty"` + + Request struct { + Name string `json:"name,omitempty"` + Instances float64 `json:"instances,omitempty"` + State string `json:"state,omitempty"` + Memory float64 `json:"memory,omitempty"` + EnvironmentVars string `json:"environment_json,omitempty"` + DockerCredentials string `json:"docker_credentials_json,omitempty"` + // audit.app.create event fields + Console bool `json:"console,omitempty"` + Buildpack string `json:"buildpack,omitempty"` + Space string `json:"space_guid,omitempty"` + HealthcheckType string `json:"health_check_type,omitempty"` + HealthcheckTimeout float64 `json:"health_check_timeout,omitempty"` + Production bool `json:"production,omitempty"` + // app.crash event fields + Index float64 `json:"index,omitempty"` + } `json:"request"` + } `json:"metadata"` +} + +// ListAppEvents returns all app events based on eventType +func (c *Client) ListAppEvents(eventType string) ([]AppEventEntity, error) { + return c.ListAppEventsByQuery(eventType, nil) +} + +// ListAppEventsByQuery returns all app events based on eventType and queries +func (c *Client) ListAppEventsByQuery(eventType string, queries []AppEventQuery) ([]AppEventEntity, error) { + var events []AppEventEntity + + if eventType != AppCrash && eventType != AppStart && eventType != AppStop && eventType != AppUpdate && eventType != AppCreate && + eventType != AppDelete && eventType != AppSSHAuth && eventType != AppSSHUnauth && eventType != AppRestage && + eventType != AppMapRoute && eventType != AppUnmapRoute { + return nil, errors.New("Unsupported app event type " + eventType) + } + + var query = "/v2/events?q=type:" + eventType + // adding the additional queries + if len(queries) > 0 { + for _, eventQuery := range queries { + if eventQuery.Filter != FilterTimestamp && eventQuery.Filter != FilterActee { + return nil, errors.New("Unsupported query filter type " + eventQuery.Filter) + } + if !stringInSlice(eventQuery.Operator, ValidOperators) { + return nil, errors.New("Unsupported query operator type " + eventQuery.Operator) + } + query += "&q=" + eventQuery.Filter + eventQuery.Operator + eventQuery.Value + } + } + + for { + eventResponse, err := c.getAppEventsResponse(query) + if err != nil { + return []AppEventEntity{}, err + } + for _, event := range eventResponse.Resources { + events = append(events, event.Entity) + } + query = eventResponse.NextURL + if query == "" { + break + } + } + + return events, nil +} + +func (c *Client) getAppEventsResponse(query string) (AppEventResponse, error) { + var eventResponse AppEventResponse + r := c.NewRequest("GET", query) + resp, err := c.DoRequest(r) + if err != nil { + return AppEventResponse{}, errors.Wrap(err, "Error requesting appevents") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return AppEventResponse{}, errors.Wrap(err, "Error reading appevents response body") + } + + err = json.Unmarshal(resBody, &eventResponse) + if err != nil { + return AppEventResponse{}, errors.Wrap(err, "Error unmarshalling appevent") + } + return eventResponse, nil +} + +func stringInSlice(str string, list []string) bool { + for _, v := range list { + if v == str { + return true + } + } + return false +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/apps.go b/vendor/github.com/cloudfoundry-community/go-cfclient/apps.go new file mode 100644 index 0000000..d4cba0c --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/apps.go @@ -0,0 +1,795 @@ +package cfclient + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" +) + +type AppResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []AppResource `json:"resources"` +} + +type AppResource struct { + Meta Meta `json:"metadata"` + Entity App `json:"entity"` +} + +type AppState string + +const ( + APP_STOPPED AppState = "STOPPED" + APP_STARTED AppState = "STARTED" +) + +type HealthCheckType string + +const ( + HEALTH_HTTP HealthCheckType = "http" + HEALTH_PORT HealthCheckType = "port" + HEALTH_PROCESS HealthCheckType = "process" +) + +type DockerCredentials struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + +type AppCreateRequest struct { + Name string `json:"name"` + SpaceGuid string `json:"space_guid"` + // Memory for the app, in MB + Memory int `json:"memory,omitempty"` + // Instances to startup + Instances int `json:"instances,omitempty"` + // Disk quota in MB + DiskQuota int `json:"disk_quota,omitempty"` + StackGuid string `json:"stack_guid,omitempty"` + // Desired state of the app. Either "STOPPED" or "STARTED" + State AppState `json:"state,omitempty"` + // Command to start an app + Command string `json:"command,omitempty"` + // Buildpack to build the app. Three options: + // 1. Blank for autodetection + // 2. GIT url + // 3. Name of an installed buildpack + Buildpack string `json:"buildpack,omitempty"` + // Endpoint to check if an app is healthy + HealthCheckHttpEndpoint string `json:"health_check_http_endpoint,omitempty"` + // How to check if an app is healthy. Defaults to HEALTH_PORT if not specified + HealthCheckType HealthCheckType `json:"health_check_type,omitempty"` + HealthCheckTimeout int `json:"health_check_timeout,omitempty"` + Diego bool `json:"diego,omitempty"` + EnableSSH bool `json:"enable_ssh,omitempty"` + DockerImage string `json:"docker_image,omitempty"` + DockerCredentials DockerCredentials `json:"docker_credentials,omitempty"` + Environment map[string]interface{} `json:"environment_json,omitempty"` +} + +type App struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + Memory int `json:"memory"` + Instances int `json:"instances"` + DiskQuota int `json:"disk_quota"` + SpaceGuid string `json:"space_guid"` + StackGuid string `json:"stack_guid"` + State string `json:"state"` + PackageState string `json:"package_state"` + Command string `json:"command"` + Buildpack string `json:"buildpack"` + DetectedBuildpack string `json:"detected_buildpack"` + DetectedBuildpackGuid string `json:"detected_buildpack_guid"` + HealthCheckHttpEndpoint string `json:"health_check_http_endpoint"` + HealthCheckType string `json:"health_check_type"` + HealthCheckTimeout int `json:"health_check_timeout"` + Diego bool `json:"diego"` + EnableSSH bool `json:"enable_ssh"` + DetectedStartCommand string `json:"detected_start_command"` + DockerImage string `json:"docker_image"` + DockerCredentialsJSON map[string]interface{} `json:"docker_credentials_json"` + DockerCredentials DockerCredentials `json:"docker_credentials"` + Environment map[string]interface{} `json:"environment_json"` + StagingFailedReason string `json:"staging_failed_reason"` + StagingFailedDescription string `json:"staging_failed_description"` + Ports []int `json:"ports"` + SpaceURL string `json:"space_url"` + SpaceData SpaceResource `json:"space"` + PackageUpdatedAt string `json:"package_updated_at"` + c *Client +} + +type AppInstance struct { + State string `json:"state"` + Since sinceTime `json:"since"` +} + +type AppStats struct { + State string `json:"state"` + Stats struct { + Name string `json:"name"` + Uris []string `json:"uris"` + Host string `json:"host"` + Port int `json:"port"` + Uptime int `json:"uptime"` + MemQuota int `json:"mem_quota"` + DiskQuota int `json:"disk_quota"` + FdsQuota int `json:"fds_quota"` + Usage struct { + Time statTime `json:"time"` + CPU float64 `json:"cpu"` + Mem int `json:"mem"` + Disk int `json:"disk"` + } `json:"usage"` + } `json:"stats"` +} + +type AppSummary struct { + Guid string `json:"guid"` + Name string `json:"name"` + ServiceCount int `json:"service_count"` + RunningInstances int `json:"running_instances"` + SpaceGuid string `json:"space_guid"` + StackGuid string `json:"stack_guid"` + Buildpack string `json:"buildpack"` + DetectedBuildpack string `json:"detected_buildpack"` + Environment map[string]interface{} `json:"environment_json"` + Memory int `json:"memory"` + Instances int `json:"instances"` + DiskQuota int `json:"disk_quota"` + State string `json:"state"` + Command string `json:"command"` + PackageState string `json:"package_state"` + HealthCheckType string `json:"health_check_type"` + HealthCheckTimeout int `json:"health_check_timeout"` + StagingFailedReason string `json:"staging_failed_reason"` + StagingFailedDescription string `json:"staging_failed_description"` + Diego bool `json:"diego"` + DockerImage string `json:"docker_image"` + DetectedStartCommand string `json:"detected_start_command"` + EnableSSH bool `json:"enable_ssh"` + DockerCredentials map[string]interface{} `json:"docker_credentials_json"` +} + +type AppEnv struct { + // These can have arbitrary JSON so need to map to interface{} + Environment map[string]interface{} `json:"environment_json"` + StagingEnv map[string]interface{} `json:"staging_env_json"` + RunningEnv map[string]interface{} `json:"running_env_json"` + SystemEnv map[string]interface{} `json:"system_env_json"` + ApplicationEnv map[string]interface{} `json:"application_env_json"` +} + +// Custom time types to handle non-RFC3339 formatting in API JSON + +type sinceTime struct { + time.Time +} + +func (s *sinceTime) UnmarshalJSON(b []byte) (err error) { + timeFlt, err := strconv.ParseFloat(string(b), 64) + if err != nil { + return err + } + time := time.Unix(int64(timeFlt), 0) + *s = sinceTime{time} + return nil +} + +func (s sinceTime) ToTime() time.Time { + t, err := time.Parse(time.UnixDate, s.Format(time.UnixDate)) + if err != nil { + panic(err) + } + return t +} + +type statTime struct { + time.Time +} + +func (s *statTime) UnmarshalJSON(b []byte) (err error) { + timeString, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + + possibleFormats := [...]string{time.RFC3339, time.RFC3339Nano, "2006-01-02 15:04:05 -0700", "2006-01-02 15:04:05 MST"} + + var value time.Time + for _, possibleFormat := range possibleFormats { + if value, err = time.Parse(possibleFormat, timeString); err == nil { + *s = statTime{value} + return nil + } + } + + return fmt.Errorf("%s was not in any of the expected Date Formats %v", timeString, possibleFormats) +} + +func (s statTime) ToTime() time.Time { + t, err := time.Parse(time.UnixDate, s.Format(time.UnixDate)) + if err != nil { + panic(err) + } + return t +} + +func (a *App) Space() (Space, error) { + var spaceResource SpaceResource + r := a.c.NewRequest("GET", a.SpaceURL) + resp, err := a.c.DoRequest(r) + if err != nil { + return Space{}, errors.Wrap(err, "Error requesting space") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return Space{}, errors.Wrap(err, "Error reading space response") + } + + err = json.Unmarshal(resBody, &spaceResource) + if err != nil { + return Space{}, errors.Wrap(err, "Error unmarshalling body") + } + return a.c.mergeSpaceResource(spaceResource), nil +} + +func (a *App) Summary() (AppSummary, error) { + var appSummary AppSummary + requestUrl := fmt.Sprintf("/v2/apps/%s/summary", a.Guid) + r := a.c.NewRequest("GET", requestUrl) + resp, err := a.c.DoRequest(r) + if err != nil { + return AppSummary{}, errors.Wrap(err, "Error requesting app summary") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return AppSummary{}, errors.Wrap(err, "Error reading app summary body") + } + err = json.Unmarshal(resBody, &appSummary) + if err != nil { + return AppSummary{}, errors.Wrap(err, "Error unmarshalling app summary") + } + return appSummary, nil +} + +// ListAppsByQueryWithLimits queries totalPages app info. When totalPages is +// less and equal than 0, it queries all app info +// When there are no more than totalPages apps on server side, all apps info will be returned +func (c *Client) ListAppsByQueryWithLimits(query url.Values, totalPages int) ([]App, error) { + return c.listApps("/v2/apps", query, totalPages) +} + +func (c *Client) ListAppsByQuery(query url.Values) ([]App, error) { + return c.listApps("/v2/apps", query, -1) +} + +// GetAppByGuidNoInlineCall will fetch app info including space and orgs information +// Without using inline-relations-depth=2 call +func (c *Client) GetAppByGuidNoInlineCall(guid string) (App, error) { + var appResource AppResource + r := c.NewRequest("GET", "/v2/apps/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return App{}, errors.Wrap(err, "Error requesting apps") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return App{}, errors.Wrap(err, "Error reading app response body") + } + + err = json.Unmarshal(resBody, &appResource) + if err != nil { + return App{}, errors.Wrap(err, "Error unmarshalling app") + } + app := c.mergeAppResource(appResource) + + // If no Space Information no need to check org. + if app.SpaceGuid != "" { + // Getting Spaces Resource + space, err := app.Space() + if err != nil { + return App{}, errors.Wrap(err, "Unable to get the Space for the app "+app.Name) + } else { + app.SpaceData.Entity = space + } + + // Getting orgResource + org, err := app.SpaceData.Entity.Org() + if err != nil { + return App{}, errors.Wrap(err, "Unable to get the Org for the app "+app.Name) + } else { + app.SpaceData.Entity.OrgData.Entity = org + } + } + + return app, nil +} + +func (c *Client) ListApps() ([]App, error) { + q := url.Values{} + q.Set("inline-relations-depth", "2") + return c.ListAppsByQuery(q) +} + +func (c *Client) ListAppsByRoute(routeGuid string) ([]App, error) { + return c.listApps(fmt.Sprintf("/v2/routes/%s/apps", routeGuid), url.Values{}, -1) +} + +func (c *Client) ListAppsBySpaceGuid(spaceGuid string) ([]App, error) { + return c.listApps(fmt.Sprintf("/v2/spaces/%s/apps", spaceGuid), url.Values{}, -1) +} + +func (c *Client) listApps(path string, query url.Values, totalPages int) ([]App, error) { + requestUrl := path + "?" + query.Encode() + pages := 0 + apps := []App{} + for { + var appResp AppResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + + if err != nil { + return nil, errors.Wrap(err, "Error requesting apps") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading app request") + } + + err = json.Unmarshal(resBody, &appResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling app") + } + for _, app := range appResp.Resources { + apps = append(apps, c.mergeAppResource(app)) + } + + requestUrl = appResp.NextUrl + if requestUrl == "" || query.Get("page") != "" { + break + } + + pages++ + if totalPages > 0 && pages >= totalPages { + break + } + } + return apps, nil +} + +func (c *Client) GetAppInstances(guid string) (map[string]AppInstance, error) { + var appInstances map[string]AppInstance + + requestURL := fmt.Sprintf("/v2/apps/%s/instances", guid) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting app instances") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading app instances") + } + err = json.Unmarshal(resBody, &appInstances) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling app instances") + } + return appInstances, nil +} + +func (c *Client) GetAppEnv(guid string) (AppEnv, error) { + var appEnv AppEnv + + requestURL := fmt.Sprintf("/v2/apps/%s/env", guid) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return appEnv, errors.Wrap(err, "Error requesting app env") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return appEnv, errors.Wrap(err, "Error reading app env") + } + err = json.Unmarshal(resBody, &appEnv) + if err != nil { + return appEnv, errors.Wrap(err, "Error unmarshalling app env") + } + return appEnv, nil +} + +func (c *Client) GetAppRoutes(guid string) ([]Route, error) { + return c.fetchRoutes(fmt.Sprintf("/v2/apps/%s/routes", guid)) +} + +func (c *Client) GetAppStats(guid string) (map[string]AppStats, error) { + var appStats map[string]AppStats + + requestURL := fmt.Sprintf("/v2/apps/%s/stats", guid) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting app stats") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading app stats") + } + err = json.Unmarshal(resBody, &appStats) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling app stats") + } + return appStats, nil +} + +func (c *Client) KillAppInstance(guid string, index string) error { + requestURL := fmt.Sprintf("/v2/apps/%s/instances/%s", guid, index) + r := c.NewRequest("DELETE", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return errors.Wrapf(err, "Error stopping app %s at index %s", guid, index) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error stopping app %s at index %s", guid, index) + } + return nil +} + +func (c *Client) GetAppByGuid(guid string) (App, error) { + var appResource AppResource + r := c.NewRequest("GET", "/v2/apps/"+guid+"?inline-relations-depth=2") + resp, err := c.DoRequest(r) + if err != nil { + return App{}, errors.Wrap(err, "Error requesting apps") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return App{}, errors.Wrap(err, "Error reading app response body") + } + + err = json.Unmarshal(resBody, &appResource) + if err != nil { + return App{}, errors.Wrap(err, "Error unmarshalling app") + } + return c.mergeAppResource(appResource), nil +} + +func (c *Client) AppByGuid(guid string) (App, error) { + return c.GetAppByGuid(guid) +} + +// AppByName takes an appName, and GUIDs for a space and org, and performs +// the API lookup with those query parameters set to return you the desired +// App object. +func (c *Client) AppByName(appName, spaceGuid, orgGuid string) (App, error) { + query := url.Values{} + query.Add("q", fmt.Sprintf("organization_guid:%s", orgGuid)) + query.Add("q", fmt.Sprintf("space_guid:%s", spaceGuid)) + query.Add("q", fmt.Sprintf("name:%s", appName)) + apps, err := c.ListAppsByQuery(query) + if err != nil { + return App{}, err + } + if len(apps) == 0 { + cfErr := NewAppNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, appName) + return App{}, cfErr + } + return apps[0], nil +} + +// UploadAppBits uploads the application's contents +func (c *Client) UploadAppBits(file io.Reader, appGUID string) error { + requestFile, err := ioutil.TempFile("", "requests") + if err != nil { + return errors.Wrap(err, "Could not create temp file for app bits") + } + + defer func() { + requestFile.Close() + os.Remove(requestFile.Name()) + }() + + writer := multipart.NewWriter(requestFile) + err = writer.WriteField("resources", "[]") + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits", appGUID) + } + + part, err := writer.CreateFormFile("application", "application.zip") + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits", appGUID) + } + + _, err = io.Copy(part, file) + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits, failed to copy all bytes", appGUID) + } + + err = writer.Close() + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits, failed to close multipart writer", appGUID) + } + + _, err = requestFile.Seek(0, 0) + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits, failed to seek beginning of file", appGUID) + } + fileStats, err := requestFile.Stat() + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits, failed to get temp file stats", appGUID) + } + + requestURL := fmt.Sprintf("/v2/apps/%s/bits", appGUID) + r := c.NewRequestWithBody("PUT", requestURL, requestFile) + req, err := r.toHTTP() + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits", appGUID) + } + + req.ContentLength = fileStats.Size() + contentType := fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary()) + req.Header.Set("Content-Type", contentType) + + resp, err := c.Do(req) + if err != nil { + return errors.Wrapf(err, "Error uploading app %s bits", appGUID) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return errors.Wrapf(err, "Error uploading app %s bits, response code: %d", appGUID, resp.StatusCode) + } + + return nil +} + +// GetAppBits downloads the application's bits as a tar file +func (c *Client) GetAppBits(guid string) (io.ReadCloser, error) { + requestURL := fmt.Sprintf("/v2/apps/%s/download", guid) + req := c.NewRequest("GET", requestURL) + resp, err := c.DoRequestWithoutRedirects(req) + if err != nil { + return nil, errors.Wrapf(err, "Error downloading app %s bits, API request failed", guid) + } + defer resp.Body.Close() + if isResponseRedirect(resp) { + // directly download the bits from blobstore using a non cloud controller transport + // some blobstores will return a 400 if an Authorization header is sent + blobStoreLocation := resp.Header.Get("Location") + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: c.Config.SkipSslValidation}, + } + client := &http.Client{Transport: tr} + resp, err = client.Get(blobStoreLocation) + if err != nil { + return nil, errors.Wrapf(err, "Error downloading app %s bits from blobstore", guid) + } + } else { + return nil, errors.Wrapf(err, "Error downloading app %s bits, expected redirect to blobstore", guid) + } + return resp.Body, nil +} + +// GetDropletBits downloads the application's droplet bits as a tar file +func (c *Client) GetDropletBits(guid string) (io.ReadCloser, error) { + requestURL := fmt.Sprintf("/v2/apps/%s/droplet/download", guid) + req := c.NewRequest("GET", requestURL) + resp, err := c.DoRequestWithoutRedirects(req) + if err != nil { + return nil, errors.Wrapf(err, "Error downloading droplet %s bits, API request failed", guid) + } + defer resp.Body.Close() + if isResponseRedirect(resp) { + // directly download the bits from blobstore using a non cloud controller transport + // some blobstores will return a 400 if an Authorization header is sent + blobStoreLocation := resp.Header.Get("Location") + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: c.Config.SkipSslValidation}, + } + client := &http.Client{Transport: tr} + resp, err = client.Get(blobStoreLocation) + if err != nil { + return nil, errors.Wrapf(err, "Error downloading droplet %s bits from blobstore", guid) + } + } else { + return nil, errors.Wrapf(err, "Error downloading droplet %s bits, expected redirect to blobstore", guid) + } + return resp.Body, nil +} + +// GetDropletBits downloads the application's droplet bits as a tar file +// Returns the GUID, job URL for monitoring, and an error +func (c *Client) UploadDropletBits(dropletReader io.Reader, appGUID string) (string, error) { + dropletFile, err := ioutil.TempFile("", "droplet") + if err != nil { + return "", errors.Wrap(err, "Could not create temp file for droplet bits") + } + + defer func() { + dropletFile.Close() + os.Remove(dropletFile.Name()) + }() + + writer := multipart.NewWriter(dropletFile) + part, err := writer.CreateFormFile("droplet", "droplet.tgz") + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet", appGUID) + } + + _, err = io.Copy(part, dropletReader) + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet, failed to copy all bytes", appGUID) + } + + err = writer.Close() + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet, failed to close multipart writer", appGUID) + } + + _, err = dropletFile.Seek(0, 0) + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet, failed to seek beginning of file", appGUID) + } + fileStats, err := dropletFile.Stat() + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet, failed to get temp file stats", appGUID) + } + + requestURL := fmt.Sprintf("/v2/apps/%s/droplet/upload", appGUID) + r := c.NewRequestWithBody("PUT", requestURL, dropletFile) + req, err := r.toHTTP() + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet", appGUID) + } + + req.ContentLength = fileStats.Size() + req.Header.Set("Content-Type", writer.FormDataContentType()) + + resp, err := c.Do(req) + if err != nil { + return "", errors.Wrapf(err, "Error uploading app %s droplet", appGUID) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return "", errors.Wrapf(err, "Error uploading app %s droplet, response code: %d", appGUID, resp.StatusCode) + } + + var respObj struct { + Metadata struct { + GUID string `json:"guid"` + URL string `json:"url"` + } `json:"metadata"` + } + + if err = json.NewDecoder(resp.Body).Decode(&respObj); err != nil { + return "", errors.Wrapf(err, "Error parsing response") + } + + return respObj.Metadata.URL, nil +} + +// CreateApp creates a new empty application that still needs it's +// app bit uploaded and to be started +func (c *Client) CreateApp(req AppCreateRequest) (App, error) { + var appResp AppResource + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return App{}, err + } + r := c.NewRequestWithBody("POST", "/v2/apps", buf) + resp, err := c.DoRequest(r) + if err != nil { + return App{}, errors.Wrapf(err, "Error creating app %s", req.Name) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return App{}, errors.Wrapf(err, "Error creating app %s, response code: %d", req.Name, resp.StatusCode) + } + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return App{}, errors.Wrapf(err, "Error reading app %s http response body", req.Name) + } + err = json.Unmarshal(resBody, &appResp) + if err != nil { + return App{}, errors.Wrapf(err, "Error deserializing app %s response", req.Name) + } + return c.mergeAppResource(appResp), nil +} + +func (c *Client) StartApp(guid string) error { + startRequest := strings.NewReader(`{ "state": "STARTED" }`) + resp, err := c.DoRequest(c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/apps/%s", guid), startRequest)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error starting app %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) StopApp(guid string) error { + stopRequest := strings.NewReader(`{ "state": "STOPPED" }`) + resp, err := c.DoRequest(c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/apps/%s", guid), stopRequest)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error stopping app %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) DeleteApp(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/apps/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting app %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) RestartApp(guid string) error { + var err error + err = c.StopApp(guid) + if err != nil { + return err + } + + err = c.StartApp(guid) + if err != nil { + return err + } + + return nil +} + +func (c *Client) mergeAppResource(app AppResource) App { + app.Entity.Guid = app.Meta.Guid + app.Entity.CreatedAt = app.Meta.CreatedAt + app.Entity.UpdatedAt = app.Meta.UpdatedAt + app.Entity.SpaceData.Entity.Guid = app.Entity.SpaceData.Meta.Guid + app.Entity.SpaceData.Entity.OrgData.Entity.Guid = app.Entity.SpaceData.Entity.OrgData.Meta.Guid + app.Entity.c = c + return app.Entity +} + +func isResponseRedirect(res *http.Response) bool { + switch res.StatusCode { + case http.StatusTemporaryRedirect, http.StatusPermanentRedirect, http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther: + return true + } + return false +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/buildpacks.go b/vendor/github.com/cloudfoundry-community/go-cfclient/buildpacks.go new file mode 100644 index 0000000..3ac6978 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/buildpacks.go @@ -0,0 +1,264 @@ +package cfclient + +import ( + "encoding/json" + "io" + "io/ioutil" + "mime/multipart" + "os" + + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +type BuildpackResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []BuildpackResource `json:"resources"` +} + +type BuildpackResource struct { + Meta Meta `json:"metadata"` + Entity Buildpack `json:"entity"` +} + +type Buildpack struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + Enabled bool `json:"enabled"` + Locked bool `json:"locked"` + Position int `json:"position"` + Filename string `json:"filename"` + Stack string `json:"stack"` + c *Client +} + +type BuildpackRequest struct { + // These are all pointers to the values so that we can tell + // whether people wanted position 0, or enable/unlock values, + // vs whether they didn't specify them and want them unchanged/default. + Name *string `json:"name,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Locked *bool `json:"locked,omitempty"` + Position *int `json:"position,omitempty"` + Stack *string `json:"stack,omitempty"` +} + +func (c *Client) CreateBuildpack(bpr *BuildpackRequest) (*Buildpack, error) { + if bpr.Name == nil || *bpr.Name == "" { + return nil, errors.New("Unable to create a buidlpack with no name") + } + requestUrl := "/v2/buildpacks" + req := c.NewRequest("POST", requestUrl) + req.obj = bpr + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error creating buildpack:") + } + defer resp.Body.Close() + bp, err := c.handleBuildpackResp(resp) + if err != nil { + return nil, errors.Wrap(err, "Error creating buildpack:") + } + return &bp, nil +} + +func (c *Client) ListBuildpacks() ([]Buildpack, error) { + var buildpacks []Buildpack + requestUrl := "/v2/buildpacks" + for { + buildpackResp, err := c.getBuildpackResponse(requestUrl) + if err != nil { + return []Buildpack{}, err + } + for _, buildpack := range buildpackResp.Resources { + buildpacks = append(buildpacks, c.mergeBuildpackResource(buildpack)) + } + requestUrl = buildpackResp.NextUrl + if requestUrl == "" { + break + } + } + return buildpacks, nil +} + +func (c *Client) DeleteBuildpack(guid string, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/buildpacks/%s?async=%t", guid, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if (async && (resp.StatusCode != http.StatusAccepted)) || (!async && (resp.StatusCode != http.StatusNoContent)) { + return errors.Wrapf(err, "Error deleting buildpack %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) getBuildpackResponse(requestUrl string) (BuildpackResponse, error) { + var buildpackResp BuildpackResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return BuildpackResponse{}, errors.Wrap(err, "Error requesting buildpacks") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return BuildpackResponse{}, errors.Wrap(err, "Error reading buildpack request") + } + err = json.Unmarshal(resBody, &buildpackResp) + if err != nil { + return BuildpackResponse{}, errors.Wrap(err, "Error unmarshalling buildpack") + } + return buildpackResp, nil +} + +func (c *Client) mergeBuildpackResource(buildpack BuildpackResource) Buildpack { + buildpack.Entity.Guid = buildpack.Meta.Guid + buildpack.Entity.CreatedAt = buildpack.Meta.CreatedAt + buildpack.Entity.UpdatedAt = buildpack.Meta.UpdatedAt + buildpack.Entity.c = c + return buildpack.Entity +} + +func (c *Client) GetBuildpackByGuid(buildpackGUID string) (Buildpack, error) { + requestUrl := fmt.Sprintf("/v2/buildpacks/%s", buildpackGUID) + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return Buildpack{}, errors.Wrap(err, "Error requesting buildpack info") + } + + return c.handleBuildpackResp(resp) +} + +func (c *Client) handleBuildpackResp(resp *http.Response) (Buildpack, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Buildpack{}, err + } + var buildpackResource BuildpackResource + if err := json.Unmarshal(body, &buildpackResource); err != nil { + return Buildpack{}, err + } + return c.mergeBuildpackResource(buildpackResource), nil +} + +func (b *Buildpack) Upload(file io.Reader, fileName string) error { + var capturedErr error + tempFile("requests", func(requestFile *os.File, err error) { + if err != nil { + capturedErr = err + return + } + writer := multipart.NewWriter(requestFile) + part, err := writer.CreateFormFile("buildpack", fileName) + + if err != nil { + _ = writer.Close() + capturedErr = err + return + } + + _, err = io.Copy(part, file) + if err != nil { + capturedErr = fmt.Errorf("Error creating upload: %s", err.Error()) + return + } + + err = writer.Close() + if err != nil { + capturedErr = err + return + } + + _, err = requestFile.Seek(0, 0) + if err != nil { + capturedErr = fmt.Errorf("Error seeking beginning of file: %s", err) + } + fileStats, err := requestFile.Stat() + if err != nil { + capturedErr = fmt.Errorf("Error getting file info: %s", err) + } + + req, err := http.NewRequest("PUT", fmt.Sprintf("%s/v2/buildpacks/%s/bits", b.c.Config.ApiAddress, b.Guid), requestFile) + if err != nil { + capturedErr = err + return + } + + req.ContentLength = fileStats.Size() + contentType := fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary()) + req.Header.Set("Content-Type", contentType) + resp, err := b.c.Do(req) // client.Do() handles the HTTP status code checking for us + if err != nil { + capturedErr = err + return + } + defer resp.Body.Close() + }) + + return errors.Wrap(capturedErr, "Error uploading buildpack:") +} + +func (b *Buildpack) Update(bpr *BuildpackRequest) error { + requestUrl := fmt.Sprintf("/v2/buildpacks/%s", b.Guid) + req := b.c.NewRequest("PUT", requestUrl) + req.obj = bpr + resp, err := b.c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error updating buildpack:") + } + defer resp.Body.Close() + newBp, err := b.c.handleBuildpackResp(resp) + if err != nil { + return errors.Wrap(err, "Error updating buildpack:") + } + b.Name = newBp.Name + b.Locked = newBp.Locked + b.Enabled = newBp.Enabled + return nil +} + +func (bpr *BuildpackRequest) Lock() { + b := true + bpr.Locked = &b +} +func (bpr *BuildpackRequest) Unlock() { + b := false + bpr.Locked = &b +} +func (bpr *BuildpackRequest) Enable() { + b := true + bpr.Enabled = &b +} +func (bpr *BuildpackRequest) Disable() { + b := false + bpr.Enabled = &b +} +func (bpr *BuildpackRequest) SetPosition(i int) { + bpr.Position = &i +} +func (bpr *BuildpackRequest) SetName(s string) { + bpr.Name = &s +} +func (bpr *BuildpackRequest) SetStack(s string) { + bpr.Stack = &s +} + +func tempFile(namePrefix string, cb func(tmpFile *os.File, err error)) { + tmpFile, err := ioutil.TempFile("", namePrefix) + + defer func() { + _ = tmpFile.Close() + _ = os.Remove(tmpFile.Name()) + }() + + cb(tmpFile, err) +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/cf_error.go b/vendor/github.com/cloudfoundry-community/go-cfclient/cf_error.go new file mode 100644 index 0000000..77a0454 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/cf_error.go @@ -0,0 +1,6119 @@ +package cfclient + +// Code generated by go generate. DO NOT EDIT. +// This file was generated by robots at +// 2022-01-11 09:36:11.804559 +1030 ACDT m=+0.186550187 + +import ( + stderrors "errors" + + pkgerrors "github.com/pkg/errors" +) + +// NewInvalidAuthTokenError returns a new CloudFoundryError +// that IsInvalidAuthTokenError will return true for +func NewInvalidAuthTokenError() CloudFoundryError { + return CloudFoundryError{ + Code: 1000, + ErrorCode: "CF-InvalidAuthToken", + Description: "Invalid Auth Token", + } +} + +// IsInvalidAuthTokenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1000 +// - HTTP code: 401 +// - message: "Invalid Auth Token" +func IsInvalidAuthTokenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1000 +} + +// NewMessageParseError returns a new CloudFoundryError +// that IsMessageParseError will return true for +func NewMessageParseError() CloudFoundryError { + return CloudFoundryError{ + Code: 1001, + ErrorCode: "CF-MessageParseError", + Description: "Request invalid due to parse error: %s", + } +} + +// IsMessageParseError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1001 +// - HTTP code: 400 +// - message: "Request invalid due to parse error: %s" +func IsMessageParseError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1001 +} + +// NewInvalidRelationError returns a new CloudFoundryError +// that IsInvalidRelationError will return true for +func NewInvalidRelationError() CloudFoundryError { + return CloudFoundryError{ + Code: 1002, + ErrorCode: "CF-InvalidRelation", + Description: "%s", + } +} + +// IsInvalidRelationError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1002 +// - HTTP code: 400 +// - message: "%s" +func IsInvalidRelationError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1002 +} + +// NewInvalidContentTypeError returns a new CloudFoundryError +// that IsInvalidContentTypeError will return true for +func NewInvalidContentTypeError() CloudFoundryError { + return CloudFoundryError{ + Code: 1003, + ErrorCode: "CF-InvalidContentType", + Description: "Invalid content type, expected: %s", + } +} + +// IsInvalidContentTypeError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1003 +// - HTTP code: 400 +// - message: "Invalid content type, expected: %s" +func IsInvalidContentTypeError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1003 +} + +// NewBadRequestError returns a new CloudFoundryError +// that IsBadRequestError will return true for +func NewBadRequestError() CloudFoundryError { + return CloudFoundryError{ + Code: 1004, + ErrorCode: "CF-BadRequest", + Description: "Bad request: %s", + } +} + +// IsBadRequestError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 1004 +// - HTTP code: 400 +// - message: "Bad request: %s" +func IsBadRequestError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 1004 +} + +// NewNotFoundError returns a new CloudFoundryError +// that IsNotFoundError will return true for +func NewNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 10000, + ErrorCode: "CF-NotFound", + Description: "Unknown request", + } +} + +// IsNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10000 +// - HTTP code: 404 +// - message: "Unknown request" +func IsNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10000 +} + +// NewServerError returns a new CloudFoundryError +// that IsServerError will return true for +func NewServerError() CloudFoundryError { + return CloudFoundryError{ + Code: 10001, + ErrorCode: "CF-ServerError", + Description: "Server error", + } +} + +// IsServerError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10001 +// - HTTP code: 500 +// - message: "Server error" +func IsServerError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10001 +} + +// NewNotAuthenticatedError returns a new CloudFoundryError +// that IsNotAuthenticatedError will return true for +func NewNotAuthenticatedError() CloudFoundryError { + return CloudFoundryError{ + Code: 10002, + ErrorCode: "CF-NotAuthenticated", + Description: "Authentication error", + } +} + +// IsNotAuthenticatedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10002 +// - HTTP code: 401 +// - message: "Authentication error" +func IsNotAuthenticatedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10002 +} + +// NewNotAuthorizedError returns a new CloudFoundryError +// that IsNotAuthorizedError will return true for +func NewNotAuthorizedError() CloudFoundryError { + return CloudFoundryError{ + Code: 10003, + ErrorCode: "CF-NotAuthorized", + Description: "You are not authorized to perform the requested action", + } +} + +// IsNotAuthorizedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10003 +// - HTTP code: 403 +// - message: "You are not authorized to perform the requested action" +func IsNotAuthorizedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10003 +} + +// NewInvalidRequestError returns a new CloudFoundryError +// that IsInvalidRequestError will return true for +func NewInvalidRequestError() CloudFoundryError { + return CloudFoundryError{ + Code: 10004, + ErrorCode: "CF-InvalidRequest", + Description: "The request is invalid", + } +} + +// IsInvalidRequestError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10004 +// - HTTP code: 400 +// - message: "The request is invalid" +func IsInvalidRequestError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10004 +} + +// NewBadQueryParameterError returns a new CloudFoundryError +// that IsBadQueryParameterError will return true for +func NewBadQueryParameterError() CloudFoundryError { + return CloudFoundryError{ + Code: 10005, + ErrorCode: "CF-BadQueryParameter", + Description: "The query parameter is invalid: %s", + } +} + +// IsBadQueryParameterError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10005 +// - HTTP code: 400 +// - message: "The query parameter is invalid: %s" +func IsBadQueryParameterError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10005 +} + +// NewAssociationNotEmptyError returns a new CloudFoundryError +// that IsAssociationNotEmptyError will return true for +func NewAssociationNotEmptyError() CloudFoundryError { + return CloudFoundryError{ + Code: 10006, + ErrorCode: "CF-AssociationNotEmpty", + Description: "Please delete the %s associations for your %s.", + } +} + +// IsAssociationNotEmptyError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10006 +// - HTTP code: 400 +// - message: "Please delete the %s associations for your %s." +func IsAssociationNotEmptyError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10006 +} + +// NewInsufficientScopeError returns a new CloudFoundryError +// that IsInsufficientScopeError will return true for +func NewInsufficientScopeError() CloudFoundryError { + return CloudFoundryError{ + Code: 10007, + ErrorCode: "CF-InsufficientScope", + Description: "Your token lacks the necessary scopes to access this resource.", + } +} + +// IsInsufficientScopeError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10007 +// - HTTP code: 403 +// - message: "Your token lacks the necessary scopes to access this resource." +func IsInsufficientScopeError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10007 +} + +// NewUnprocessableEntityError returns a new CloudFoundryError +// that IsUnprocessableEntityError will return true for +func NewUnprocessableEntityError() CloudFoundryError { + return CloudFoundryError{ + Code: 10008, + ErrorCode: "CF-UnprocessableEntity", + Description: "%s", + } +} + +// IsUnprocessableEntityError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10008 +// - HTTP code: 422 +// - message: "%s" +func IsUnprocessableEntityError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10008 +} + +// NewUnableToPerformError returns a new CloudFoundryError +// that IsUnableToPerformError will return true for +func NewUnableToPerformError() CloudFoundryError { + return CloudFoundryError{ + Code: 10009, + ErrorCode: "CF-UnableToPerform", + Description: "%s could not be completed: %s", + } +} + +// IsUnableToPerformError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10009 +// - HTTP code: 400 +// - message: "%s could not be completed: %s" +func IsUnableToPerformError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10009 +} + +// NewResourceNotFoundError returns a new CloudFoundryError +// that IsResourceNotFoundError will return true for +func NewResourceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 10010, + ErrorCode: "CF-ResourceNotFound", + Description: "%s", + } +} + +// IsResourceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10010 +// - HTTP code: 404 +// - message: "%s" +func IsResourceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10010 +} + +// NewDatabaseError returns a new CloudFoundryError +// that IsDatabaseError will return true for +func NewDatabaseError() CloudFoundryError { + return CloudFoundryError{ + Code: 10011, + ErrorCode: "CF-DatabaseError", + Description: "Database error", + } +} + +// IsDatabaseError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10011 +// - HTTP code: 500 +// - message: "Database error" +func IsDatabaseError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10011 +} + +// NewOrderByParameterInvalidError returns a new CloudFoundryError +// that IsOrderByParameterInvalidError will return true for +func NewOrderByParameterInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 10012, + ErrorCode: "CF-OrderByParameterInvalid", + Description: "Cannot order by: %s", + } +} + +// IsOrderByParameterInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10012 +// - HTTP code: 500 +// - message: "Cannot order by: %s" +func IsOrderByParameterInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10012 +} + +// NewRateLimitExceededError returns a new CloudFoundryError +// that IsRateLimitExceededError will return true for +func NewRateLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 10013, + ErrorCode: "CF-RateLimitExceeded", + Description: "Rate Limit Exceeded", + } +} + +// IsRateLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10013 +// - HTTP code: 429 +// - message: "Rate Limit Exceeded" +func IsRateLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10013 +} + +// NewIPBasedRateLimitExceededError returns a new CloudFoundryError +// that IsIPBasedRateLimitExceededError will return true for +func NewIPBasedRateLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 10014, + ErrorCode: "CF-IPBasedRateLimitExceeded", + Description: "Rate Limit Exceeded: Unauthenticated requests from this IP address have exceeded the limit. Please log in.", + } +} + +// IsIPBasedRateLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10014 +// - HTTP code: 429 +// - message: "Rate Limit Exceeded: Unauthenticated requests from this IP address have exceeded the limit. Please log in." +func IsIPBasedRateLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10014 +} + +// NewServiceUnavailableError returns a new CloudFoundryError +// that IsServiceUnavailableError will return true for +func NewServiceUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 10015, + ErrorCode: "CF-ServiceUnavailable", + Description: "%s", + } +} + +// IsServiceUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10015 +// - HTTP code: 503 +// - message: "%s" +func IsServiceUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10015 +} + +// NewServiceBrokerRateLimitExceededError returns a new CloudFoundryError +// that IsServiceBrokerRateLimitExceededError will return true for +func NewServiceBrokerRateLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 10016, + ErrorCode: "CF-ServiceBrokerRateLimitExceeded", + Description: "Service broker concurrent request limit exceeded", + } +} + +// IsServiceBrokerRateLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 10016 +// - HTTP code: 429 +// - message: "Service broker concurrent request limit exceeded" +func IsServiceBrokerRateLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 10016 +} + +// NewUserInvalidError returns a new CloudFoundryError +// that IsUserInvalidError will return true for +func NewUserInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 20001, + ErrorCode: "CF-UserInvalid", + Description: "The user info is invalid: %s", + } +} + +// IsUserInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20001 +// - HTTP code: 400 +// - message: "The user info is invalid: %s" +func IsUserInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20001 +} + +// NewUaaIdTakenError returns a new CloudFoundryError +// that IsUaaIdTakenError will return true for +func NewUaaIdTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 20002, + ErrorCode: "CF-UaaIdTaken", + Description: "The UAA ID is taken: %s", + } +} + +// IsUaaIdTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20002 +// - HTTP code: 400 +// - message: "The UAA ID is taken: %s" +func IsUaaIdTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20002 +} + +// NewUserNotFoundError returns a new CloudFoundryError +// that IsUserNotFoundError will return true for +func NewUserNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 20003, + ErrorCode: "CF-UserNotFound", + Description: "The user could not be found: %s", + } +} + +// IsUserNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20003 +// - HTTP code: 404 +// - message: "The user could not be found: %s" +func IsUserNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20003 +} + +// NewUaaUnavailableError returns a new CloudFoundryError +// that IsUaaUnavailableError will return true for +func NewUaaUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 20004, + ErrorCode: "CF-UaaUnavailable", + Description: "The UAA service is currently unavailable", + } +} + +// IsUaaUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20004 +// - HTTP code: 503 +// - message: "The UAA service is currently unavailable" +func IsUaaUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20004 +} + +// NewUaaEndpointDisabledError returns a new CloudFoundryError +// that IsUaaEndpointDisabledError will return true for +func NewUaaEndpointDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 20005, + ErrorCode: "CF-UaaEndpointDisabled", + Description: "The UAA endpoint needed is disabled", + } +} + +// IsUaaEndpointDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20005 +// - HTTP code: 501 +// - message: "The UAA endpoint needed is disabled" +func IsUaaEndpointDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20005 +} + +// NewUserIsInMultipleOriginsError returns a new CloudFoundryError +// that IsUserIsInMultipleOriginsError will return true for +func NewUserIsInMultipleOriginsError() CloudFoundryError { + return CloudFoundryError{ + Code: 20006, + ErrorCode: "CF-UserIsInMultipleOrigins", + Description: "The user exists in multiple origins. Specify an origin for the requested user from: %s", + } +} + +// IsUserIsInMultipleOriginsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20006 +// - HTTP code: 400 +// - message: "The user exists in multiple origins. Specify an origin for the requested user from: %s" +func IsUserIsInMultipleOriginsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20006 +} + +// NewUserWithOriginNotFoundError returns a new CloudFoundryError +// that IsUserWithOriginNotFoundError will return true for +func NewUserWithOriginNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 20007, + ErrorCode: "CF-UserWithOriginNotFound", + Description: "The user could not be found, %s", + } +} + +// IsUserWithOriginNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 20007 +// - HTTP code: 404 +// - message: "The user could not be found, %s" +func IsUserWithOriginNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 20007 +} + +// NewOutOfRouterGroupPortsError returns a new CloudFoundryError +// that IsOutOfRouterGroupPortsError will return true for +func NewOutOfRouterGroupPortsError() CloudFoundryError { + return CloudFoundryError{ + Code: 21008, + ErrorCode: "CF-OutOfRouterGroupPorts", + Description: "There are no more ports available for router group: %s. Please contact your administrator for more information.", + } +} + +// IsOutOfRouterGroupPortsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 21008 +// - HTTP code: 403 +// - message: "There are no more ports available for router group: %s. Please contact your administrator for more information." +func IsOutOfRouterGroupPortsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 21008 +} + +// NewOrganizationInvalidError returns a new CloudFoundryError +// that IsOrganizationInvalidError will return true for +func NewOrganizationInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 30001, + ErrorCode: "CF-OrganizationInvalid", + Description: "The organization info is invalid: %s", + } +} + +// IsOrganizationInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30001 +// - HTTP code: 400 +// - message: "The organization info is invalid: %s" +func IsOrganizationInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30001 +} + +// NewOrganizationNameTakenError returns a new CloudFoundryError +// that IsOrganizationNameTakenError will return true for +func NewOrganizationNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 30002, + ErrorCode: "CF-OrganizationNameTaken", + Description: "The organization name is taken: %s", + } +} + +// IsOrganizationNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30002 +// - HTTP code: 400 +// - message: "The organization name is taken: %s" +func IsOrganizationNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30002 +} + +// NewOrganizationNotFoundError returns a new CloudFoundryError +// that IsOrganizationNotFoundError will return true for +func NewOrganizationNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 30003, + ErrorCode: "CF-OrganizationNotFound", + Description: "The organization could not be found: %s", + } +} + +// IsOrganizationNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30003 +// - HTTP code: 404 +// - message: "The organization could not be found: %s" +func IsOrganizationNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30003 +} + +// NewLastManagerInOrgError returns a new CloudFoundryError +// that IsLastManagerInOrgError will return true for +func NewLastManagerInOrgError() CloudFoundryError { + return CloudFoundryError{ + Code: 30004, + ErrorCode: "CF-LastManagerInOrg", + Description: "Cannot remove last Org Manager in org", + } +} + +// IsLastManagerInOrgError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30004 +// - HTTP code: 403 +// - message: "Cannot remove last Org Manager in org" +func IsLastManagerInOrgError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30004 +} + +// NewLastBillingManagerInOrgError returns a new CloudFoundryError +// that IsLastBillingManagerInOrgError will return true for +func NewLastBillingManagerInOrgError() CloudFoundryError { + return CloudFoundryError{ + Code: 30005, + ErrorCode: "CF-LastBillingManagerInOrg", + Description: "Cannot remove last Billing Manager in org", + } +} + +// IsLastBillingManagerInOrgError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30005 +// - HTTP code: 403 +// - message: "Cannot remove last Billing Manager in org" +func IsLastBillingManagerInOrgError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30005 +} + +// NewLastUserInOrgError returns a new CloudFoundryError +// that IsLastUserInOrgError will return true for +func NewLastUserInOrgError() CloudFoundryError { + return CloudFoundryError{ + Code: 30006, + ErrorCode: "CF-LastUserInOrg", + Description: "Cannot remove last User in org", + } +} + +// IsLastUserInOrgError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30006 +// - HTTP code: 403 +// - message: "Cannot remove last User in org" +func IsLastUserInOrgError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30006 +} + +// NewOrganizationAlreadySetError returns a new CloudFoundryError +// that IsOrganizationAlreadySetError will return true for +func NewOrganizationAlreadySetError() CloudFoundryError { + return CloudFoundryError{ + Code: 30007, + ErrorCode: "CF-OrganizationAlreadySet", + Description: "Cannot change organization", + } +} + +// IsOrganizationAlreadySetError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 30007 +// - HTTP code: 400 +// - message: "Cannot change organization" +func IsOrganizationAlreadySetError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 30007 +} + +// NewSpaceInvalidError returns a new CloudFoundryError +// that IsSpaceInvalidError will return true for +func NewSpaceInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 40001, + ErrorCode: "CF-SpaceInvalid", + Description: "The app space info is invalid: %s", + } +} + +// IsSpaceInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 40001 +// - HTTP code: 400 +// - message: "The app space info is invalid: %s" +func IsSpaceInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 40001 +} + +// NewSpaceNameTakenError returns a new CloudFoundryError +// that IsSpaceNameTakenError will return true for +func NewSpaceNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 40002, + ErrorCode: "CF-SpaceNameTaken", + Description: "The app space name is taken: %s", + } +} + +// IsSpaceNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 40002 +// - HTTP code: 400 +// - message: "The app space name is taken: %s" +func IsSpaceNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 40002 +} + +// NewSpaceUserNotInOrgError returns a new CloudFoundryError +// that IsSpaceUserNotInOrgError will return true for +func NewSpaceUserNotInOrgError() CloudFoundryError { + return CloudFoundryError{ + Code: 40003, + ErrorCode: "CF-SpaceUserNotInOrg", + Description: "The app space and the user are not in the same org: %s", + } +} + +// IsSpaceUserNotInOrgError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 40003 +// - HTTP code: 400 +// - message: "The app space and the user are not in the same org: %s" +func IsSpaceUserNotInOrgError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 40003 +} + +// NewSpaceNotFoundError returns a new CloudFoundryError +// that IsSpaceNotFoundError will return true for +func NewSpaceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 40004, + ErrorCode: "CF-SpaceNotFound", + Description: "The app space could not be found: %s", + } +} + +// IsSpaceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 40004 +// - HTTP code: 404 +// - message: "The app space could not be found: %s" +func IsSpaceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 40004 +} + +// NewServiceInstanceNameEmptyError returns a new CloudFoundryError +// that IsServiceInstanceNameEmptyError will return true for +func NewServiceInstanceNameEmptyError() CloudFoundryError { + return CloudFoundryError{ + Code: 60001, + ErrorCode: "CF-ServiceInstanceNameEmpty", + Description: "Service instance name is required.", + } +} + +// IsServiceInstanceNameEmptyError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60001 +// - HTTP code: 400 +// - message: "Service instance name is required." +func IsServiceInstanceNameEmptyError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60001 +} + +// NewServiceInstanceNameTakenError returns a new CloudFoundryError +// that IsServiceInstanceNameTakenError will return true for +func NewServiceInstanceNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 60002, + ErrorCode: "CF-ServiceInstanceNameTaken", + Description: "The service instance name is taken: %s", + } +} + +// IsServiceInstanceNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60002 +// - HTTP code: 400 +// - message: "The service instance name is taken: %s" +func IsServiceInstanceNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60002 +} + +// NewServiceInstanceInvalidError returns a new CloudFoundryError +// that IsServiceInstanceInvalidError will return true for +func NewServiceInstanceInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 60003, + ErrorCode: "CF-ServiceInstanceInvalid", + Description: "The service instance is invalid: %s", + } +} + +// IsServiceInstanceInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60003 +// - HTTP code: 400 +// - message: "The service instance is invalid: %s" +func IsServiceInstanceInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60003 +} + +// NewServiceInstanceNotFoundError returns a new CloudFoundryError +// that IsServiceInstanceNotFoundError will return true for +func NewServiceInstanceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 60004, + ErrorCode: "CF-ServiceInstanceNotFound", + Description: "The service instance could not be found: %s", + } +} + +// IsServiceInstanceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60004 +// - HTTP code: 404 +// - message: "The service instance could not be found: %s" +func IsServiceInstanceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60004 +} + +// NewServiceInstanceQuotaExceededError returns a new CloudFoundryError +// that IsServiceInstanceQuotaExceededError will return true for +func NewServiceInstanceQuotaExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 60005, + ErrorCode: "CF-ServiceInstanceQuotaExceeded", + Description: "You have exceeded your organization's services limit.", + } +} + +// IsServiceInstanceQuotaExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60005 +// - HTTP code: 400 +// - message: "You have exceeded your organization's services limit." +func IsServiceInstanceQuotaExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60005 +} + +// NewPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError returns a new CloudFoundryError +// that IsPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError will return true for +func NewPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 60006, + ErrorCode: "CF-PreviouslyUsedAs_ServiceInstancePaidQuotaExceeded", + Description: "You have exceeded your organization's services limit.", + } +} + +// IsPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60006 +// - HTTP code: 400 +// - message: "You have exceeded your organization's services limit." +func IsPreviouslyUsedAs_ServiceInstancePaidQuotaExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60006 +} + +// NewServiceInstanceServicePlanNotAllowedError returns a new CloudFoundryError +// that IsServiceInstanceServicePlanNotAllowedError will return true for +func NewServiceInstanceServicePlanNotAllowedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60007, + ErrorCode: "CF-ServiceInstanceServicePlanNotAllowed", + Description: "The service instance cannot be created because paid service plans are not allowed.", + } +} + +// IsServiceInstanceServicePlanNotAllowedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60007 +// - HTTP code: 400 +// - message: "The service instance cannot be created because paid service plans are not allowed." +func IsServiceInstanceServicePlanNotAllowedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60007 +} + +// NewServiceInstanceDuplicateNotAllowedError returns a new CloudFoundryError +// that IsServiceInstanceDuplicateNotAllowedError will return true for +func NewServiceInstanceDuplicateNotAllowedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60008, + ErrorCode: "CF-ServiceInstanceDuplicateNotAllowed", + Description: "An instance of this service is already present in this space. Some services only support one instance per space.", + } +} + +// IsServiceInstanceDuplicateNotAllowedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60008 +// - HTTP code: 400 +// - message: "An instance of this service is already present in this space. Some services only support one instance per space." +func IsServiceInstanceDuplicateNotAllowedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60008 +} + +// NewServiceInstanceNameTooLongError returns a new CloudFoundryError +// that IsServiceInstanceNameTooLongError will return true for +func NewServiceInstanceNameTooLongError() CloudFoundryError { + return CloudFoundryError{ + Code: 60009, + ErrorCode: "CF-ServiceInstanceNameTooLong", + Description: "You have requested an invalid service instance name. Names are limited to 255 characters.", + } +} + +// IsServiceInstanceNameTooLongError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60009 +// - HTTP code: 400 +// - message: "You have requested an invalid service instance name. Names are limited to 255 characters." +func IsServiceInstanceNameTooLongError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60009 +} + +// NewServiceInstanceOrganizationNotAuthorizedError returns a new CloudFoundryError +// that IsServiceInstanceOrganizationNotAuthorizedError will return true for +func NewServiceInstanceOrganizationNotAuthorizedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60010, + ErrorCode: "CF-ServiceInstanceOrganizationNotAuthorized", + Description: "A service instance for the selected plan cannot be created in this organization. The plan is visible because another organization you belong to has access to it.", + } +} + +// IsServiceInstanceOrganizationNotAuthorizedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60010 +// - HTTP code: 403 +// - message: "A service instance for the selected plan cannot be created in this organization. The plan is visible because another organization you belong to has access to it." +func IsServiceInstanceOrganizationNotAuthorizedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60010 +} + +// NewServiceInstanceDeprovisionFailedError returns a new CloudFoundryError +// that IsServiceInstanceDeprovisionFailedError will return true for +func NewServiceInstanceDeprovisionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60011, + ErrorCode: "CF-ServiceInstanceDeprovisionFailed", + Description: "The service broker reported an error during deprovisioning: %s", + } +} + +// IsServiceInstanceDeprovisionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60011 +// - HTTP code: 409 +// - message: "The service broker reported an error during deprovisioning: %s" +func IsServiceInstanceDeprovisionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60011 +} + +// NewServiceInstanceSpaceQuotaExceededError returns a new CloudFoundryError +// that IsServiceInstanceSpaceQuotaExceededError will return true for +func NewServiceInstanceSpaceQuotaExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 60012, + ErrorCode: "CF-ServiceInstanceSpaceQuotaExceeded", + Description: "You have exceeded your space's services limit.", + } +} + +// IsServiceInstanceSpaceQuotaExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60012 +// - HTTP code: 400 +// - message: "You have exceeded your space's services limit." +func IsServiceInstanceSpaceQuotaExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60012 +} + +// NewServiceInstanceServicePlanNotAllowedBySpaceQuotaError returns a new CloudFoundryError +// that IsServiceInstanceServicePlanNotAllowedBySpaceQuotaError will return true for +func NewServiceInstanceServicePlanNotAllowedBySpaceQuotaError() CloudFoundryError { + return CloudFoundryError{ + Code: 60013, + ErrorCode: "CF-ServiceInstanceServicePlanNotAllowedBySpaceQuota", + Description: "The service instance cannot be created because paid service plans are not allowed for your space.", + } +} + +// IsServiceInstanceServicePlanNotAllowedBySpaceQuotaError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60013 +// - HTTP code: 400 +// - message: "The service instance cannot be created because paid service plans are not allowed for your space." +func IsServiceInstanceServicePlanNotAllowedBySpaceQuotaError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60013 +} + +// NewServiceInstanceSpaceChangeNotAllowedError returns a new CloudFoundryError +// that IsServiceInstanceSpaceChangeNotAllowedError will return true for +func NewServiceInstanceSpaceChangeNotAllowedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60014, + ErrorCode: "CF-ServiceInstanceSpaceChangeNotAllowed", + Description: "Cannot update space for service instance.", + } +} + +// IsServiceInstanceSpaceChangeNotAllowedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60014 +// - HTTP code: 400 +// - message: "Cannot update space for service instance." +func IsServiceInstanceSpaceChangeNotAllowedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60014 +} + +// NewServiceInstanceTagsTooLongError returns a new CloudFoundryError +// that IsServiceInstanceTagsTooLongError will return true for +func NewServiceInstanceTagsTooLongError() CloudFoundryError { + return CloudFoundryError{ + Code: 60015, + ErrorCode: "CF-ServiceInstanceTagsTooLong", + Description: "Combined length of tags for service %s must be 2048 characters or less.", + } +} + +// IsServiceInstanceTagsTooLongError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60015 +// - HTTP code: 400 +// - message: "Combined length of tags for service %s must be 2048 characters or less." +func IsServiceInstanceTagsTooLongError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60015 +} + +// NewAsyncServiceInstanceOperationInProgressError returns a new CloudFoundryError +// that IsAsyncServiceInstanceOperationInProgressError will return true for +func NewAsyncServiceInstanceOperationInProgressError() CloudFoundryError { + return CloudFoundryError{ + Code: 60016, + ErrorCode: "CF-AsyncServiceInstanceOperationInProgress", + Description: "An operation for service instance %s is in progress.", + } +} + +// IsAsyncServiceInstanceOperationInProgressError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60016 +// - HTTP code: 409 +// - message: "An operation for service instance %s is in progress." +func IsAsyncServiceInstanceOperationInProgressError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60016 +} + +// NewServiceInstanceRouteBindingSpaceMismatchError returns a new CloudFoundryError +// that IsServiceInstanceRouteBindingSpaceMismatchError will return true for +func NewServiceInstanceRouteBindingSpaceMismatchError() CloudFoundryError { + return CloudFoundryError{ + Code: 60017, + ErrorCode: "CF-ServiceInstanceRouteBindingSpaceMismatch", + Description: "The service instance and the route are in different spaces.", + } +} + +// IsServiceInstanceRouteBindingSpaceMismatchError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60017 +// - HTTP code: 400 +// - message: "The service instance and the route are in different spaces." +func IsServiceInstanceRouteBindingSpaceMismatchError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60017 +} + +// NewServiceInstanceSpaceNotAuthorizedError returns a new CloudFoundryError +// that IsServiceInstanceSpaceNotAuthorizedError will return true for +func NewServiceInstanceSpaceNotAuthorizedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60018, + ErrorCode: "CF-ServiceInstanceSpaceNotAuthorized", + Description: "A service instance for the selected plan cannot be created in this space.", + } +} + +// IsServiceInstanceSpaceNotAuthorizedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60018 +// - HTTP code: 403 +// - message: "A service instance for the selected plan cannot be created in this space." +func IsServiceInstanceSpaceNotAuthorizedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60018 +} + +// NewServiceInstanceRouteServiceURLInvalidError returns a new CloudFoundryError +// that IsServiceInstanceRouteServiceURLInvalidError will return true for +func NewServiceInstanceRouteServiceURLInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 60019, + ErrorCode: "CF-ServiceInstanceRouteServiceURLInvalid", + Description: "The route service URL is invalid: %s", + } +} + +// IsServiceInstanceRouteServiceURLInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60019 +// - HTTP code: 400 +// - message: "The route service URL is invalid: %s" +func IsServiceInstanceRouteServiceURLInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60019 +} + +// NewServiceInstanceRouteServiceRequiresDiegoError returns a new CloudFoundryError +// that IsServiceInstanceRouteServiceRequiresDiegoError will return true for +func NewServiceInstanceRouteServiceRequiresDiegoError() CloudFoundryError { + return CloudFoundryError{ + Code: 60020, + ErrorCode: "CF-ServiceInstanceRouteServiceRequiresDiego", + Description: "Route services are only supported for apps on Diego. Unbind the service instance from the route or enable Diego for the app.", + } +} + +// IsServiceInstanceRouteServiceRequiresDiegoError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60020 +// - HTTP code: 400 +// - message: "Route services are only supported for apps on Diego. Unbind the service instance from the route or enable Diego for the app." +func IsServiceInstanceRouteServiceRequiresDiegoError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60020 +} + +// NewServiceInstanceRouteServiceDisabledError returns a new CloudFoundryError +// that IsServiceInstanceRouteServiceDisabledError will return true for +func NewServiceInstanceRouteServiceDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 60021, + ErrorCode: "CF-ServiceInstanceRouteServiceDisabled", + Description: "Support for route services is disabled", + } +} + +// IsServiceInstanceRouteServiceDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60021 +// - HTTP code: 403 +// - message: "Support for route services is disabled" +func IsServiceInstanceRouteServiceDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60021 +} + +// NewAppPortMappingRequiresDiegoError returns a new CloudFoundryError +// that IsAppPortMappingRequiresDiegoError will return true for +func NewAppPortMappingRequiresDiegoError() CloudFoundryError { + return CloudFoundryError{ + Code: 60022, + ErrorCode: "CF-AppPortMappingRequiresDiego", + Description: "App ports are supported for Diego apps only.", + } +} + +// IsAppPortMappingRequiresDiegoError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60022 +// - HTTP code: 400 +// - message: "App ports are supported for Diego apps only." +func IsAppPortMappingRequiresDiegoError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60022 +} + +// NewRoutePortNotEnabledOnAppError returns a new CloudFoundryError +// that IsRoutePortNotEnabledOnAppError will return true for +func NewRoutePortNotEnabledOnAppError() CloudFoundryError { + return CloudFoundryError{ + Code: 60023, + ErrorCode: "CF-RoutePortNotEnabledOnApp", + Description: "Routes can only be mapped to ports already enabled for the application.", + } +} + +// IsRoutePortNotEnabledOnAppError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60023 +// - HTTP code: 400 +// - message: "Routes can only be mapped to ports already enabled for the application." +func IsRoutePortNotEnabledOnAppError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60023 +} + +// NewMultipleAppPortsMappedDiegoToDeaError returns a new CloudFoundryError +// that IsMultipleAppPortsMappedDiegoToDeaError will return true for +func NewMultipleAppPortsMappedDiegoToDeaError() CloudFoundryError { + return CloudFoundryError{ + Code: 60024, + ErrorCode: "CF-MultipleAppPortsMappedDiegoToDea", + Description: "The app has routes mapped to multiple ports. Multiple ports are supported for Diego only. Please unmap routes from all but one app port. Multiple routes can be mapped to the same port if desired.", + } +} + +// IsMultipleAppPortsMappedDiegoToDeaError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60024 +// - HTTP code: 400 +// - message: "The app has routes mapped to multiple ports. Multiple ports are supported for Diego only. Please unmap routes from all but one app port. Multiple routes can be mapped to the same port if desired." +func IsMultipleAppPortsMappedDiegoToDeaError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60024 +} + +// NewVolumeMountServiceDisabledError returns a new CloudFoundryError +// that IsVolumeMountServiceDisabledError will return true for +func NewVolumeMountServiceDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 60025, + ErrorCode: "CF-VolumeMountServiceDisabled", + Description: "Support for volume mount services is disabled", + } +} + +// IsVolumeMountServiceDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60025 +// - HTTP code: 403 +// - message: "Support for volume mount services is disabled" +func IsVolumeMountServiceDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60025 +} + +// NewDockerAppToDeaError returns a new CloudFoundryError +// that IsDockerAppToDeaError will return true for +func NewDockerAppToDeaError() CloudFoundryError { + return CloudFoundryError{ + Code: 60026, + ErrorCode: "CF-DockerAppToDea", + Description: "Docker apps cannot run on DEAs", + } +} + +// IsDockerAppToDeaError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60026 +// - HTTP code: 400 +// - message: "Docker apps cannot run on DEAs" +func IsDockerAppToDeaError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60026 +} + +// NewServiceInstanceRecursiveDeleteFailedError returns a new CloudFoundryError +// that IsServiceInstanceRecursiveDeleteFailedError will return true for +func NewServiceInstanceRecursiveDeleteFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60027, + ErrorCode: "CF-ServiceInstanceRecursiveDeleteFailed", + Description: "Deletion of service instance %s failed because one or more associated resources could not be deleted.%s", + } +} + +// IsServiceInstanceRecursiveDeleteFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60027 +// - HTTP code: 502 +// - message: "Deletion of service instance %s failed because one or more associated resources could not be deleted.\n\n%s" +func IsServiceInstanceRecursiveDeleteFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60027 +} + +// NewManagedServiceInstanceNotFoundError returns a new CloudFoundryError +// that IsManagedServiceInstanceNotFoundError will return true for +func NewManagedServiceInstanceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 60028, + ErrorCode: "CF-ManagedServiceInstanceNotFound", + Description: "The service instance could not be found: %s", + } +} + +// IsManagedServiceInstanceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60028 +// - HTTP code: 404 +// - message: "The service instance could not be found: %s" +func IsManagedServiceInstanceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60028 +} + +// NewServiceInstanceWithInaccessiblePlanNotUpdateableError returns a new CloudFoundryError +// that IsServiceInstanceWithInaccessiblePlanNotUpdateableError will return true for +func NewServiceInstanceWithInaccessiblePlanNotUpdateableError() CloudFoundryError { + return CloudFoundryError{ + Code: 60029, + ErrorCode: "CF-ServiceInstanceWithInaccessiblePlanNotUpdateable", + Description: "Cannot update %s of a service instance that belongs to inaccessible plan", + } +} + +// IsServiceInstanceWithInaccessiblePlanNotUpdateableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60029 +// - HTTP code: 403 +// - message: "Cannot update %s of a service instance that belongs to inaccessible plan" +func IsServiceInstanceWithInaccessiblePlanNotUpdateableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60029 +} + +// NewServiceInstanceProvisionFailedError returns a new CloudFoundryError +// that IsServiceInstanceProvisionFailedError will return true for +func NewServiceInstanceProvisionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 60030, + ErrorCode: "CF-ServiceInstanceProvisionFailed", + Description: "The service broker reported an error during provisioning: %s", + } +} + +// IsServiceInstanceProvisionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 60030 +// - HTTP code: 400 +// - message: "The service broker reported an error during provisioning: %s" +func IsServiceInstanceProvisionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 60030 +} + +// NewRuntimeInvalidError returns a new CloudFoundryError +// that IsRuntimeInvalidError will return true for +func NewRuntimeInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 70001, + ErrorCode: "CF-RuntimeInvalid", + Description: "The runtime is invalid: %s", + } +} + +// IsRuntimeInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 70001 +// - HTTP code: 400 +// - message: "The runtime is invalid: %s" +func IsRuntimeInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 70001 +} + +// NewRuntimeNameTakenError returns a new CloudFoundryError +// that IsRuntimeNameTakenError will return true for +func NewRuntimeNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 70002, + ErrorCode: "CF-RuntimeNameTaken", + Description: "The runtime name is taken: %s", + } +} + +// IsRuntimeNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 70002 +// - HTTP code: 400 +// - message: "The runtime name is taken: %s" +func IsRuntimeNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 70002 +} + +// NewRuntimeNotFoundError returns a new CloudFoundryError +// that IsRuntimeNotFoundError will return true for +func NewRuntimeNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 70003, + ErrorCode: "CF-RuntimeNotFound", + Description: "The runtime could not be found: %s", + } +} + +// IsRuntimeNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 70003 +// - HTTP code: 404 +// - message: "The runtime could not be found: %s" +func IsRuntimeNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 70003 +} + +// NewFrameworkInvalidError returns a new CloudFoundryError +// that IsFrameworkInvalidError will return true for +func NewFrameworkInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 80001, + ErrorCode: "CF-FrameworkInvalid", + Description: "The framework is invalid: %s", + } +} + +// IsFrameworkInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 80001 +// - HTTP code: 400 +// - message: "The framework is invalid: %s" +func IsFrameworkInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 80001 +} + +// NewFrameworkNameTakenError returns a new CloudFoundryError +// that IsFrameworkNameTakenError will return true for +func NewFrameworkNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 80002, + ErrorCode: "CF-FrameworkNameTaken", + Description: "The framework name is taken: %s", + } +} + +// IsFrameworkNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 80002 +// - HTTP code: 400 +// - message: "The framework name is taken: %s" +func IsFrameworkNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 80002 +} + +// NewFrameworkNotFoundError returns a new CloudFoundryError +// that IsFrameworkNotFoundError will return true for +func NewFrameworkNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 80003, + ErrorCode: "CF-FrameworkNotFound", + Description: "The framework could not be found: %s", + } +} + +// IsFrameworkNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 80003 +// - HTTP code: 404 +// - message: "The framework could not be found: %s" +func IsFrameworkNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 80003 +} + +// NewServiceBindingInvalidError returns a new CloudFoundryError +// that IsServiceBindingInvalidError will return true for +func NewServiceBindingInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 90001, + ErrorCode: "CF-ServiceBindingInvalid", + Description: "The service binding is invalid: %s", + } +} + +// IsServiceBindingInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90001 +// - HTTP code: 400 +// - message: "The service binding is invalid: %s" +func IsServiceBindingInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90001 +} + +// NewServiceBindingDifferentSpacesError returns a new CloudFoundryError +// that IsServiceBindingDifferentSpacesError will return true for +func NewServiceBindingDifferentSpacesError() CloudFoundryError { + return CloudFoundryError{ + Code: 90002, + ErrorCode: "CF-ServiceBindingDifferentSpaces", + Description: "The app and the service are not in the same app space: %s", + } +} + +// IsServiceBindingDifferentSpacesError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90002 +// - HTTP code: 400 +// - message: "The app and the service are not in the same app space: %s" +func IsServiceBindingDifferentSpacesError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90002 +} + +// NewServiceBindingAppServiceTakenError returns a new CloudFoundryError +// that IsServiceBindingAppServiceTakenError will return true for +func NewServiceBindingAppServiceTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 90003, + ErrorCode: "CF-ServiceBindingAppServiceTaken", + Description: "%s", + } +} + +// IsServiceBindingAppServiceTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90003 +// - HTTP code: 400 +// - message: "%s" +func IsServiceBindingAppServiceTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90003 +} + +// NewServiceBindingNotFoundError returns a new CloudFoundryError +// that IsServiceBindingNotFoundError will return true for +func NewServiceBindingNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 90004, + ErrorCode: "CF-ServiceBindingNotFound", + Description: "The service binding could not be found: %s", + } +} + +// IsServiceBindingNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90004 +// - HTTP code: 404 +// - message: "The service binding could not be found: %s" +func IsServiceBindingNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90004 +} + +// NewUnbindableServiceError returns a new CloudFoundryError +// that IsUnbindableServiceError will return true for +func NewUnbindableServiceError() CloudFoundryError { + return CloudFoundryError{ + Code: 90005, + ErrorCode: "CF-UnbindableService", + Description: "The service instance doesn't support binding.", + } +} + +// IsUnbindableServiceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90005 +// - HTTP code: 400 +// - message: "The service instance doesn't support binding." +func IsUnbindableServiceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90005 +} + +// NewInvalidLoggingServiceBindingError returns a new CloudFoundryError +// that IsInvalidLoggingServiceBindingError will return true for +func NewInvalidLoggingServiceBindingError() CloudFoundryError { + return CloudFoundryError{ + Code: 90006, + ErrorCode: "CF-InvalidLoggingServiceBinding", + Description: "The service is attempting to stream logs from your application, but is not registered as a logging service. Please contact the service provider.", + } +} + +// IsInvalidLoggingServiceBindingError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90006 +// - HTTP code: 502 +// - message: "The service is attempting to stream logs from your application, but is not registered as a logging service. Please contact the service provider." +func IsInvalidLoggingServiceBindingError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90006 +} + +// NewServiceFetchBindingParametersNotSupportedError returns a new CloudFoundryError +// that IsServiceFetchBindingParametersNotSupportedError will return true for +func NewServiceFetchBindingParametersNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 90007, + ErrorCode: "CF-ServiceFetchBindingParametersNotSupported", + Description: "This service does not support fetching service binding parameters.", + } +} + +// IsServiceFetchBindingParametersNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90007 +// - HTTP code: 400 +// - message: "This service does not support fetching service binding parameters." +func IsServiceFetchBindingParametersNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90007 +} + +// NewAsyncServiceBindingOperationInProgressError returns a new CloudFoundryError +// that IsAsyncServiceBindingOperationInProgressError will return true for +func NewAsyncServiceBindingOperationInProgressError() CloudFoundryError { + return CloudFoundryError{ + Code: 90008, + ErrorCode: "CF-AsyncServiceBindingOperationInProgress", + Description: "An operation for the service binding between app %s and service instance %s is in progress.", + } +} + +// IsAsyncServiceBindingOperationInProgressError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 90008 +// - HTTP code: 409 +// - message: "An operation for the service binding between app %s and service instance %s is in progress." +func IsAsyncServiceBindingOperationInProgressError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 90008 +} + +// NewAppInvalidError returns a new CloudFoundryError +// that IsAppInvalidError will return true for +func NewAppInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 100001, + ErrorCode: "CF-AppInvalid", + Description: "The app is invalid: %s", + } +} + +// IsAppInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100001 +// - HTTP code: 400 +// - message: "The app is invalid: %s" +func IsAppInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100001 +} + +// NewAppNameTakenError returns a new CloudFoundryError +// that IsAppNameTakenError will return true for +func NewAppNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 100002, + ErrorCode: "CF-AppNameTaken", + Description: "The app name is taken: %s", + } +} + +// IsAppNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100002 +// - HTTP code: 400 +// - message: "The app name is taken: %s" +func IsAppNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100002 +} + +// NewAppNotFoundError returns a new CloudFoundryError +// that IsAppNotFoundError will return true for +func NewAppNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 100004, + ErrorCode: "CF-AppNotFound", + Description: "The app could not be found: %s", + } +} + +// IsAppNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100004 +// - HTTP code: 404 +// - message: "The app could not be found: %s" +func IsAppNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100004 +} + +// NewAppMemoryQuotaExceededError returns a new CloudFoundryError +// that IsAppMemoryQuotaExceededError will return true for +func NewAppMemoryQuotaExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 100005, + ErrorCode: "CF-AppMemoryQuotaExceeded", + Description: "You have exceeded your organization's memory limit: %s", + } +} + +// IsAppMemoryQuotaExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100005 +// - HTTP code: 400 +// - message: "You have exceeded your organization's memory limit: %s" +func IsAppMemoryQuotaExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100005 +} + +// NewAppMemoryInvalidError returns a new CloudFoundryError +// that IsAppMemoryInvalidError will return true for +func NewAppMemoryInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 100006, + ErrorCode: "CF-AppMemoryInvalid", + Description: "You have specified an invalid amount of memory for your application.", + } +} + +// IsAppMemoryInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100006 +// - HTTP code: 400 +// - message: "You have specified an invalid amount of memory for your application." +func IsAppMemoryInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100006 +} + +// NewQuotaInstanceMemoryLimitExceededError returns a new CloudFoundryError +// that IsQuotaInstanceMemoryLimitExceededError will return true for +func NewQuotaInstanceMemoryLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 100007, + ErrorCode: "CF-QuotaInstanceMemoryLimitExceeded", + Description: "You have exceeded the instance memory limit for your organization's quota.", + } +} + +// IsQuotaInstanceMemoryLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100007 +// - HTTP code: 400 +// - message: "You have exceeded the instance memory limit for your organization's quota." +func IsQuotaInstanceMemoryLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100007 +} + +// NewQuotaInstanceLimitExceededError returns a new CloudFoundryError +// that IsQuotaInstanceLimitExceededError will return true for +func NewQuotaInstanceLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 100008, + ErrorCode: "CF-QuotaInstanceLimitExceeded", + Description: "You have exceeded the instance limit for your organization's quota.", + } +} + +// IsQuotaInstanceLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100008 +// - HTTP code: 400 +// - message: "You have exceeded the instance limit for your organization's quota." +func IsQuotaInstanceLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100008 +} + +// NewAppMemoryInsufficientForSidecarsError returns a new CloudFoundryError +// that IsAppMemoryInsufficientForSidecarsError will return true for +func NewAppMemoryInsufficientForSidecarsError() CloudFoundryError { + return CloudFoundryError{ + Code: 100009, + ErrorCode: "CF-AppMemoryInsufficientForSidecars", + Description: "The requested memory allocation is not large enough to run all of your sidecar processes.", + } +} + +// IsAppMemoryInsufficientForSidecarsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 100009 +// - HTTP code: 400 +// - message: "The requested memory allocation is not large enough to run all of your sidecar processes." +func IsAppMemoryInsufficientForSidecarsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 100009 +} + +// NewServicePlanInvalidError returns a new CloudFoundryError +// that IsServicePlanInvalidError will return true for +func NewServicePlanInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 110001, + ErrorCode: "CF-ServicePlanInvalid", + Description: "The service plan is invalid: %s", + } +} + +// IsServicePlanInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 110001 +// - HTTP code: 400 +// - message: "The service plan is invalid: %s" +func IsServicePlanInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 110001 +} + +// NewServicePlanNameTakenError returns a new CloudFoundryError +// that IsServicePlanNameTakenError will return true for +func NewServicePlanNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 110002, + ErrorCode: "CF-ServicePlanNameTaken", + Description: "The service plan name is taken: %s", + } +} + +// IsServicePlanNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 110002 +// - HTTP code: 400 +// - message: "The service plan name is taken: %s" +func IsServicePlanNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 110002 +} + +// NewServicePlanNotFoundError returns a new CloudFoundryError +// that IsServicePlanNotFoundError will return true for +func NewServicePlanNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 110003, + ErrorCode: "CF-ServicePlanNotFound", + Description: "The service plan could not be found: %s", + } +} + +// IsServicePlanNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 110003 +// - HTTP code: 404 +// - message: "The service plan could not be found: %s" +func IsServicePlanNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 110003 +} + +// NewServicePlanNotUpdateableError returns a new CloudFoundryError +// that IsServicePlanNotUpdateableError will return true for +func NewServicePlanNotUpdateableError() CloudFoundryError { + return CloudFoundryError{ + Code: 110004, + ErrorCode: "CF-ServicePlanNotUpdateable", + Description: "The service does not support changing plans.", + } +} + +// IsServicePlanNotUpdateableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 110004 +// - HTTP code: 400 +// - message: "The service does not support changing plans." +func IsServicePlanNotUpdateableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 110004 +} + +// NewServiceInvalidError returns a new CloudFoundryError +// that IsServiceInvalidError will return true for +func NewServiceInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 120001, + ErrorCode: "CF-ServiceInvalid", + Description: "The service is invalid: %s", + } +} + +// IsServiceInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 120001 +// - HTTP code: 400 +// - message: "The service is invalid: %s" +func IsServiceInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 120001 +} + +// NewServiceLabelTakenError returns a new CloudFoundryError +// that IsServiceLabelTakenError will return true for +func NewServiceLabelTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 120002, + ErrorCode: "CF-ServiceLabelTaken", + Description: "The service label is taken: %s", + } +} + +// IsServiceLabelTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 120002 +// - HTTP code: 400 +// - message: "The service label is taken: %s" +func IsServiceLabelTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 120002 +} + +// NewServiceNotFoundError returns a new CloudFoundryError +// that IsServiceNotFoundError will return true for +func NewServiceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 120003, + ErrorCode: "CF-ServiceNotFound", + Description: "The service could not be found: %s", + } +} + +// IsServiceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 120003 +// - HTTP code: 404 +// - message: "The service could not be found: %s" +func IsServiceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 120003 +} + +// NewServiceFetchInstanceParametersNotSupportedError returns a new CloudFoundryError +// that IsServiceFetchInstanceParametersNotSupportedError will return true for +func NewServiceFetchInstanceParametersNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 120004, + ErrorCode: "CF-ServiceFetchInstanceParametersNotSupported", + Description: "This service does not support fetching service instance parameters.", + } +} + +// IsServiceFetchInstanceParametersNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 120004 +// - HTTP code: 400 +// - message: "This service does not support fetching service instance parameters." +func IsServiceFetchInstanceParametersNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 120004 +} + +// NewDomainInvalidError returns a new CloudFoundryError +// that IsDomainInvalidError will return true for +func NewDomainInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 130001, + ErrorCode: "CF-DomainInvalid", + Description: "The domain is invalid: %s", + } +} + +// IsDomainInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130001 +// - HTTP code: 400 +// - message: "The domain is invalid: %s" +func IsDomainInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130001 +} + +// NewDomainNotFoundError returns a new CloudFoundryError +// that IsDomainNotFoundError will return true for +func NewDomainNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 130002, + ErrorCode: "CF-DomainNotFound", + Description: "The domain could not be found: %s", + } +} + +// IsDomainNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130002 +// - HTTP code: 404 +// - message: "The domain could not be found: %s" +func IsDomainNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130002 +} + +// NewDomainNameTakenError returns a new CloudFoundryError +// that IsDomainNameTakenError will return true for +func NewDomainNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 130003, + ErrorCode: "CF-DomainNameTaken", + Description: "The domain name is taken: %s", + } +} + +// IsDomainNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130003 +// - HTTP code: 400 +// - message: "The domain name is taken: %s" +func IsDomainNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130003 +} + +// NewPathInvalidError returns a new CloudFoundryError +// that IsPathInvalidError will return true for +func NewPathInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 130004, + ErrorCode: "CF-PathInvalid", + Description: "The path is invalid: %s", + } +} + +// IsPathInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130004 +// - HTTP code: 400 +// - message: "The path is invalid: %s" +func IsPathInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130004 +} + +// NewTotalPrivateDomainsExceededError returns a new CloudFoundryError +// that IsTotalPrivateDomainsExceededError will return true for +func NewTotalPrivateDomainsExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 130005, + ErrorCode: "CF-TotalPrivateDomainsExceeded", + Description: "The number of private domains exceeds the quota for organization: %s", + } +} + +// IsTotalPrivateDomainsExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130005 +// - HTTP code: 400 +// - message: "The number of private domains exceeds the quota for organization: %s" +func IsTotalPrivateDomainsExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130005 +} + +// NewServiceDoesNotSupportRoutesError returns a new CloudFoundryError +// that IsServiceDoesNotSupportRoutesError will return true for +func NewServiceDoesNotSupportRoutesError() CloudFoundryError { + return CloudFoundryError{ + Code: 130006, + ErrorCode: "CF-ServiceDoesNotSupportRoutes", + Description: "This service does not support route binding.", + } +} + +// IsServiceDoesNotSupportRoutesError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130006 +// - HTTP code: 400 +// - message: "This service does not support route binding." +func IsServiceDoesNotSupportRoutesError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130006 +} + +// NewRouteAlreadyBoundToServiceInstanceError returns a new CloudFoundryError +// that IsRouteAlreadyBoundToServiceInstanceError will return true for +func NewRouteAlreadyBoundToServiceInstanceError() CloudFoundryError { + return CloudFoundryError{ + Code: 130007, + ErrorCode: "CF-RouteAlreadyBoundToServiceInstance", + Description: "A route may only be bound to a single service instance", + } +} + +// IsRouteAlreadyBoundToServiceInstanceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130007 +// - HTTP code: 400 +// - message: "A route may only be bound to a single service instance" +func IsRouteAlreadyBoundToServiceInstanceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130007 +} + +// NewServiceInstanceAlreadyBoundToSameRouteError returns a new CloudFoundryError +// that IsServiceInstanceAlreadyBoundToSameRouteError will return true for +func NewServiceInstanceAlreadyBoundToSameRouteError() CloudFoundryError { + return CloudFoundryError{ + Code: 130008, + ErrorCode: "CF-ServiceInstanceAlreadyBoundToSameRoute", + Description: "The route and service instance are already bound.", + } +} + +// IsServiceInstanceAlreadyBoundToSameRouteError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130008 +// - HTTP code: 400 +// - message: "The route and service instance are already bound." +func IsServiceInstanceAlreadyBoundToSameRouteError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130008 +} + +// NewInternalDomainCannotBeDeletedError returns a new CloudFoundryError +// that IsInternalDomainCannotBeDeletedError will return true for +func NewInternalDomainCannotBeDeletedError() CloudFoundryError { + return CloudFoundryError{ + Code: 130009, + ErrorCode: "CF-InternalDomainCannotBeDeleted", + Description: "The domain '%s' cannot be deleted. It is reserved by the platform.", + } +} + +// IsInternalDomainCannotBeDeletedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130009 +// - HTTP code: 422 +// - message: "The domain '%s' cannot be deleted. It is reserved by the platform." +func IsInternalDomainCannotBeDeletedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130009 +} + +// NewRouteServiceCannotBeBoundToInternalRouteError returns a new CloudFoundryError +// that IsRouteServiceCannotBeBoundToInternalRouteError will return true for +func NewRouteServiceCannotBeBoundToInternalRouteError() CloudFoundryError { + return CloudFoundryError{ + Code: 130010, + ErrorCode: "CF-RouteServiceCannotBeBoundToInternalRoute", + Description: "Route services cannot be bound to internal routes.", + } +} + +// IsRouteServiceCannotBeBoundToInternalRouteError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 130010 +// - HTTP code: 400 +// - message: "Route services cannot be bound to internal routes." +func IsRouteServiceCannotBeBoundToInternalRouteError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 130010 +} + +// NewLegacyApiWithoutDefaultSpaceError returns a new CloudFoundryError +// that IsLegacyApiWithoutDefaultSpaceError will return true for +func NewLegacyApiWithoutDefaultSpaceError() CloudFoundryError { + return CloudFoundryError{ + Code: 140001, + ErrorCode: "CF-LegacyApiWithoutDefaultSpace", + Description: "A legacy api call requiring a default app space was called, but no default app space is set for the user.", + } +} + +// IsLegacyApiWithoutDefaultSpaceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 140001 +// - HTTP code: 400 +// - message: "A legacy api call requiring a default app space was called, but no default app space is set for the user." +func IsLegacyApiWithoutDefaultSpaceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 140001 +} + +// NewAppPackageInvalidError returns a new CloudFoundryError +// that IsAppPackageInvalidError will return true for +func NewAppPackageInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 150001, + ErrorCode: "CF-AppPackageInvalid", + Description: "The app package is invalid: %s", + } +} + +// IsAppPackageInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150001 +// - HTTP code: 400 +// - message: "The app package is invalid: %s" +func IsAppPackageInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150001 +} + +// NewAppPackageNotFoundError returns a new CloudFoundryError +// that IsAppPackageNotFoundError will return true for +func NewAppPackageNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 150002, + ErrorCode: "CF-AppPackageNotFound", + Description: "The app package could not be found: %s", + } +} + +// IsAppPackageNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150002 +// - HTTP code: 404 +// - message: "The app package could not be found: %s" +func IsAppPackageNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150002 +} + +// NewInsufficientRunningResourcesAvailableError returns a new CloudFoundryError +// that IsInsufficientRunningResourcesAvailableError will return true for +func NewInsufficientRunningResourcesAvailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 150003, + ErrorCode: "CF-InsufficientRunningResourcesAvailable", + Description: "One or more instances could not be started because of insufficient running resources.", + } +} + +// IsInsufficientRunningResourcesAvailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150003 +// - HTTP code: 503 +// - message: "One or more instances could not be started because of insufficient running resources." +func IsInsufficientRunningResourcesAvailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150003 +} + +// NewPackageBitsAlreadyUploadedError returns a new CloudFoundryError +// that IsPackageBitsAlreadyUploadedError will return true for +func NewPackageBitsAlreadyUploadedError() CloudFoundryError { + return CloudFoundryError{ + Code: 150004, + ErrorCode: "CF-PackageBitsAlreadyUploaded", + Description: "Bits may be uploaded only once. Create a new package to upload different bits.", + } +} + +// IsPackageBitsAlreadyUploadedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150004 +// - HTTP code: 400 +// - message: "Bits may be uploaded only once. Create a new package to upload different bits." +func IsPackageBitsAlreadyUploadedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150004 +} + +// NewBlobstoreNotLocalError returns a new CloudFoundryError +// that IsBlobstoreNotLocalError will return true for +func NewBlobstoreNotLocalError() CloudFoundryError { + return CloudFoundryError{ + Code: 150005, + ErrorCode: "CF-BlobstoreNotLocal", + Description: "Downloading blobs can only be done directly to the blobstore.", + } +} + +// IsBlobstoreNotLocalError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150005 +// - HTTP code: 400 +// - message: "Downloading blobs can only be done directly to the blobstore." +func IsBlobstoreNotLocalError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150005 +} + +// NewBlobstoreUnavailableError returns a new CloudFoundryError +// that IsBlobstoreUnavailableError will return true for +func NewBlobstoreUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 150006, + ErrorCode: "CF-BlobstoreUnavailable", + Description: "Failed to perform operation due to blobstore unavailability.", + } +} + +// IsBlobstoreUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150006 +// - HTTP code: 502 +// - message: "Failed to perform operation due to blobstore unavailability." +func IsBlobstoreUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150006 +} + +// NewBlobstoreError returns a new CloudFoundryError +// that IsBlobstoreError will return true for +func NewBlobstoreError() CloudFoundryError { + return CloudFoundryError{ + Code: 150007, + ErrorCode: "CF-BlobstoreError", + Description: "Failed to perform blobstore operation after three retries.", + } +} + +// IsBlobstoreError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150007 +// - HTTP code: 500 +// - message: "Failed to perform blobstore operation after three retries." +func IsBlobstoreError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150007 +} + +// NewDockerImageMissingError returns a new CloudFoundryError +// that IsDockerImageMissingError will return true for +func NewDockerImageMissingError() CloudFoundryError { + return CloudFoundryError{ + Code: 150008, + ErrorCode: "CF-DockerImageMissing", + Description: "Docker credentials can only be supplied for apps with a 'docker_image'", + } +} + +// IsDockerImageMissingError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150008 +// - HTTP code: 400 +// - message: "Docker credentials can only be supplied for apps with a 'docker_image'" +func IsDockerImageMissingError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150008 +} + +// NewAppRecursiveDeleteFailedError returns a new CloudFoundryError +// that IsAppRecursiveDeleteFailedError will return true for +func NewAppRecursiveDeleteFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 150009, + ErrorCode: "CF-AppRecursiveDeleteFailed", + Description: "Deletion of app %s failed because one or more associated resources could not be deleted.%s", + } +} + +// IsAppRecursiveDeleteFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 150009 +// - HTTP code: 502 +// - message: "Deletion of app %s failed because one or more associated resources could not be deleted.\n\n%s" +func IsAppRecursiveDeleteFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 150009 +} + +// NewAppBitsUploadInvalidError returns a new CloudFoundryError +// that IsAppBitsUploadInvalidError will return true for +func NewAppBitsUploadInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 160001, + ErrorCode: "CF-AppBitsUploadInvalid", + Description: "The app upload is invalid: %s", + } +} + +// IsAppBitsUploadInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 160001 +// - HTTP code: 400 +// - message: "The app upload is invalid: %s" +func IsAppBitsUploadInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 160001 +} + +// NewAppBitsCopyInvalidError returns a new CloudFoundryError +// that IsAppBitsCopyInvalidError will return true for +func NewAppBitsCopyInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 160002, + ErrorCode: "CF-AppBitsCopyInvalid", + Description: "The app copy is invalid: %s", + } +} + +// IsAppBitsCopyInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 160002 +// - HTTP code: 400 +// - message: "The app copy is invalid: %s" +func IsAppBitsCopyInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 160002 +} + +// NewAppResourcesFileModeInvalidError returns a new CloudFoundryError +// that IsAppResourcesFileModeInvalidError will return true for +func NewAppResourcesFileModeInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 160003, + ErrorCode: "CF-AppResourcesFileModeInvalid", + Description: "The resource file mode is invalid: %s", + } +} + +// IsAppResourcesFileModeInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 160003 +// - HTTP code: 400 +// - message: "The resource file mode is invalid: %s" +func IsAppResourcesFileModeInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 160003 +} + +// NewAppResourcesFilePathInvalidError returns a new CloudFoundryError +// that IsAppResourcesFilePathInvalidError will return true for +func NewAppResourcesFilePathInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 160004, + ErrorCode: "CF-AppResourcesFilePathInvalid", + Description: "The resource file path is invalid: %s", + } +} + +// IsAppResourcesFilePathInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 160004 +// - HTTP code: 400 +// - message: "The resource file path is invalid: %s" +func IsAppResourcesFilePathInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 160004 +} + +// NewStagingError returns a new CloudFoundryError +// that IsStagingError will return true for +func NewStagingError() CloudFoundryError { + return CloudFoundryError{ + Code: 170001, + ErrorCode: "CF-StagingError", + Description: "Staging error: %s", + } +} + +// IsStagingError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170001 +// - HTTP code: 400 +// - message: "Staging error: %s" +func IsStagingError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170001 +} + +// NewNotStagedError returns a new CloudFoundryError +// that IsNotStagedError will return true for +func NewNotStagedError() CloudFoundryError { + return CloudFoundryError{ + Code: 170002, + ErrorCode: "CF-NotStaged", + Description: "App has not finished staging", + } +} + +// IsNotStagedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170002 +// - HTTP code: 400 +// - message: "App has not finished staging" +func IsNotStagedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170002 +} + +// NewNoAppDetectedError returns a new CloudFoundryError +// that IsNoAppDetectedError will return true for +func NewNoAppDetectedError() CloudFoundryError { + return CloudFoundryError{ + Code: 170003, + ErrorCode: "CF-NoAppDetectedError", + Description: "An app was not successfully detected by any available buildpack", + } +} + +// IsNoAppDetectedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170003 +// - HTTP code: 400 +// - message: "An app was not successfully detected by any available buildpack" +func IsNoAppDetectedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170003 +} + +// NewBuildpackCompileFailedError returns a new CloudFoundryError +// that IsBuildpackCompileFailedError will return true for +func NewBuildpackCompileFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 170004, + ErrorCode: "CF-BuildpackCompileFailed", + Description: "App staging failed in the buildpack compile phase", + } +} + +// IsBuildpackCompileFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170004 +// - HTTP code: 400 +// - message: "App staging failed in the buildpack compile phase" +func IsBuildpackCompileFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170004 +} + +// NewBuildpackReleaseFailedError returns a new CloudFoundryError +// that IsBuildpackReleaseFailedError will return true for +func NewBuildpackReleaseFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 170005, + ErrorCode: "CF-BuildpackReleaseFailed", + Description: "App staging failed in the buildpack release phase", + } +} + +// IsBuildpackReleaseFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170005 +// - HTTP code: 400 +// - message: "App staging failed in the buildpack release phase" +func IsBuildpackReleaseFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170005 +} + +// NewNoBuildpacksFoundError returns a new CloudFoundryError +// that IsNoBuildpacksFoundError will return true for +func NewNoBuildpacksFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 170006, + ErrorCode: "CF-NoBuildpacksFound", + Description: "There are no buildpacks available", + } +} + +// IsNoBuildpacksFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170006 +// - HTTP code: 400 +// - message: "There are no buildpacks available" +func IsNoBuildpacksFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170006 +} + +// NewStagingTimeExpiredError returns a new CloudFoundryError +// that IsStagingTimeExpiredError will return true for +func NewStagingTimeExpiredError() CloudFoundryError { + return CloudFoundryError{ + Code: 170007, + ErrorCode: "CF-StagingTimeExpired", + Description: "Staging time expired: %s", + } +} + +// IsStagingTimeExpiredError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170007 +// - HTTP code: 504 +// - message: "Staging time expired: %s" +func IsStagingTimeExpiredError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170007 +} + +// NewInsufficientResourcesError returns a new CloudFoundryError +// that IsInsufficientResourcesError will return true for +func NewInsufficientResourcesError() CloudFoundryError { + return CloudFoundryError{ + Code: 170008, + ErrorCode: "CF-InsufficientResources", + Description: "Insufficient resources", + } +} + +// IsInsufficientResourcesError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170008 +// - HTTP code: 400 +// - message: "Insufficient resources" +func IsInsufficientResourcesError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170008 +} + +// NewNoCompatibleCellError returns a new CloudFoundryError +// that IsNoCompatibleCellError will return true for +func NewNoCompatibleCellError() CloudFoundryError { + return CloudFoundryError{ + Code: 170009, + ErrorCode: "CF-NoCompatibleCell", + Description: "Found no compatible cell", + } +} + +// IsNoCompatibleCellError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170009 +// - HTTP code: 400 +// - message: "Found no compatible cell" +func IsNoCompatibleCellError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170009 +} + +// NewStagerUnavailableError returns a new CloudFoundryError +// that IsStagerUnavailableError will return true for +func NewStagerUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 170010, + ErrorCode: "CF-StagerUnavailable", + Description: "Stager is unavailable: %s", + } +} + +// IsStagerUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170010 +// - HTTP code: 503 +// - message: "Stager is unavailable: %s" +func IsStagerUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170010 +} + +// NewStagerError returns a new CloudFoundryError +// that IsStagerError will return true for +func NewStagerError() CloudFoundryError { + return CloudFoundryError{ + Code: 170011, + ErrorCode: "CF-StagerError", + Description: "Stager error: %s", + } +} + +// IsStagerError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170011 +// - HTTP code: 500 +// - message: "Stager error: %s" +func IsStagerError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170011 +} + +// NewRunnerInvalidRequestError returns a new CloudFoundryError +// that IsRunnerInvalidRequestError will return true for +func NewRunnerInvalidRequestError() CloudFoundryError { + return CloudFoundryError{ + Code: 170014, + ErrorCode: "CF-RunnerInvalidRequest", + Description: "Runner invalid request: %s", + } +} + +// IsRunnerInvalidRequestError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170014 +// - HTTP code: 500 +// - message: "Runner invalid request: %s" +func IsRunnerInvalidRequestError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170014 +} + +// NewRunnerUnavailableError returns a new CloudFoundryError +// that IsRunnerUnavailableError will return true for +func NewRunnerUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 170015, + ErrorCode: "CF-RunnerUnavailable", + Description: "Runner is unavailable: %s", + } +} + +// IsRunnerUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170015 +// - HTTP code: 503 +// - message: "Runner is unavailable: %s" +func IsRunnerUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170015 +} + +// NewRunnerError returns a new CloudFoundryError +// that IsRunnerError will return true for +func NewRunnerError() CloudFoundryError { + return CloudFoundryError{ + Code: 170016, + ErrorCode: "CF-RunnerError", + Description: "Runner error: %s", + } +} + +// IsRunnerError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170016 +// - HTTP code: 500 +// - message: "Runner error: %s" +func IsRunnerError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170016 +} + +// NewStagingInProgressError returns a new CloudFoundryError +// that IsStagingInProgressError will return true for +func NewStagingInProgressError() CloudFoundryError { + return CloudFoundryError{ + Code: 170017, + ErrorCode: "CF-StagingInProgress", + Description: "Only one build can be STAGING at a time per application.", + } +} + +// IsStagingInProgressError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170017 +// - HTTP code: 422 +// - message: "Only one build can be STAGING at a time per application." +func IsStagingInProgressError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170017 +} + +// NewInvalidTaskAddressError returns a new CloudFoundryError +// that IsInvalidTaskAddressError will return true for +func NewInvalidTaskAddressError() CloudFoundryError { + return CloudFoundryError{ + Code: 170018, + ErrorCode: "CF-InvalidTaskAddress", + Description: "Invalid config: %s", + } +} + +// IsInvalidTaskAddressError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170018 +// - HTTP code: 500 +// - message: "Invalid config: %s" +func IsInvalidTaskAddressError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170018 +} + +// NewTaskError returns a new CloudFoundryError +// that IsTaskError will return true for +func NewTaskError() CloudFoundryError { + return CloudFoundryError{ + Code: 170019, + ErrorCode: "CF-TaskError", + Description: "Task failed: %s", + } +} + +// IsTaskError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170019 +// - HTTP code: 500 +// - message: "Task failed: %s" +func IsTaskError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170019 +} + +// NewTaskWorkersUnavailableError returns a new CloudFoundryError +// that IsTaskWorkersUnavailableError will return true for +func NewTaskWorkersUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 170020, + ErrorCode: "CF-TaskWorkersUnavailable", + Description: "Task workers are unavailable: %s", + } +} + +// IsTaskWorkersUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170020 +// - HTTP code: 503 +// - message: "Task workers are unavailable: %s" +func IsTaskWorkersUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170020 +} + +// NewInvalidTaskRequestError returns a new CloudFoundryError +// that IsInvalidTaskRequestError will return true for +func NewInvalidTaskRequestError() CloudFoundryError { + return CloudFoundryError{ + Code: 170021, + ErrorCode: "CF-InvalidTaskRequest", + Description: "The task request is invalid: %s", + } +} + +// IsInvalidTaskRequestError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 170021 +// - HTTP code: 422 +// - message: "The task request is invalid: %s" +func IsInvalidTaskRequestError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 170021 +} + +// NewServiceGatewayError returns a new CloudFoundryError +// that IsServiceGatewayError will return true for +func NewServiceGatewayError() CloudFoundryError { + return CloudFoundryError{ + Code: 180002, + ErrorCode: "CF-ServiceGatewayError", + Description: "Service gateway internal error: %s", + } +} + +// IsServiceGatewayError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 180002 +// - HTTP code: 503 +// - message: "Service gateway internal error: %s" +func IsServiceGatewayError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 180002 +} + +// NewServiceNotImplementedError returns a new CloudFoundryError +// that IsServiceNotImplementedError will return true for +func NewServiceNotImplementedError() CloudFoundryError { + return CloudFoundryError{ + Code: 180003, + ErrorCode: "CF-ServiceNotImplemented", + Description: "Operation not supported for service", + } +} + +// IsServiceNotImplementedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 180003 +// - HTTP code: 501 +// - message: "Operation not supported for service" +func IsServiceNotImplementedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 180003 +} + +// NewSDSNotAvailableError returns a new CloudFoundryError +// that IsSDSNotAvailableError will return true for +func NewSDSNotAvailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 180004, + ErrorCode: "CF-SDSNotAvailable", + Description: "No serialization service backends available", + } +} + +// IsSDSNotAvailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 180004 +// - HTTP code: 501 +// - message: "No serialization service backends available" +func IsSDSNotAvailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 180004 +} + +// NewFileError returns a new CloudFoundryError +// that IsFileError will return true for +func NewFileError() CloudFoundryError { + return CloudFoundryError{ + Code: 190001, + ErrorCode: "CF-FileError", + Description: "File error: %s", + } +} + +// IsFileError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 190001 +// - HTTP code: 400 +// - message: "File error: %s" +func IsFileError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 190001 +} + +// NewStatsError returns a new CloudFoundryError +// that IsStatsError will return true for +func NewStatsError() CloudFoundryError { + return CloudFoundryError{ + Code: 200001, + ErrorCode: "CF-StatsError", + Description: "Stats error: %s", + } +} + +// IsStatsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 200001 +// - HTTP code: 400 +// - message: "Stats error: %s" +func IsStatsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 200001 +} + +// NewStatsUnavailableError returns a new CloudFoundryError +// that IsStatsUnavailableError will return true for +func NewStatsUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 200002, + ErrorCode: "CF-StatsUnavailable", + Description: "Stats unavailable: %s", + } +} + +// IsStatsUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 200002 +// - HTTP code: 503 +// - message: "Stats unavailable: %s" +func IsStatsUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 200002 +} + +// NewAppStoppedStatsError returns a new CloudFoundryError +// that IsAppStoppedStatsError will return true for +func NewAppStoppedStatsError() CloudFoundryError { + return CloudFoundryError{ + Code: 200003, + ErrorCode: "CF-AppStoppedStatsError", + Description: "Could not fetch stats for stopped app: %s", + } +} + +// IsAppStoppedStatsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 200003 +// - HTTP code: 400 +// - message: "Could not fetch stats for stopped app: %s" +func IsAppStoppedStatsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 200003 +} + +// NewRouteInvalidError returns a new CloudFoundryError +// that IsRouteInvalidError will return true for +func NewRouteInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 210001, + ErrorCode: "CF-RouteInvalid", + Description: "The route is invalid: %s", + } +} + +// IsRouteInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210001 +// - HTTP code: 400 +// - message: "The route is invalid: %s" +func IsRouteInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210001 +} + +// NewRouteNotFoundError returns a new CloudFoundryError +// that IsRouteNotFoundError will return true for +func NewRouteNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 210002, + ErrorCode: "CF-RouteNotFound", + Description: "The route could not be found: %s", + } +} + +// IsRouteNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210002 +// - HTTP code: 404 +// - message: "The route could not be found: %s" +func IsRouteNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210002 +} + +// NewRouteHostTakenError returns a new CloudFoundryError +// that IsRouteHostTakenError will return true for +func NewRouteHostTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 210003, + ErrorCode: "CF-RouteHostTaken", + Description: "The host is taken: %s", + } +} + +// IsRouteHostTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210003 +// - HTTP code: 400 +// - message: "The host is taken: %s" +func IsRouteHostTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210003 +} + +// NewRoutePathTakenError returns a new CloudFoundryError +// that IsRoutePathTakenError will return true for +func NewRoutePathTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 210004, + ErrorCode: "CF-RoutePathTaken", + Description: "The path is taken: %s", + } +} + +// IsRoutePathTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210004 +// - HTTP code: 400 +// - message: "The path is taken: %s" +func IsRoutePathTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210004 +} + +// NewRoutePortTakenError returns a new CloudFoundryError +// that IsRoutePortTakenError will return true for +func NewRoutePortTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 210005, + ErrorCode: "CF-RoutePortTaken", + Description: "The port is taken: %s", + } +} + +// IsRoutePortTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210005 +// - HTTP code: 400 +// - message: "The port is taken: %s" +func IsRoutePortTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210005 +} + +// NewRouteMappingTakenError returns a new CloudFoundryError +// that IsRouteMappingTakenError will return true for +func NewRouteMappingTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 210006, + ErrorCode: "CF-RouteMappingTaken", + Description: "The route mapping is taken: %s", + } +} + +// IsRouteMappingTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210006 +// - HTTP code: 400 +// - message: "The route mapping is taken: %s" +func IsRouteMappingTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210006 +} + +// NewRouteMappingNotFoundError returns a new CloudFoundryError +// that IsRouteMappingNotFoundError will return true for +func NewRouteMappingNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 210007, + ErrorCode: "CF-RouteMappingNotFound", + Description: "The route mapping could not be found: %s", + } +} + +// IsRouteMappingNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210007 +// - HTTP code: 404 +// - message: "The route mapping could not be found: %s" +func IsRouteMappingNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210007 +} + +// NewRouterGroupNotFoundError returns a new CloudFoundryError +// that IsRouterGroupNotFoundError will return true for +func NewRouterGroupNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 210009, + ErrorCode: "CF-RouterGroupNotFound", + Description: "The router group could not be found: %s", + } +} + +// IsRouterGroupNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 210009 +// - HTTP code: 404 +// - message: "The router group could not be found: %s" +func IsRouterGroupNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 210009 +} + +// NewInstancesError returns a new CloudFoundryError +// that IsInstancesError will return true for +func NewInstancesError() CloudFoundryError { + return CloudFoundryError{ + Code: 220001, + ErrorCode: "CF-InstancesError", + Description: "Instances error: %s", + } +} + +// IsInstancesError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 220001 +// - HTTP code: 400 +// - message: "Instances error: %s" +func IsInstancesError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 220001 +} + +// NewInstancesUnavailableError returns a new CloudFoundryError +// that IsInstancesUnavailableError will return true for +func NewInstancesUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 220002, + ErrorCode: "CF-InstancesUnavailable", + Description: "Instances information unavailable: %s", + } +} + +// IsInstancesUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 220002 +// - HTTP code: 503 +// - message: "Instances information unavailable: %s" +func IsInstancesUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 220002 +} + +// NewEventNotFoundError returns a new CloudFoundryError +// that IsEventNotFoundError will return true for +func NewEventNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 230002, + ErrorCode: "CF-EventNotFound", + Description: "Event could not be found: %s", + } +} + +// IsEventNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 230002 +// - HTTP code: 404 +// - message: "Event could not be found: %s" +func IsEventNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 230002 +} + +// NewQuotaDefinitionNotFoundError returns a new CloudFoundryError +// that IsQuotaDefinitionNotFoundError will return true for +func NewQuotaDefinitionNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 240001, + ErrorCode: "CF-QuotaDefinitionNotFound", + Description: "Quota Definition could not be found: %s", + } +} + +// IsQuotaDefinitionNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 240001 +// - HTTP code: 404 +// - message: "Quota Definition could not be found: %s" +func IsQuotaDefinitionNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 240001 +} + +// NewQuotaDefinitionNameTakenError returns a new CloudFoundryError +// that IsQuotaDefinitionNameTakenError will return true for +func NewQuotaDefinitionNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 240002, + ErrorCode: "CF-QuotaDefinitionNameTaken", + Description: "Quota Definition is taken: %s", + } +} + +// IsQuotaDefinitionNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 240002 +// - HTTP code: 400 +// - message: "Quota Definition is taken: %s" +func IsQuotaDefinitionNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 240002 +} + +// NewQuotaDefinitionInvalidError returns a new CloudFoundryError +// that IsQuotaDefinitionInvalidError will return true for +func NewQuotaDefinitionInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 240003, + ErrorCode: "CF-QuotaDefinitionInvalid", + Description: "Quota Definition is invalid: %s", + } +} + +// IsQuotaDefinitionInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 240003 +// - HTTP code: 400 +// - message: "Quota Definition is invalid: %s" +func IsQuotaDefinitionInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 240003 +} + +// NewQuotaDefinitionMemoryLimitInvalidError returns a new CloudFoundryError +// that IsQuotaDefinitionMemoryLimitInvalidError will return true for +func NewQuotaDefinitionMemoryLimitInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 240004, + ErrorCode: "CF-QuotaDefinitionMemoryLimitInvalid", + Description: "Quota Definition memory limit cannot be less than -1", + } +} + +// IsQuotaDefinitionMemoryLimitInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 240004 +// - HTTP code: 400 +// - message: "Quota Definition memory limit cannot be less than -1" +func IsQuotaDefinitionMemoryLimitInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 240004 +} + +// NewStackInvalidError returns a new CloudFoundryError +// that IsStackInvalidError will return true for +func NewStackInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 250001, + ErrorCode: "CF-StackInvalid", + Description: "The stack is invalid: %s", + } +} + +// IsStackInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 250001 +// - HTTP code: 400 +// - message: "The stack is invalid: %s" +func IsStackInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 250001 +} + +// NewStackNameTakenError returns a new CloudFoundryError +// that IsStackNameTakenError will return true for +func NewStackNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 250002, + ErrorCode: "CF-StackNameTaken", + Description: "The stack name is taken: %s", + } +} + +// IsStackNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 250002 +// - HTTP code: 400 +// - message: "The stack name is taken: %s" +func IsStackNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 250002 +} + +// NewStackNotFoundError returns a new CloudFoundryError +// that IsStackNotFoundError will return true for +func NewStackNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 250003, + ErrorCode: "CF-StackNotFound", + Description: "The stack could not be found: %s", + } +} + +// IsStackNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 250003 +// - HTTP code: 404 +// - message: "The stack could not be found: %s" +func IsStackNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 250003 +} + +// NewServicePlanVisibilityInvalidError returns a new CloudFoundryError +// that IsServicePlanVisibilityInvalidError will return true for +func NewServicePlanVisibilityInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 260001, + ErrorCode: "CF-ServicePlanVisibilityInvalid", + Description: "Service Plan Visibility is invalid: %s", + } +} + +// IsServicePlanVisibilityInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 260001 +// - HTTP code: 400 +// - message: "Service Plan Visibility is invalid: %s" +func IsServicePlanVisibilityInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 260001 +} + +// NewServicePlanVisibilityAlreadyExistsError returns a new CloudFoundryError +// that IsServicePlanVisibilityAlreadyExistsError will return true for +func NewServicePlanVisibilityAlreadyExistsError() CloudFoundryError { + return CloudFoundryError{ + Code: 260002, + ErrorCode: "CF-ServicePlanVisibilityAlreadyExists", + Description: "This combination of ServicePlan and Organization is already taken: %s", + } +} + +// IsServicePlanVisibilityAlreadyExistsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 260002 +// - HTTP code: 400 +// - message: "This combination of ServicePlan and Organization is already taken: %s" +func IsServicePlanVisibilityAlreadyExistsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 260002 +} + +// NewServicePlanVisibilityNotFoundError returns a new CloudFoundryError +// that IsServicePlanVisibilityNotFoundError will return true for +func NewServicePlanVisibilityNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 260003, + ErrorCode: "CF-ServicePlanVisibilityNotFound", + Description: "The service plan visibility could not be found: %s", + } +} + +// IsServicePlanVisibilityNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 260003 +// - HTTP code: 404 +// - message: "The service plan visibility could not be found: %s" +func IsServicePlanVisibilityNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 260003 +} + +// NewServiceBrokerInvalidError returns a new CloudFoundryError +// that IsServiceBrokerInvalidError will return true for +func NewServiceBrokerInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 270001, + ErrorCode: "CF-ServiceBrokerInvalid", + Description: "Service broker is invalid: %s", + } +} + +// IsServiceBrokerInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270001 +// - HTTP code: 400 +// - message: "Service broker is invalid: %s" +func IsServiceBrokerInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270001 +} + +// NewServiceBrokerNameTakenError returns a new CloudFoundryError +// that IsServiceBrokerNameTakenError will return true for +func NewServiceBrokerNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 270002, + ErrorCode: "CF-ServiceBrokerNameTaken", + Description: "The service broker name is taken", + } +} + +// IsServiceBrokerNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270002 +// - HTTP code: 400 +// - message: "The service broker name is taken" +func IsServiceBrokerNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270002 +} + +// NewServiceBrokerUrlTakenError returns a new CloudFoundryError +// that IsServiceBrokerUrlTakenError will return true for +func NewServiceBrokerUrlTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 270003, + ErrorCode: "CF-ServiceBrokerUrlTaken", + Description: "The service broker url is taken: %s", + } +} + +// IsServiceBrokerUrlTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270003 +// - HTTP code: 400 +// - message: "The service broker url is taken: %s" +func IsServiceBrokerUrlTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270003 +} + +// NewServiceBrokerNotFoundError returns a new CloudFoundryError +// that IsServiceBrokerNotFoundError will return true for +func NewServiceBrokerNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 270004, + ErrorCode: "CF-ServiceBrokerNotFound", + Description: "The service broker was not found: %s", + } +} + +// IsServiceBrokerNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270004 +// - HTTP code: 404 +// - message: "The service broker was not found: %s" +func IsServiceBrokerNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270004 +} + +// NewServiceBrokerNotRemovableError returns a new CloudFoundryError +// that IsServiceBrokerNotRemovableError will return true for +func NewServiceBrokerNotRemovableError() CloudFoundryError { + return CloudFoundryError{ + Code: 270010, + ErrorCode: "CF-ServiceBrokerNotRemovable", + Description: "Can not remove brokers that have associated service instances: %s", + } +} + +// IsServiceBrokerNotRemovableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270010 +// - HTTP code: 400 +// - message: "Can not remove brokers that have associated service instances: %s" +func IsServiceBrokerNotRemovableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270010 +} + +// NewServiceBrokerUrlInvalidError returns a new CloudFoundryError +// that IsServiceBrokerUrlInvalidError will return true for +func NewServiceBrokerUrlInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 270011, + ErrorCode: "CF-ServiceBrokerUrlInvalid", + Description: "%s is not a valid URL", + } +} + +// IsServiceBrokerUrlInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270011 +// - HTTP code: 400 +// - message: "%s is not a valid URL" +func IsServiceBrokerUrlInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270011 +} + +// NewServiceBrokerCatalogInvalidError returns a new CloudFoundryError +// that IsServiceBrokerCatalogInvalidError will return true for +func NewServiceBrokerCatalogInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 270012, + ErrorCode: "CF-ServiceBrokerCatalogInvalid", + Description: "Service broker catalog is invalid: %s", + } +} + +// IsServiceBrokerCatalogInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270012 +// - HTTP code: 502 +// - message: "Service broker catalog is invalid: %s" +func IsServiceBrokerCatalogInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270012 +} + +// NewServiceBrokerDashboardClientFailureError returns a new CloudFoundryError +// that IsServiceBrokerDashboardClientFailureError will return true for +func NewServiceBrokerDashboardClientFailureError() CloudFoundryError { + return CloudFoundryError{ + Code: 270013, + ErrorCode: "CF-ServiceBrokerDashboardClientFailure", + Description: "Service broker dashboard clients could not be modified: %s", + } +} + +// IsServiceBrokerDashboardClientFailureError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270013 +// - HTTP code: 502 +// - message: "Service broker dashboard clients could not be modified: %s" +func IsServiceBrokerDashboardClientFailureError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270013 +} + +// NewServiceBrokerAsyncRequiredError returns a new CloudFoundryError +// that IsServiceBrokerAsyncRequiredError will return true for +func NewServiceBrokerAsyncRequiredError() CloudFoundryError { + return CloudFoundryError{ + Code: 270014, + ErrorCode: "CF-ServiceBrokerAsyncRequired", + Description: "This service plan requires client support for asynchronous service operations.", + } +} + +// IsServiceBrokerAsyncRequiredError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270014 +// - HTTP code: 400 +// - message: "This service plan requires client support for asynchronous service operations." +func IsServiceBrokerAsyncRequiredError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270014 +} + +// NewServiceDashboardClientMissingUrlError returns a new CloudFoundryError +// that IsServiceDashboardClientMissingUrlError will return true for +func NewServiceDashboardClientMissingUrlError() CloudFoundryError { + return CloudFoundryError{ + Code: 270015, + ErrorCode: "CF-ServiceDashboardClientMissingUrl", + Description: "Service broker returned dashboard client configuration without a dashboard URL", + } +} + +// IsServiceDashboardClientMissingUrlError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270015 +// - HTTP code: 502 +// - message: "Service broker returned dashboard client configuration without a dashboard URL" +func IsServiceDashboardClientMissingUrlError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270015 +} + +// NewServiceBrokerUrlBasicAuthNotSupportedError returns a new CloudFoundryError +// that IsServiceBrokerUrlBasicAuthNotSupportedError will return true for +func NewServiceBrokerUrlBasicAuthNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 270016, + ErrorCode: "CF-ServiceBrokerUrlBasicAuthNotSupported", + Description: "User name and password fields in the broker URI are not supported", + } +} + +// IsServiceBrokerUrlBasicAuthNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270016 +// - HTTP code: 400 +// - message: "User name and password fields in the broker URI are not supported" +func IsServiceBrokerUrlBasicAuthNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270016 +} + +// NewServiceBrokerRespondedAsyncWhenNotAllowedError returns a new CloudFoundryError +// that IsServiceBrokerRespondedAsyncWhenNotAllowedError will return true for +func NewServiceBrokerRespondedAsyncWhenNotAllowedError() CloudFoundryError { + return CloudFoundryError{ + Code: 270017, + ErrorCode: "CF-ServiceBrokerRespondedAsyncWhenNotAllowed", + Description: "The service broker responded asynchronously to a request, but the accepts_incomplete query parameter was false or not given.", + } +} + +// IsServiceBrokerRespondedAsyncWhenNotAllowedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270017 +// - HTTP code: 502 +// - message: "The service broker responded asynchronously to a request, but the accepts_incomplete query parameter was false or not given." +func IsServiceBrokerRespondedAsyncWhenNotAllowedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270017 +} + +// NewServiceBrokerConcurrencyError returns a new CloudFoundryError +// that IsServiceBrokerConcurrencyError will return true for +func NewServiceBrokerConcurrencyError() CloudFoundryError { + return CloudFoundryError{ + Code: 270018, + ErrorCode: "CF-ServiceBrokerConcurrencyError", + Description: "The service broker could not perform this operation in parallel with other running operations", + } +} + +// IsServiceBrokerConcurrencyError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270018 +// - HTTP code: 422 +// - message: "The service broker could not perform this operation in parallel with other running operations" +func IsServiceBrokerConcurrencyError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270018 +} + +// NewServiceBrokerCatalogIncompatibleError returns a new CloudFoundryError +// that IsServiceBrokerCatalogIncompatibleError will return true for +func NewServiceBrokerCatalogIncompatibleError() CloudFoundryError { + return CloudFoundryError{ + Code: 270019, + ErrorCode: "CF-ServiceBrokerCatalogIncompatible", + Description: "Service broker catalog is incompatible: %s", + } +} + +// IsServiceBrokerCatalogIncompatibleError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270019 +// - HTTP code: 502 +// - message: "Service broker catalog is incompatible: %s" +func IsServiceBrokerCatalogIncompatibleError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270019 +} + +// NewServiceBrokerRequestRejectedError returns a new CloudFoundryError +// that IsServiceBrokerRequestRejectedError will return true for +func NewServiceBrokerRequestRejectedError() CloudFoundryError { + return CloudFoundryError{ + Code: 270020, + ErrorCode: "CF-ServiceBrokerRequestRejected", + Description: "The service broker rejected the request. Status Code: %s. Please check that the URL points to a valid service broker.", + } +} + +// IsServiceBrokerRequestRejectedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270020 +// - HTTP code: 502 +// - message: "The service broker rejected the request. Status Code: %s. Please check that the URL points to a valid service broker." +func IsServiceBrokerRequestRejectedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270020 +} + +// NewServiceBrokerRequestMalformedError returns a new CloudFoundryError +// that IsServiceBrokerRequestMalformedError will return true for +func NewServiceBrokerRequestMalformedError() CloudFoundryError { + return CloudFoundryError{ + Code: 270021, + ErrorCode: "CF-ServiceBrokerRequestMalformed", + Description: "The service broker returned an invalid response: expected valid JSON object in body. Please check that the URL points to a valid service broker.", + } +} + +// IsServiceBrokerRequestMalformedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 270021 +// - HTTP code: 502 +// - message: "The service broker returned an invalid response: expected valid JSON object in body. Please check that the URL points to a valid service broker." +func IsServiceBrokerRequestMalformedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 270021 +} + +// NewBuildpackNameStackTakenError returns a new CloudFoundryError +// that IsBuildpackNameStackTakenError will return true for +func NewBuildpackNameStackTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 290000, + ErrorCode: "CF-BuildpackNameStackTaken", + Description: "The buildpack name %s is already in use for the stack %s", + } +} + +// IsBuildpackNameStackTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290000 +// - HTTP code: 422 +// - message: "The buildpack name %s is already in use for the stack %s" +func IsBuildpackNameStackTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290000 +} + +// NewBuildpackNameTakenError returns a new CloudFoundryError +// that IsBuildpackNameTakenError will return true for +func NewBuildpackNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 290001, + ErrorCode: "CF-BuildpackNameTaken", + Description: "The buildpack name is already in use: %s", + } +} + +// IsBuildpackNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290001 +// - HTTP code: 400 +// - message: "The buildpack name is already in use: %s" +func IsBuildpackNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290001 +} + +// NewBuildpackBitsUploadInvalidError returns a new CloudFoundryError +// that IsBuildpackBitsUploadInvalidError will return true for +func NewBuildpackBitsUploadInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 290002, + ErrorCode: "CF-BuildpackBitsUploadInvalid", + Description: "The buildpack upload is invalid: %s", + } +} + +// IsBuildpackBitsUploadInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290002 +// - HTTP code: 400 +// - message: "The buildpack upload is invalid: %s" +func IsBuildpackBitsUploadInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290002 +} + +// NewBuildpackInvalidError returns a new CloudFoundryError +// that IsBuildpackInvalidError will return true for +func NewBuildpackInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 290003, + ErrorCode: "CF-BuildpackInvalid", + Description: "Buildpack is invalid: %s", + } +} + +// IsBuildpackInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290003 +// - HTTP code: 400 +// - message: "Buildpack is invalid: %s" +func IsBuildpackInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290003 +} + +// NewCustomBuildpacksDisabledError returns a new CloudFoundryError +// that IsCustomBuildpacksDisabledError will return true for +func NewCustomBuildpacksDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 290004, + ErrorCode: "CF-CustomBuildpacksDisabled", + Description: "Custom buildpacks are disabled", + } +} + +// IsCustomBuildpacksDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290004 +// - HTTP code: 400 +// - message: "Custom buildpacks are disabled" +func IsCustomBuildpacksDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290004 +} + +// NewBuildpackLockedError returns a new CloudFoundryError +// that IsBuildpackLockedError will return true for +func NewBuildpackLockedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290005, + ErrorCode: "CF-BuildpackLocked", + Description: "The buildpack is locked", + } +} + +// IsBuildpackLockedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290005 +// - HTTP code: 409 +// - message: "The buildpack is locked" +func IsBuildpackLockedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290005 +} + +// NewJobTimeoutError returns a new CloudFoundryError +// that IsJobTimeoutError will return true for +func NewJobTimeoutError() CloudFoundryError { + return CloudFoundryError{ + Code: 290006, + ErrorCode: "CF-JobTimeout", + Description: "The job execution has timed out.", + } +} + +// IsJobTimeoutError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290006 +// - HTTP code: 524 +// - message: "The job execution has timed out." +func IsJobTimeoutError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290006 +} + +// NewSpaceDeleteTimeoutError returns a new CloudFoundryError +// that IsSpaceDeleteTimeoutError will return true for +func NewSpaceDeleteTimeoutError() CloudFoundryError { + return CloudFoundryError{ + Code: 290007, + ErrorCode: "CF-SpaceDeleteTimeout", + Description: "Deletion of space %s timed out before all resources within could be deleted", + } +} + +// IsSpaceDeleteTimeoutError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290007 +// - HTTP code: 524 +// - message: "Deletion of space %s timed out before all resources within could be deleted" +func IsSpaceDeleteTimeoutError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290007 +} + +// NewSpaceDeletionFailedError returns a new CloudFoundryError +// that IsSpaceDeletionFailedError will return true for +func NewSpaceDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290008, + ErrorCode: "CF-SpaceDeletionFailed", + Description: "Deletion of space %s failed because one or more resources within could not be deleted.%s", + } +} + +// IsSpaceDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290008 +// - HTTP code: 502 +// - message: "Deletion of space %s failed because one or more resources within could not be deleted.\n\n%s" +func IsSpaceDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290008 +} + +// NewOrganizationDeleteTimeoutError returns a new CloudFoundryError +// that IsOrganizationDeleteTimeoutError will return true for +func NewOrganizationDeleteTimeoutError() CloudFoundryError { + return CloudFoundryError{ + Code: 290009, + ErrorCode: "CF-OrganizationDeleteTimeout", + Description: "Delete of organization %s timed out before all resources within could be deleted", + } +} + +// IsOrganizationDeleteTimeoutError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290009 +// - HTTP code: 524 +// - message: "Delete of organization %s timed out before all resources within could be deleted" +func IsOrganizationDeleteTimeoutError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290009 +} + +// NewOrganizationDeletionFailedError returns a new CloudFoundryError +// that IsOrganizationDeletionFailedError will return true for +func NewOrganizationDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290010, + ErrorCode: "CF-OrganizationDeletionFailed", + Description: "Deletion of organization %s failed because one or more resources within could not be deleted.%s", + } +} + +// IsOrganizationDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290010 +// - HTTP code: 502 +// - message: "Deletion of organization %s failed because one or more resources within could not be deleted.\n\n%s" +func IsOrganizationDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290010 +} + +// NewNonrecursiveSpaceDeletionFailedError returns a new CloudFoundryError +// that IsNonrecursiveSpaceDeletionFailedError will return true for +func NewNonrecursiveSpaceDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290011, + ErrorCode: "CF-NonrecursiveSpaceDeletionFailed", + Description: "Resource inside space %s must first be deleted, or specify recursive delete.", + } +} + +// IsNonrecursiveSpaceDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290011 +// - HTTP code: 400 +// - message: "Resource inside space %s must first be deleted, or specify recursive delete." +func IsNonrecursiveSpaceDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290011 +} + +// NewBitsServiceError returns a new CloudFoundryError +// that IsBitsServiceError will return true for +func NewBitsServiceError() CloudFoundryError { + return CloudFoundryError{ + Code: 290012, + ErrorCode: "CF-BitsServiceError", + Description: "The bits service returned an error: %s", + } +} + +// IsBitsServiceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290012 +// - HTTP code: 500 +// - message: "The bits service returned an error: %s" +func IsBitsServiceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290012 +} + +// NewSpaceRolesDeletionTimeoutError returns a new CloudFoundryError +// that IsSpaceRolesDeletionTimeoutError will return true for +func NewSpaceRolesDeletionTimeoutError() CloudFoundryError { + return CloudFoundryError{ + Code: 290013, + ErrorCode: "CF-SpaceRolesDeletionTimeout", + Description: "Deletion of roles for space %s timed out before all roles could be deleted", + } +} + +// IsSpaceRolesDeletionTimeoutError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290013 +// - HTTP code: 524 +// - message: "Deletion of roles for space %s timed out before all roles could be deleted" +func IsSpaceRolesDeletionTimeoutError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290013 +} + +// NewOrganizationRolesDeletionFailedError returns a new CloudFoundryError +// that IsOrganizationRolesDeletionFailedError will return true for +func NewOrganizationRolesDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290014, + ErrorCode: "CF-OrganizationRolesDeletionFailed", + Description: "Failed to delete one or more roles for organization %s", + } +} + +// IsOrganizationRolesDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290014 +// - HTTP code: 502 +// - message: "Failed to delete one or more roles for organization %s" +func IsOrganizationRolesDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290014 +} + +// NewSpaceRolesDeletionFailedError returns a new CloudFoundryError +// that IsSpaceRolesDeletionFailedError will return true for +func NewSpaceRolesDeletionFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 290016, + ErrorCode: "CF-SpaceRolesDeletionFailed", + Description: "Failed to delete one or more roles for space %s", + } +} + +// IsSpaceRolesDeletionFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 290016 +// - HTTP code: 502 +// - message: "Failed to delete one or more roles for space %s" +func IsSpaceRolesDeletionFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 290016 +} + +// NewSecurityGroupInvalidError returns a new CloudFoundryError +// that IsSecurityGroupInvalidError will return true for +func NewSecurityGroupInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 300001, + ErrorCode: "CF-SecurityGroupInvalid", + Description: "The security group is invalid: %s", + } +} + +// IsSecurityGroupInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300001 +// - HTTP code: 400 +// - message: "The security group is invalid: %s" +func IsSecurityGroupInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300001 +} + +// NewSecurityGroupNotFoundError returns a new CloudFoundryError +// that IsSecurityGroupNotFoundError will return true for +func NewSecurityGroupNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 300002, + ErrorCode: "CF-SecurityGroupNotFound", + Description: "The security group could not be found: %s", + } +} + +// IsSecurityGroupNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300002 +// - HTTP code: 404 +// - message: "The security group could not be found: %s" +func IsSecurityGroupNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300002 +} + +// NewSecurityGroupStagingDefaultInvalidError returns a new CloudFoundryError +// that IsSecurityGroupStagingDefaultInvalidError will return true for +func NewSecurityGroupStagingDefaultInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 300003, + ErrorCode: "CF-SecurityGroupStagingDefaultInvalid", + Description: "The security group could not be found: %s", + } +} + +// IsSecurityGroupStagingDefaultInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300003 +// - HTTP code: 400 +// - message: "The security group could not be found: %s" +func IsSecurityGroupStagingDefaultInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300003 +} + +// NewSecurityGroupRunningDefaultInvalidError returns a new CloudFoundryError +// that IsSecurityGroupRunningDefaultInvalidError will return true for +func NewSecurityGroupRunningDefaultInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 300004, + ErrorCode: "CF-SecurityGroupRunningDefaultInvalid", + Description: "The security group could not be found: %s", + } +} + +// IsSecurityGroupRunningDefaultInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300004 +// - HTTP code: 400 +// - message: "The security group could not be found: %s" +func IsSecurityGroupRunningDefaultInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300004 +} + +// NewSecurityGroupNameTakenError returns a new CloudFoundryError +// that IsSecurityGroupNameTakenError will return true for +func NewSecurityGroupNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 300005, + ErrorCode: "CF-SecurityGroupNameTaken", + Description: "The security group name is taken: %s", + } +} + +// IsSecurityGroupNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 300005 +// - HTTP code: 400 +// - message: "The security group name is taken: %s" +func IsSecurityGroupNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 300005 +} + +// NewSpaceQuotaDefinitionInvalidError returns a new CloudFoundryError +// that IsSpaceQuotaDefinitionInvalidError will return true for +func NewSpaceQuotaDefinitionInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 310001, + ErrorCode: "CF-SpaceQuotaDefinitionInvalid", + Description: "Space Quota Definition is invalid: %s", + } +} + +// IsSpaceQuotaDefinitionInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310001 +// - HTTP code: 400 +// - message: "Space Quota Definition is invalid: %s" +func IsSpaceQuotaDefinitionInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310001 +} + +// NewSpaceQuotaDefinitionNameTakenError returns a new CloudFoundryError +// that IsSpaceQuotaDefinitionNameTakenError will return true for +func NewSpaceQuotaDefinitionNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 310002, + ErrorCode: "CF-SpaceQuotaDefinitionNameTaken", + Description: "The space quota definition name is taken: %s", + } +} + +// IsSpaceQuotaDefinitionNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310002 +// - HTTP code: 400 +// - message: "The space quota definition name is taken: %s" +func IsSpaceQuotaDefinitionNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310002 +} + +// NewSpaceQuotaMemoryLimitExceededError returns a new CloudFoundryError +// that IsSpaceQuotaMemoryLimitExceededError will return true for +func NewSpaceQuotaMemoryLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310003, + ErrorCode: "CF-SpaceQuotaMemoryLimitExceeded", + Description: "You have exceeded your space's memory limit: %s", + } +} + +// IsSpaceQuotaMemoryLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310003 +// - HTTP code: 400 +// - message: "You have exceeded your space's memory limit: %s" +func IsSpaceQuotaMemoryLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310003 +} + +// NewSpaceQuotaInstanceMemoryLimitExceededError returns a new CloudFoundryError +// that IsSpaceQuotaInstanceMemoryLimitExceededError will return true for +func NewSpaceQuotaInstanceMemoryLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310004, + ErrorCode: "CF-SpaceQuotaInstanceMemoryLimitExceeded", + Description: "You have exceeded the instance memory limit for your space's quota.", + } +} + +// IsSpaceQuotaInstanceMemoryLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310004 +// - HTTP code: 400 +// - message: "You have exceeded the instance memory limit for your space's quota." +func IsSpaceQuotaInstanceMemoryLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310004 +} + +// NewSpaceQuotaTotalRoutesExceededError returns a new CloudFoundryError +// that IsSpaceQuotaTotalRoutesExceededError will return true for +func NewSpaceQuotaTotalRoutesExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310005, + ErrorCode: "CF-SpaceQuotaTotalRoutesExceeded", + Description: "You have exceeded the total routes for your space's quota.", + } +} + +// IsSpaceQuotaTotalRoutesExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310005 +// - HTTP code: 400 +// - message: "You have exceeded the total routes for your space's quota." +func IsSpaceQuotaTotalRoutesExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310005 +} + +// NewOrgQuotaTotalRoutesExceededError returns a new CloudFoundryError +// that IsOrgQuotaTotalRoutesExceededError will return true for +func NewOrgQuotaTotalRoutesExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310006, + ErrorCode: "CF-OrgQuotaTotalRoutesExceeded", + Description: "You have exceeded the total routes for your organization's quota.", + } +} + +// IsOrgQuotaTotalRoutesExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310006 +// - HTTP code: 400 +// - message: "You have exceeded the total routes for your organization's quota." +func IsOrgQuotaTotalRoutesExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310006 +} + +// NewSpaceQuotaDefinitionNotFoundError returns a new CloudFoundryError +// that IsSpaceQuotaDefinitionNotFoundError will return true for +func NewSpaceQuotaDefinitionNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 310007, + ErrorCode: "CF-SpaceQuotaDefinitionNotFound", + Description: "Space Quota Definition could not be found: %s", + } +} + +// IsSpaceQuotaDefinitionNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310007 +// - HTTP code: 404 +// - message: "Space Quota Definition could not be found: %s" +func IsSpaceQuotaDefinitionNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310007 +} + +// NewSpaceQuotaInstanceLimitExceededError returns a new CloudFoundryError +// that IsSpaceQuotaInstanceLimitExceededError will return true for +func NewSpaceQuotaInstanceLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310008, + ErrorCode: "CF-SpaceQuotaInstanceLimitExceeded", + Description: "You have exceeded the instance limit for your space's quota.", + } +} + +// IsSpaceQuotaInstanceLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310008 +// - HTTP code: 400 +// - message: "You have exceeded the instance limit for your space's quota." +func IsSpaceQuotaInstanceLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310008 +} + +// NewOrgQuotaTotalReservedRoutePortsExceededError returns a new CloudFoundryError +// that IsOrgQuotaTotalReservedRoutePortsExceededError will return true for +func NewOrgQuotaTotalReservedRoutePortsExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310009, + ErrorCode: "CF-OrgQuotaTotalReservedRoutePortsExceeded", + Description: "You have exceeded the total reserved route ports for your organization's quota.", + } +} + +// IsOrgQuotaTotalReservedRoutePortsExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310009 +// - HTTP code: 400 +// - message: "You have exceeded the total reserved route ports for your organization's quota." +func IsOrgQuotaTotalReservedRoutePortsExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310009 +} + +// NewSpaceQuotaTotalReservedRoutePortsExceededError returns a new CloudFoundryError +// that IsSpaceQuotaTotalReservedRoutePortsExceededError will return true for +func NewSpaceQuotaTotalReservedRoutePortsExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 310010, + ErrorCode: "CF-SpaceQuotaTotalReservedRoutePortsExceeded", + Description: "You have exceeded the total reserved route ports for your space's quota.", + } +} + +// IsSpaceQuotaTotalReservedRoutePortsExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 310010 +// - HTTP code: 400 +// - message: "You have exceeded the total reserved route ports for your space's quota." +func IsSpaceQuotaTotalReservedRoutePortsExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 310010 +} + +// NewDiegoDisabledError returns a new CloudFoundryError +// that IsDiegoDisabledError will return true for +func NewDiegoDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 320001, + ErrorCode: "CF-DiegoDisabled", + Description: "Diego has not been enabled.", + } +} + +// IsDiegoDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320001 +// - HTTP code: 400 +// - message: "Diego has not been enabled." +func IsDiegoDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320001 +} + +// NewDiegoDockerBuildpackConflictError returns a new CloudFoundryError +// that IsDiegoDockerBuildpackConflictError will return true for +func NewDiegoDockerBuildpackConflictError() CloudFoundryError { + return CloudFoundryError{ + Code: 320002, + ErrorCode: "CF-DiegoDockerBuildpackConflict", + Description: "You cannot specify a custom buildpack and a docker image at the same time.", + } +} + +// IsDiegoDockerBuildpackConflictError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320002 +// - HTTP code: 400 +// - message: "You cannot specify a custom buildpack and a docker image at the same time." +func IsDiegoDockerBuildpackConflictError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320002 +} + +// NewDockerDisabledError returns a new CloudFoundryError +// that IsDockerDisabledError will return true for +func NewDockerDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 320003, + ErrorCode: "CF-DockerDisabled", + Description: "Docker support has not been enabled.", + } +} + +// IsDockerDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320003 +// - HTTP code: 400 +// - message: "Docker support has not been enabled." +func IsDockerDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320003 +} + +// NewStagingBackendInvalidError returns a new CloudFoundryError +// that IsStagingBackendInvalidError will return true for +func NewStagingBackendInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 320004, + ErrorCode: "CF-StagingBackendInvalid", + Description: "The request staging completion endpoint only handles apps desired to stage on the Diego backend.", + } +} + +// IsStagingBackendInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320004 +// - HTTP code: 403 +// - message: "The request staging completion endpoint only handles apps desired to stage on the Diego backend." +func IsStagingBackendInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320004 +} + +// NewBackendSelectionNotAuthorizedError returns a new CloudFoundryError +// that IsBackendSelectionNotAuthorizedError will return true for +func NewBackendSelectionNotAuthorizedError() CloudFoundryError { + return CloudFoundryError{ + Code: 320005, + ErrorCode: "CF-BackendSelectionNotAuthorized", + Description: "You cannot select the backend on which to run this application", + } +} + +// IsBackendSelectionNotAuthorizedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320005 +// - HTTP code: 403 +// - message: "You cannot select the backend on which to run this application" +func IsBackendSelectionNotAuthorizedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320005 +} + +// NewRevisionsEnabledError returns a new CloudFoundryError +// that IsRevisionsEnabledError will return true for +func NewRevisionsEnabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 320006, + ErrorCode: "CF-RevisionsEnabled", + Description: "V2 restaging is disabled when your app has revisions enabled", + } +} + +// IsRevisionsEnabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 320006 +// - HTTP code: 400 +// - message: "V2 restaging is disabled when your app has revisions enabled" +func IsRevisionsEnabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 320006 +} + +// NewFeatureFlagNotFoundError returns a new CloudFoundryError +// that IsFeatureFlagNotFoundError will return true for +func NewFeatureFlagNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 330000, + ErrorCode: "CF-FeatureFlagNotFound", + Description: "The feature flag could not be found: %s", + } +} + +// IsFeatureFlagNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 330000 +// - HTTP code: 404 +// - message: "The feature flag could not be found: %s" +func IsFeatureFlagNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 330000 +} + +// NewFeatureFlagInvalidError returns a new CloudFoundryError +// that IsFeatureFlagInvalidError will return true for +func NewFeatureFlagInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 330001, + ErrorCode: "CF-FeatureFlagInvalid", + Description: "The feature flag is invalid: %s", + } +} + +// IsFeatureFlagInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 330001 +// - HTTP code: 400 +// - message: "The feature flag is invalid: %s" +func IsFeatureFlagInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 330001 +} + +// NewFeatureDisabledError returns a new CloudFoundryError +// that IsFeatureDisabledError will return true for +func NewFeatureDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 330002, + ErrorCode: "CF-FeatureDisabled", + Description: "Feature Disabled: %s", + } +} + +// IsFeatureDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 330002 +// - HTTP code: 403 +// - message: "Feature Disabled: %s" +func IsFeatureDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 330002 +} + +// NewUserProvidedServiceInstanceNotFoundError returns a new CloudFoundryError +// that IsUserProvidedServiceInstanceNotFoundError will return true for +func NewUserProvidedServiceInstanceNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 340001, + ErrorCode: "CF-UserProvidedServiceInstanceNotFound", + Description: "The service instance could not be found: %s", + } +} + +// IsUserProvidedServiceInstanceNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 340001 +// - HTTP code: 404 +// - message: "The service instance could not be found: %s" +func IsUserProvidedServiceInstanceNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 340001 +} + +// NewUserProvidedServiceInstanceHandlerNeededError returns a new CloudFoundryError +// that IsUserProvidedServiceInstanceHandlerNeededError will return true for +func NewUserProvidedServiceInstanceHandlerNeededError() CloudFoundryError { + return CloudFoundryError{ + Code: 340002, + ErrorCode: "CF-UserProvidedServiceInstanceHandlerNeeded", + Description: "Please use the User Provided Services API to manage this resource.", + } +} + +// IsUserProvidedServiceInstanceHandlerNeededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 340002 +// - HTTP code: 400 +// - message: "Please use the User Provided Services API to manage this resource." +func IsUserProvidedServiceInstanceHandlerNeededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 340002 +} + +// NewProcessInvalidError returns a new CloudFoundryError +// that IsProcessInvalidError will return true for +func NewProcessInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 350001, + ErrorCode: "CF-ProcessInvalid", + Description: "The process is invalid: %s", + } +} + +// IsProcessInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 350001 +// - HTTP code: 400 +// - message: "The process is invalid: %s" +func IsProcessInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 350001 +} + +// NewUnableToDeleteError returns a new CloudFoundryError +// that IsUnableToDeleteError will return true for +func NewUnableToDeleteError() CloudFoundryError { + return CloudFoundryError{ + Code: 350002, + ErrorCode: "CF-UnableToDelete", + Description: "Unable to perform delete action: %s", + } +} + +// IsUnableToDeleteError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 350002 +// - HTTP code: 400 +// - message: "Unable to perform delete action: %s" +func IsUnableToDeleteError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 350002 +} + +// NewProcessNotFoundError returns a new CloudFoundryError +// that IsProcessNotFoundError will return true for +func NewProcessNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 350003, + ErrorCode: "CF-ProcessNotFound", + Description: "The process could not be found: %s", + } +} + +// IsProcessNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 350003 +// - HTTP code: 404 +// - message: "The process could not be found: %s" +func IsProcessNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 350003 +} + +// NewServiceKeyNameTakenError returns a new CloudFoundryError +// that IsServiceKeyNameTakenError will return true for +func NewServiceKeyNameTakenError() CloudFoundryError { + return CloudFoundryError{ + Code: 360001, + ErrorCode: "CF-ServiceKeyNameTaken", + Description: "The service key name is taken: %s", + } +} + +// IsServiceKeyNameTakenError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360001 +// - HTTP code: 400 +// - message: "The service key name is taken: %s" +func IsServiceKeyNameTakenError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360001 +} + +// NewServiceKeyInvalidError returns a new CloudFoundryError +// that IsServiceKeyInvalidError will return true for +func NewServiceKeyInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 360002, + ErrorCode: "CF-ServiceKeyInvalid", + Description: "The service key is invalid: %s", + } +} + +// IsServiceKeyInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360002 +// - HTTP code: 400 +// - message: "The service key is invalid: %s" +func IsServiceKeyInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360002 +} + +// NewServiceKeyNotFoundError returns a new CloudFoundryError +// that IsServiceKeyNotFoundError will return true for +func NewServiceKeyNotFoundError() CloudFoundryError { + return CloudFoundryError{ + Code: 360003, + ErrorCode: "CF-ServiceKeyNotFound", + Description: "The service key could not be found: %s", + } +} + +// IsServiceKeyNotFoundError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360003 +// - HTTP code: 404 +// - message: "The service key could not be found: %s" +func IsServiceKeyNotFoundError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360003 +} + +// NewServiceKeyNotSupportedError returns a new CloudFoundryError +// that IsServiceKeyNotSupportedError will return true for +func NewServiceKeyNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 360004, + ErrorCode: "CF-ServiceKeyNotSupported", + Description: "%s", + } +} + +// IsServiceKeyNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360004 +// - HTTP code: 400 +// - message: "%s" +func IsServiceKeyNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360004 +} + +// NewServiceKeyCredentialStoreUnavailableError returns a new CloudFoundryError +// that IsServiceKeyCredentialStoreUnavailableError will return true for +func NewServiceKeyCredentialStoreUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 360005, + ErrorCode: "CF-ServiceKeyCredentialStoreUnavailable", + Description: "Credential store is unavailable", + } +} + +// IsServiceKeyCredentialStoreUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 360005 +// - HTTP code: 503 +// - message: "Credential store is unavailable" +func IsServiceKeyCredentialStoreUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 360005 +} + +// NewRoutingApiUnavailableError returns a new CloudFoundryError +// that IsRoutingApiUnavailableError will return true for +func NewRoutingApiUnavailableError() CloudFoundryError { + return CloudFoundryError{ + Code: 370001, + ErrorCode: "CF-RoutingApiUnavailable", + Description: "The Routing API is currently unavailable", + } +} + +// IsRoutingApiUnavailableError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 370001 +// - HTTP code: 503 +// - message: "The Routing API is currently unavailable" +func IsRoutingApiUnavailableError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 370001 +} + +// NewRoutingApiDisabledError returns a new CloudFoundryError +// that IsRoutingApiDisabledError will return true for +func NewRoutingApiDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 370003, + ErrorCode: "CF-RoutingApiDisabled", + Description: "Routing API is disabled", + } +} + +// IsRoutingApiDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 370003 +// - HTTP code: 403 +// - message: "Routing API is disabled" +func IsRoutingApiDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 370003 +} + +// NewEnvironmentVariableGroupInvalidError returns a new CloudFoundryError +// that IsEnvironmentVariableGroupInvalidError will return true for +func NewEnvironmentVariableGroupInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 380001, + ErrorCode: "CF-EnvironmentVariableGroupInvalid", + Description: "The Environment Variable Group is invalid: %s", + } +} + +// IsEnvironmentVariableGroupInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 380001 +// - HTTP code: 400 +// - message: "The Environment Variable Group is invalid: %s" +func IsEnvironmentVariableGroupInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 380001 +} + +// NewDropletUploadInvalidError returns a new CloudFoundryError +// that IsDropletUploadInvalidError will return true for +func NewDropletUploadInvalidError() CloudFoundryError { + return CloudFoundryError{ + Code: 380002, + ErrorCode: "CF-DropletUploadInvalid", + Description: "The droplet upload is invalid: %s", + } +} + +// IsDropletUploadInvalidError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 380002 +// - HTTP code: 400 +// - message: "The droplet upload is invalid: %s" +func IsDropletUploadInvalidError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 380002 +} + +// NewServiceInstanceUnshareFailedError returns a new CloudFoundryError +// that IsServiceInstanceUnshareFailedError will return true for +func NewServiceInstanceUnshareFailedError() CloudFoundryError { + return CloudFoundryError{ + Code: 390001, + ErrorCode: "CF-ServiceInstanceUnshareFailed", + Description: "Unshare of service instance failed: %s", + } +} + +// IsServiceInstanceUnshareFailedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390001 +// - HTTP code: 502 +// - message: "Unshare of service instance failed: \n\n%s" +func IsServiceInstanceUnshareFailedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390001 +} + +// NewServiceInstanceDeletionSharesExistsError returns a new CloudFoundryError +// that IsServiceInstanceDeletionSharesExistsError will return true for +func NewServiceInstanceDeletionSharesExistsError() CloudFoundryError { + return CloudFoundryError{ + Code: 390002, + ErrorCode: "CF-ServiceInstanceDeletionSharesExists", + Description: "Service instances must be unshared before they can be deleted. Unsharing %s will automatically delete any bindings that have been made to applications in other spaces.", + } +} + +// IsServiceInstanceDeletionSharesExistsError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390002 +// - HTTP code: 422 +// - message: "Service instances must be unshared before they can be deleted. Unsharing %s will automatically delete any bindings that have been made to applications in other spaces." +func IsServiceInstanceDeletionSharesExistsError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390002 +} + +// NewSharedServiceInstanceCannotBeRenamedError returns a new CloudFoundryError +// that IsSharedServiceInstanceCannotBeRenamedError will return true for +func NewSharedServiceInstanceCannotBeRenamedError() CloudFoundryError { + return CloudFoundryError{ + Code: 390003, + ErrorCode: "CF-SharedServiceInstanceCannotBeRenamed", + Description: "Service instances that have been shared cannot be renamed", + } +} + +// IsSharedServiceInstanceCannotBeRenamedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390003 +// - HTTP code: 422 +// - message: "Service instances that have been shared cannot be renamed" +func IsSharedServiceInstanceCannotBeRenamedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390003 +} + +// NewSharedServiceInstanceNotUpdatableInTargetSpaceError returns a new CloudFoundryError +// that IsSharedServiceInstanceNotUpdatableInTargetSpaceError will return true for +func NewSharedServiceInstanceNotUpdatableInTargetSpaceError() CloudFoundryError { + return CloudFoundryError{ + Code: 390004, + ErrorCode: "CF-SharedServiceInstanceNotUpdatableInTargetSpace", + Description: "You cannot update service instances that have been shared with you", + } +} + +// IsSharedServiceInstanceNotUpdatableInTargetSpaceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390004 +// - HTTP code: 403 +// - message: "You cannot update service instances that have been shared with you" +func IsSharedServiceInstanceNotUpdatableInTargetSpaceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390004 +} + +// NewSharedServiceInstanceNotDeletableInTargetSpaceError returns a new CloudFoundryError +// that IsSharedServiceInstanceNotDeletableInTargetSpaceError will return true for +func NewSharedServiceInstanceNotDeletableInTargetSpaceError() CloudFoundryError { + return CloudFoundryError{ + Code: 390005, + ErrorCode: "CF-SharedServiceInstanceNotDeletableInTargetSpace", + Description: "You cannot delete service instances that have been shared with you", + } +} + +// IsSharedServiceInstanceNotDeletableInTargetSpaceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390005 +// - HTTP code: 403 +// - message: "You cannot delete service instances that have been shared with you" +func IsSharedServiceInstanceNotDeletableInTargetSpaceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390005 +} + +// NewMaintenanceInfoNotSupportedError returns a new CloudFoundryError +// that IsMaintenanceInfoNotSupportedError will return true for +func NewMaintenanceInfoNotSupportedError() CloudFoundryError { + return CloudFoundryError{ + Code: 390006, + ErrorCode: "CF-MaintenanceInfoNotSupported", + Description: "The service broker does not support upgrades for service instances created from this plan.", + } +} + +// IsMaintenanceInfoNotSupportedError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390006 +// - HTTP code: 422 +// - message: "The service broker does not support upgrades for service instances created from this plan." +func IsMaintenanceInfoNotSupportedError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390006 +} + +// NewMaintenanceInfoNotSemverError returns a new CloudFoundryError +// that IsMaintenanceInfoNotSemverError will return true for +func NewMaintenanceInfoNotSemverError() CloudFoundryError { + return CloudFoundryError{ + Code: 390007, + ErrorCode: "CF-MaintenanceInfoNotSemver", + Description: "maintenance_info.version should be a semantic version.", + } +} + +// IsMaintenanceInfoNotSemverError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390007 +// - HTTP code: 422 +// - message: "maintenance_info.version should be a semantic version." +func IsMaintenanceInfoNotSemverError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390007 +} + +// NewMaintenanceInfoNotUpdatableWhenChangingPlanError returns a new CloudFoundryError +// that IsMaintenanceInfoNotUpdatableWhenChangingPlanError will return true for +func NewMaintenanceInfoNotUpdatableWhenChangingPlanError() CloudFoundryError { + return CloudFoundryError{ + Code: 390008, + ErrorCode: "CF-MaintenanceInfoNotUpdatableWhenChangingPlan", + Description: "maintenance_info should not be changed when switching to different plan.", + } +} + +// IsMaintenanceInfoNotUpdatableWhenChangingPlanError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390008 +// - HTTP code: 422 +// - message: "maintenance_info should not be changed when switching to different plan." +func IsMaintenanceInfoNotUpdatableWhenChangingPlanError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390008 +} + +// NewMaintenanceInfoConflictError returns a new CloudFoundryError +// that IsMaintenanceInfoConflictError will return true for +func NewMaintenanceInfoConflictError() CloudFoundryError { + return CloudFoundryError{ + Code: 390009, + ErrorCode: "CF-MaintenanceInfoConflict", + Description: "maintenance_info.version requested is invalid. Please ensure the catalog is up to date and you are providing a version supported by this service plan.", + } +} + +// IsMaintenanceInfoConflictError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390009 +// - HTTP code: 422 +// - message: "maintenance_info.version requested is invalid. Please ensure the catalog is up to date and you are providing a version supported by this service plan." +func IsMaintenanceInfoConflictError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390009 +} + +// NewBuildpackStacksDontMatchError returns a new CloudFoundryError +// that IsBuildpackStacksDontMatchError will return true for +func NewBuildpackStacksDontMatchError() CloudFoundryError { + return CloudFoundryError{ + Code: 390011, + ErrorCode: "CF-BuildpackStacksDontMatch", + Description: "Uploaded buildpack stack (%s) does not match %s", + } +} + +// IsBuildpackStacksDontMatchError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390011 +// - HTTP code: 422 +// - message: "Uploaded buildpack stack (%s) does not match %s" +func IsBuildpackStacksDontMatchError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390011 +} + +// NewBuildpackStackDoesNotExistError returns a new CloudFoundryError +// that IsBuildpackStackDoesNotExistError will return true for +func NewBuildpackStackDoesNotExistError() CloudFoundryError { + return CloudFoundryError{ + Code: 390012, + ErrorCode: "CF-BuildpackStackDoesNotExist", + Description: "Uploaded buildpack stack (%s) does not exist", + } +} + +// IsBuildpackStackDoesNotExistError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390012 +// - HTTP code: 422 +// - message: "Uploaded buildpack stack (%s) does not exist" +func IsBuildpackStackDoesNotExistError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390012 +} + +// NewBuildpackZipError returns a new CloudFoundryError +// that IsBuildpackZipError will return true for +func NewBuildpackZipError() CloudFoundryError { + return CloudFoundryError{ + Code: 390013, + ErrorCode: "CF-BuildpackZipError", + Description: "Buildpack zip error: %s", + } +} + +// IsBuildpackZipError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390013 +// - HTTP code: 422 +// - message: "Buildpack zip error: %s" +func IsBuildpackZipError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390013 +} + +// NewDeploymentsDisabledError returns a new CloudFoundryError +// that IsDeploymentsDisabledError will return true for +func NewDeploymentsDisabledError() CloudFoundryError { + return CloudFoundryError{ + Code: 390014, + ErrorCode: "CF-DeploymentsDisabled", + Description: "Deployments cannot be created due to manifest property 'temporary_disable_deployments'", + } +} + +// IsDeploymentsDisabledError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390014 +// - HTTP code: 403 +// - message: "Deployments cannot be created due to manifest property 'temporary_disable_deployments'" +func IsDeploymentsDisabledError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390014 +} + +// NewNoCurrentEncryptionKeyError returns a new CloudFoundryError +// that IsNoCurrentEncryptionKeyError will return true for +func NewNoCurrentEncryptionKeyError() CloudFoundryError { + return CloudFoundryError{ + Code: 390015, + ErrorCode: "CF-NoCurrentEncryptionKey", + Description: "Please set the desired encryption key in the manifest at ‘cc.database_encryption.current_key_label’", + } +} + +// IsNoCurrentEncryptionKeyError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390015 +// - HTTP code: 422 +// - message: "Please set the desired encryption key in the manifest at ‘cc.database_encryption.current_key_label’" +func IsNoCurrentEncryptionKeyError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390015 +} + +// NewScaleDisabledDuringDeploymentError returns a new CloudFoundryError +// that IsScaleDisabledDuringDeploymentError will return true for +func NewScaleDisabledDuringDeploymentError() CloudFoundryError { + return CloudFoundryError{ + Code: 390016, + ErrorCode: "CF-ScaleDisabledDuringDeployment", + Description: "Cannot scale this process while a deployment is in flight.", + } +} + +// IsScaleDisabledDuringDeploymentError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390016 +// - HTTP code: 422 +// - message: "Cannot scale this process while a deployment is in flight." +func IsScaleDisabledDuringDeploymentError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390016 +} + +// NewProcessUpdateDisabledDuringDeploymentError returns a new CloudFoundryError +// that IsProcessUpdateDisabledDuringDeploymentError will return true for +func NewProcessUpdateDisabledDuringDeploymentError() CloudFoundryError { + return CloudFoundryError{ + Code: 390017, + ErrorCode: "CF-ProcessUpdateDisabledDuringDeployment", + Description: "Cannot update this process while a deployment is in flight.", + } +} + +// IsProcessUpdateDisabledDuringDeploymentError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390017 +// - HTTP code: 422 +// - message: "Cannot update this process while a deployment is in flight." +func IsProcessUpdateDisabledDuringDeploymentError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390017 +} + +// NewLabelLimitExceededError returns a new CloudFoundryError +// that IsLabelLimitExceededError will return true for +func NewLabelLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 390020, + ErrorCode: "CF-LabelLimitExceeded", + Description: "Failed to add %d labels because it would exceed maximum of %d", + } +} + +// IsLabelLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390020 +// - HTTP code: 422 +// - message: "Failed to add %d labels because it would exceed maximum of %d" +func IsLabelLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390020 +} + +// NewAnnotationLimitExceededError returns a new CloudFoundryError +// that IsAnnotationLimitExceededError will return true for +func NewAnnotationLimitExceededError() CloudFoundryError { + return CloudFoundryError{ + Code: 390023, + ErrorCode: "CF-AnnotationLimitExceeded", + Description: "Failed to add %d annotations because it would exceed maximum of %d", + } +} + +// IsAnnotationLimitExceededError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390023 +// - HTTP code: 422 +// - message: "Failed to add %d annotations because it would exceed maximum of %d" +func IsAnnotationLimitExceededError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390023 +} + +// NewStopDisabledDuringDeploymentError returns a new CloudFoundryError +// that IsStopDisabledDuringDeploymentError will return true for +func NewStopDisabledDuringDeploymentError() CloudFoundryError { + return CloudFoundryError{ + Code: 390024, + ErrorCode: "CF-StopDisabledDuringDeployment", + Description: "Cannot stop the app while it is deploying, please cancel the deployment before stopping the app.", + } +} + +// IsStopDisabledDuringDeploymentError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 390024 +// - HTTP code: 422 +// - message: "Cannot stop the app while it is deploying, please cancel the deployment before stopping the app." +func IsStopDisabledDuringDeploymentError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 390024 +} + +// NewKubernetesRouteResourceError returns a new CloudFoundryError +// that IsKubernetesRouteResourceError will return true for +func NewKubernetesRouteResourceError() CloudFoundryError { + return CloudFoundryError{ + Code: 400001, + ErrorCode: "CF-KubernetesRouteResourceError", + Description: "Failed to create/update/delete Route resource with guid '%s' on Kubernetes", + } +} + +// IsKubernetesRouteResourceError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 400001 +// - HTTP code: 422 +// - message: "Failed to create/update/delete Route resource with guid '%s' on Kubernetes" +func IsKubernetesRouteResourceError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 400001 +} + +// NewKpackImageError returns a new CloudFoundryError +// that IsKpackImageError will return true for +func NewKpackImageError() CloudFoundryError { + return CloudFoundryError{ + Code: 400002, + ErrorCode: "CF-KpackImageError", + Description: "Failed to %s Image resource for staging: '%s'", + } +} + +// IsKpackImageError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 400002 +// - HTTP code: 422 +// - message: "Failed to %s Image resource for staging: '%s'" +func IsKpackImageError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 400002 +} + +// NewKpackBuilderError returns a new CloudFoundryError +// that IsKpackBuilderError will return true for +func NewKpackBuilderError() CloudFoundryError { + return CloudFoundryError{ + Code: 400003, + ErrorCode: "CF-KpackBuilderError", + Description: "Failed to %s Builder resource: '%s'", + } +} + +// IsKpackBuilderError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 400003 +// - HTTP code: 422 +// - message: "Failed to %s Builder resource: '%s'" +func IsKpackBuilderError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 400003 +} + +// NewEiriniLRPError returns a new CloudFoundryError +// that IsEiriniLRPError will return true for +func NewEiriniLRPError() CloudFoundryError { + return CloudFoundryError{ + Code: 410001, + ErrorCode: "CF-EiriniLRPError", + Description: "Failed to %s LRP resource: '%s'", + } +} + +// IsEiriniLRPError returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: 410001 +// - HTTP code: 422 +// - message: "Failed to %s LRP resource: '%s'" +func IsEiriniLRPError(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == 410001 +} + +func cloudFoundryError(err error) (cferr CloudFoundryError, ok bool) { + type causer interface { + Cause() error + } + if _, isCauser := err.(causer); isCauser { + cause := pkgerrors.Cause(err) + cferr, ok = cause.(CloudFoundryError) + } else { + ok = stderrors.As(err, &cferr) + } + return cferr, ok +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/client.go b/vendor/github.com/cloudfoundry-community/go-cfclient/client.go new file mode 100644 index 0000000..8c9d8c3 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/client.go @@ -0,0 +1,551 @@ +package cfclient + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + + "time" + + "github.com/pkg/errors" + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" +) + +// Client used to communicate with Cloud Foundry +type Client struct { + Config Config + Endpoint Endpoint +} + +type Endpoint struct { + DopplerEndpoint string `json:"doppler_logging_endpoint"` + LoggingEndpoint string `json:"logging_endpoint"` + AuthEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + AppSSHEndpoint string `json:"app_ssh_endpoint"` + AppSSHOauthClient string `json:"app_ssh_oauth_client"` +} + +// Config is used to configure the creation of a client +type Config struct { + ApiAddress string `json:"api_url"` + Username string `json:"user"` + Password string `json:"password"` + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + SkipSslValidation bool `json:"skip_ssl_validation"` + HttpClient *http.Client + Token string `json:"auth_token"` + TokenSource oauth2.TokenSource + tokenSourceDeadline *time.Time + UserAgent string `json:"user_agent"` + Origin string `json:"-"` +} + +type LoginHint struct { + Origin string `json:"origin"` +} + +// Request is used to help build up a request +type Request struct { + method string + url string + params url.Values + body io.Reader + obj interface{} +} + +type cfHomeConfig struct { + AccessToken string + RefreshToken string + Target string + AuthorizationEndpoint string + OrganizationFields struct { + Name string + } + SpaceFields struct { + Name string + } + SSLDisabled bool +} + +func NewConfigFromCF() (*Config, error) { + return NewConfigFromCFHome("") +} + +func NewConfigFromCFHome(cfHomeDir string) (*Config, error) { + var err error + + if cfHomeDir == "" { + cfHomeDir = os.Getenv("CF_HOME") + if cfHomeDir == "" { + cfHomeDir, err = os.UserHomeDir() + if err != nil { + return nil, err + } + } + } + + cfHomeConfig, err := loadCFHomeConfig(cfHomeDir) + if err != nil { + return nil, err + } + + cfg := DefaultConfig() + cfg.Token = cfHomeConfig.AccessToken + cfg.ApiAddress = cfHomeConfig.Target + cfg.SkipSslValidation = cfHomeConfig.SSLDisabled + + return cfg, nil +} + +func loadCFHomeConfig(cfHomeDir string) (*cfHomeConfig, error) { + cfConfigDir := filepath.Join(cfHomeDir, ".cf") + cfJSON, err := ioutil.ReadFile(filepath.Join(cfConfigDir, "config.json")) + if err != nil { + return nil, err + } + + var cfg cfHomeConfig + err = json.Unmarshal(cfJSON, &cfg) + if err == nil { + if len(cfg.AccessToken) > len("bearer ") { + cfg.AccessToken = cfg.AccessToken[len("bearer "):] + } + } + + return &cfg, nil +} + +// DefaultConfig creates a default config object used by CF client +func DefaultConfig() *Config { + return &Config{ + ApiAddress: "http://api.bosh-lite.com", + Username: "admin", + Password: "admin", + Token: "", + SkipSslValidation: false, + HttpClient: http.DefaultClient, + UserAgent: "Go-CF-client/1.1", + } +} + +func DefaultEndpoint() *Endpoint { + return &Endpoint{ + DopplerEndpoint: "wss://doppler.10.244.0.34.xip.io:443", + LoggingEndpoint: "wss://loggregator.10.244.0.34.xip.io:443", + TokenEndpoint: "https://uaa.10.244.0.34.xip.io", + AuthEndpoint: "https://login.10.244.0.34.xip.io", + } +} + +// NewClient returns a new client +func NewClient(config *Config) (client *Client, err error) { + // bootstrap the config + defConfig := DefaultConfig() + + if len(config.ApiAddress) == 0 { + config.ApiAddress = defConfig.ApiAddress + } + + if len(config.Username) == 0 { + config.Username = defConfig.Username + } + + if len(config.Password) == 0 { + config.Password = defConfig.Password + } + + if len(config.Token) == 0 { + config.Token = defConfig.Token + } + + if len(config.UserAgent) == 0 { + config.UserAgent = defConfig.UserAgent + } + + if config.HttpClient == nil { + config.HttpClient = defConfig.HttpClient + } + + if config.HttpClient.Transport == nil { + config.HttpClient.Transport = shallowDefaultTransport() + } + + var tp *http.Transport + + switch t := config.HttpClient.Transport.(type) { + case *http.Transport: + tp = t + case *oauth2.Transport: + if bt, ok := t.Base.(*http.Transport); ok { + tp = bt + } + } + + if tp != nil { + if tp.TLSClientConfig == nil { + tp.TLSClientConfig = &tls.Config{} + } + tp.TLSClientConfig.InsecureSkipVerify = config.SkipSslValidation + } + + config.ApiAddress = strings.TrimRight(config.ApiAddress, "/") + + client = &Client{ + Config: *config, + } + + if err := client.refreshEndpoint(); err != nil { + return nil, err + } + + return client, nil +} + +func shallowDefaultTransport() *http.Transport { + defaultTransport := http.DefaultTransport.(*http.Transport) + return &http.Transport{ + Proxy: defaultTransport.Proxy, + TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout, + ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout, + } +} + +func getUserAuth(ctx context.Context, config Config, endpoint *Endpoint) (Config, error) { + authConfig := &oauth2.Config{ + ClientID: "cf", + Scopes: []string{""}, + Endpoint: oauth2.Endpoint{ + AuthURL: endpoint.AuthEndpoint + "/oauth/auth", + TokenURL: endpoint.TokenEndpoint + "/oauth/token", + }, + } + if config.Origin != "" { + loginHint := LoginHint{config.Origin} + origin, err := json.Marshal(loginHint) + if err != nil { + return config, errors.Wrap(err, "Error creating login_hint") + } + val := url.Values{} + val.Set("login_hint", string(origin)) + authConfig.Endpoint.TokenURL = fmt.Sprintf("%s?%s", authConfig.Endpoint.TokenURL, val.Encode()) + } + + token, err := authConfig.PasswordCredentialsToken(ctx, config.Username, config.Password) + if err != nil { + return config, errors.Wrap(err, "Error getting token") + } + + config.tokenSourceDeadline = &token.Expiry + config.TokenSource = authConfig.TokenSource(ctx, token) + config.HttpClient = oauth2.NewClient(ctx, config.TokenSource) + + return config, err +} + +func getClientAuth(ctx context.Context, config Config, endpoint *Endpoint) Config { + authConfig := &clientcredentials.Config{ + ClientID: config.ClientID, + ClientSecret: config.ClientSecret, + TokenURL: endpoint.TokenEndpoint + "/oauth/token", + } + + config.TokenSource = authConfig.TokenSource(ctx) + config.HttpClient = authConfig.Client(ctx) + return config +} + +// getUserTokenAuth initializes client credentials from existing bearer token. +func getUserTokenAuth(ctx context.Context, config Config, endpoint *Endpoint) Config { + authConfig := &oauth2.Config{ + ClientID: "cf", + Scopes: []string{""}, + Endpoint: oauth2.Endpoint{ + AuthURL: endpoint.AuthEndpoint + "/oauth/auth", + TokenURL: endpoint.TokenEndpoint + "/oauth/token", + }, + } + + // Token is expected to have no "bearer" prefix + token := &oauth2.Token{ + AccessToken: config.Token, + TokenType: "Bearer"} + + config.TokenSource = authConfig.TokenSource(ctx, token) + config.HttpClient = oauth2.NewClient(ctx, config.TokenSource) + + return config +} + +func getInfo(api string, httpClient *http.Client) (*Endpoint, error) { + var endpoint Endpoint + + if api == "" { + return DefaultEndpoint(), nil + } + + resp, err := httpClient.Get(api + "/v2/info") + if err != nil { + return nil, err + } + defer resp.Body.Close() + + err = decodeBody(resp, &endpoint) + if err != nil { + return nil, err + } + + return &endpoint, err +} + +// NewRequest is used to create a new Request +func (c *Client) NewRequest(method, path string) *Request { + r := &Request{ + method: method, + url: c.Config.ApiAddress + path, + params: make(map[string][]string), + } + return r +} + +// NewRequestWithBody is used to create a new request with +// arbigtrary body io.Reader. +func (c *Client) NewRequestWithBody(method, path string, body io.Reader) *Request { + r := c.NewRequest(method, path) + + // Set request body + r.body = body + + return r +} + +// DoRequest runs a request with our client +func (c *Client) DoRequest(r *Request) (*http.Response, error) { + req, err := r.toHTTP() + if err != nil { + return nil, err + } + return c.Do(req) +} + +// DoRequestWithoutRedirects executes the request without following redirects +func (c *Client) DoRequestWithoutRedirects(r *Request) (*http.Response, error) { + prevCheckRedirect := c.Config.HttpClient.CheckRedirect + c.Config.HttpClient.CheckRedirect = func(httpReq *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + defer func() { + c.Config.HttpClient.CheckRedirect = prevCheckRedirect + }() + return c.DoRequest(r) +} + +func (c *Client) Do(req *http.Request) (*http.Response, error) { + req.Header.Set("User-Agent", c.Config.UserAgent) + if req.Body != nil && req.Header.Get("Content-type") == "" { + req.Header.Set("Content-type", "application/json") + } + + resp, err := c.Config.HttpClient.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode >= http.StatusBadRequest { + return c.handleError(resp) + } + + return resp, nil +} + +func (c *Client) handleError(resp *http.Response) (*http.Response, error) { + body, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return resp, CloudFoundryHTTPError{ + StatusCode: resp.StatusCode, + Status: resp.Status, + Body: body, + } + } + defer resp.Body.Close() + + // Unmarshal V2 error response + if strings.HasPrefix(resp.Request.URL.Path, "/v2/") { + var cfErr CloudFoundryError + if err := json.Unmarshal(body, &cfErr); err != nil { + return resp, CloudFoundryHTTPError{ + StatusCode: resp.StatusCode, + Status: resp.Status, + Body: body, + } + } + return nil, cfErr + } + + // Unmarshal a V3 error response and convert it into a V2 model + var cfErrorsV3 CloudFoundryErrorsV3 + if err := json.Unmarshal(body, &cfErrorsV3); err != nil { + return resp, CloudFoundryHTTPError{ + StatusCode: resp.StatusCode, + Status: resp.Status, + Body: body, + } + } + return nil, NewCloudFoundryErrorFromV3Errors(cfErrorsV3) +} + +func (c *Client) refreshEndpoint() error { + // we want to keep the Timeout value from config.HttpClient + timeout := c.Config.HttpClient.Timeout + + ctx := context.Background() + ctx = context.WithValue(ctx, oauth2.HTTPClient, c.Config.HttpClient) + + endpoint, err := getInfo(c.Config.ApiAddress, oauth2.NewClient(ctx, nil)) + + if err != nil { + return errors.Wrap(err, "Could not get api /v2/info") + } + + switch { + case c.Config.Token != "": + c.Config = getUserTokenAuth(ctx, c.Config, endpoint) + case c.Config.ClientID != "": + c.Config = getClientAuth(ctx, c.Config, endpoint) + default: + c.Config, err = getUserAuth(ctx, c.Config, endpoint) + if err != nil { + return err + } + } + // make sure original Timeout value will be used + if c.Config.HttpClient.Timeout != timeout { + c.Config.HttpClient.Timeout = timeout + } + + c.Endpoint = *endpoint + return nil +} + +// toHTTP converts the request to an HTTP Request +func (r *Request) toHTTP() (*http.Request, error) { + + // Check if we should encode the body + if r.body == nil && r.obj != nil { + b, err := encodeBody(r.obj) + if err != nil { + return nil, err + } + r.body = b + } + + // Create the HTTP Request + return http.NewRequest(r.method, r.url, r.body) +} + +// decodeBody is used to JSON decode a body +func decodeBody(resp *http.Response, out interface{}) error { + defer resp.Body.Close() + dec := json.NewDecoder(resp.Body) + return dec.Decode(out) +} + +// encodeBody is used to encode a request body +func encodeBody(obj interface{}) (io.Reader, error) { + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(obj); err != nil { + return nil, err + } + return buf, nil +} + +func (c *Client) GetToken() (string, error) { + if c.Config.tokenSourceDeadline != nil && c.Config.tokenSourceDeadline.Before(time.Now()) { + if err := c.refreshEndpoint(); err != nil { + return "", err + } + } + + token, err := c.Config.TokenSource.Token() + if err != nil { + return "", errors.Wrap(err, "Error getting bearer token") + } + return "bearer " + token.AccessToken, nil +} + +var ErrPreventRedirect = errors.New("prevent-redirect") + +func (c *Client) GetSSHCode() (string, error) { + authorizeUrl, err := url.Parse(c.Endpoint.TokenEndpoint) + if err != nil { + return "", err + } + + values := url.Values{} + values.Set("response_type", "code") + values.Set("grant_type", "authorization_code") + values.Set("client_id", c.Endpoint.AppSSHOauthClient) // client_id,used by cf server + + authorizeUrl.Path = "/oauth/authorize" + authorizeUrl.RawQuery = values.Encode() + + req, err := http.NewRequest("GET", authorizeUrl.String(), nil) + if err != nil { + return "", err + } + + token, err := c.GetToken() + if err != nil { + return "", err + } + + req.Header.Add("authorization", token) + httpClient := &http.Client{ + CheckRedirect: func(req *http.Request, _ []*http.Request) error { + return ErrPreventRedirect + }, + Timeout: 30 * time.Second, + Transport: &http.Transport{ + DisableKeepAlives: true, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: c.Config.SkipSslValidation, + }, + Proxy: http.ProxyFromEnvironment, + TLSHandshakeTimeout: 10 * time.Second, + }, + } + + resp, err := httpClient.Do(req) + if err == nil { + return "", errors.New("authorization server did not redirect with one time code") + } + defer resp.Body.Close() + if netErr, ok := err.(*url.Error); !ok || netErr.Err != ErrPreventRedirect { + return "", errors.New(fmt.Sprintf("error requesting one time code from server: %s", err.Error())) + } + + loc, err := resp.Location() + if err != nil { + return "", errors.New(fmt.Sprintf("error getting the redirected location: %s", err.Error())) + } + + codes := loc.Query()["code"] + if len(codes) != 1 { + return "", errors.New("unable to acquire one time code from authorization response") + } + + return codes[0], nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/domains.go b/vendor/github.com/cloudfoundry-community/go-cfclient/domains.go new file mode 100644 index 0000000..b33e767 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/domains.go @@ -0,0 +1,314 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/pkg/errors" +) + +type DomainsResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []DomainResource `json:"resources"` +} + +type SharedDomainsResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SharedDomainResource `json:"resources"` +} + +type DomainResource struct { + Meta Meta `json:"metadata"` + Entity Domain `json:"entity"` +} + +type SharedDomainResource struct { + Meta Meta `json:"metadata"` + Entity SharedDomain `json:"entity"` +} + +type Domain struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + OwningOrganizationGuid string `json:"owning_organization_guid"` + OwningOrganizationUrl string `json:"owning_organization_url"` + SharedOrganizationsUrl string `json:"shared_organizations_url"` + c *Client +} + +type SharedDomain struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + RouterGroupGuid string `json:"router_group_guid"` + RouterGroupType string `json:"router_group_type"` + Internal bool `json:"internal"` + c *Client +} + +func (c *Client) ListDomainsByQuery(query url.Values) ([]Domain, error) { + var domains []Domain + requestURL := "/v2/private_domains?" + query.Encode() + for { + var domainResp DomainsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting domains") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading domains request") + } + + err = json.Unmarshal(resBody, &domainResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling domains") + } + for _, domain := range domainResp.Resources { + domain.Entity.Guid = domain.Meta.Guid + domain.Entity.CreatedAt = domain.Meta.CreatedAt + domain.Entity.UpdatedAt = domain.Meta.UpdatedAt + domain.Entity.c = c + domains = append(domains, domain.Entity) + } + requestURL = domainResp.NextUrl + if requestURL == "" { + break + } + } + return domains, nil +} + +func (c *Client) ListDomains() ([]Domain, error) { + return c.ListDomainsByQuery(nil) +} + +func (c *Client) ListSharedDomainsByQuery(query url.Values) ([]SharedDomain, error) { + var domains []SharedDomain + requestURL := "/v2/shared_domains?" + query.Encode() + for { + var domainResp SharedDomainsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting shared domains") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading shared domains request") + } + + err = json.Unmarshal(resBody, &domainResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling shared domains") + } + for _, domain := range domainResp.Resources { + domain.Entity.Guid = domain.Meta.Guid + domain.Entity.CreatedAt = domain.Meta.CreatedAt + domain.Entity.UpdatedAt = domain.Meta.UpdatedAt + domain.Entity.c = c + domains = append(domains, domain.Entity) + } + requestURL = domainResp.NextUrl + if requestURL == "" { + break + } + } + return domains, nil +} + +func (c *Client) ListSharedDomains() ([]SharedDomain, error) { + return c.ListSharedDomainsByQuery(nil) +} + +func (c *Client) GetSharedDomainByGuid(guid string) (SharedDomain, error) { + r := c.NewRequest("GET", "/v2/shared_domains/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return SharedDomain{}, errors.Wrap(err, "Error requesting shared domain") + } + defer resp.Body.Close() + retval, err := c.handleSharedDomainResp(resp) + return *retval, err +} + +func (c *Client) CreateSharedDomain(name string, internal bool, router_group_guid string) (*SharedDomain, error) { + req := c.NewRequest("POST", "/v2/shared_domains") + params := map[string]interface{}{ + "name": name, + "internal": internal, + } + + if strings.TrimSpace(router_group_guid) != "" { + params["router_group_guid"] = router_group_guid + } + + req.obj = params + + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, errors.Wrapf(err, "Error creating shared domain %s, response code: %d", name, resp.StatusCode) + } + return c.handleSharedDomainResp(resp) +} + +func (c *Client) DeleteSharedDomain(guid string, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/shared_domains/%s?async=%t", guid, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if (async && (resp.StatusCode != http.StatusAccepted)) || (!async && (resp.StatusCode != http.StatusNoContent)) { + return errors.Wrapf(err, "Error deleting organization %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) GetDomainByName(name string) (Domain, error) { + q := url.Values{} + q.Set("q", "name:"+name) + domains, err := c.ListDomainsByQuery(q) + if err != nil { + return Domain{}, errors.Wrapf(err, "Error during domain lookup %s", name) + } + if len(domains) == 0 { + cfErr := NewDomainNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return Domain{}, cfErr + } + return domains[0], nil +} + +func (c *Client) GetDomainByGuid(guid string) (Domain, error) { + r := c.NewRequest("GET", "/v2/private_domains/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return Domain{}, errors.Wrap(err, "Error requesting private domain") + } + defer resp.Body.Close() + retval, err := c.handleDomainResp(resp) + return *retval, err +} + +func (c *Client) GetSharedDomainByName(name string) (SharedDomain, error) { + q := url.Values{} + q.Set("q", "name:"+name) + domains, err := c.ListSharedDomainsByQuery(q) + if err != nil { + return SharedDomain{}, errors.Wrapf(err, "Error during shared domain lookup %s", name) + } + if len(domains) == 0 { + cfErr := NewDomainNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return SharedDomain{}, cfErr + } + return domains[0], nil +} + +func (c *Client) CreateDomain(name, orgGuid string) (*Domain, error) { + req := c.NewRequest("POST", "/v2/private_domains") + req.obj = map[string]interface{}{ + "name": name, + "owning_organization_guid": orgGuid, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusCreated { + return nil, errors.Wrapf(err, "Error creating domain %s, response code: %d", name, resp.StatusCode) + } + return c.handleDomainResp(resp) +} + +func (c *Client) DeleteDomain(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/private_domains/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting domain %s, response code: %d", guid, resp.StatusCode) + } + return nil +} +func (c *Client) handleDomainResp(resp *http.Response) (*Domain, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var domainResource DomainResource + err = json.Unmarshal(body, &domainResource) + if err != nil { + return nil, err + } + return c.mergeDomainResource(domainResource), nil +} + +func (c *Client) handleSharedDomainResp(resp *http.Response) (*SharedDomain, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var domainResource SharedDomainResource + err = json.Unmarshal(body, &domainResource) + if err != nil { + return nil, err + } + return c.mergeSharedDomainResource(domainResource), nil +} + +func (c *Client) getDomainsResponse(requestURL string) (DomainsResponse, error) { + var domainResp DomainsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return DomainsResponse{}, errors.Wrap(err, "Error requesting domains") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return DomainsResponse{}, errors.Wrap(err, "Error reading domains request") + } + err = json.Unmarshal(resBody, &domainResp) + if err != nil { + return DomainsResponse{}, errors.Wrap(err, "Error unmarshalling org") + } + return domainResp, nil +} + +func (c *Client) mergeDomainResource(domainResource DomainResource) *Domain { + domainResource.Entity.Guid = domainResource.Meta.Guid + domainResource.Entity.c = c + domainResource.Entity.CreatedAt = domainResource.Meta.CreatedAt + domainResource.Entity.UpdatedAt = domainResource.Meta.UpdatedAt + return &domainResource.Entity +} + +func (c *Client) mergeSharedDomainResource(domainResource SharedDomainResource) *SharedDomain { + domainResource.Entity.Guid = domainResource.Meta.Guid + domainResource.Entity.c = c + domainResource.Entity.CreatedAt = domainResource.Meta.CreatedAt + domainResource.Entity.UpdatedAt = domainResource.Meta.UpdatedAt + return &domainResource.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/environmentvariablegroups.go b/vendor/github.com/cloudfoundry-community/go-cfclient/environmentvariablegroups.go new file mode 100644 index 0000000..4d8da18 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/environmentvariablegroups.go @@ -0,0 +1,64 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" +) + +type EnvironmentVariableGroup map[string]interface{} + +func (c *Client) GetRunningEnvironmentVariableGroup() (EnvironmentVariableGroup, error) { + return c.getEnvironmentVariableGroup(true) +} + +func (c *Client) GetStagingEnvironmentVariableGroup() (EnvironmentVariableGroup, error) { + return c.getEnvironmentVariableGroup(false) +} + +func (c *Client) getEnvironmentVariableGroup(running bool) (EnvironmentVariableGroup, error) { + evgType := "staging" + if running { + evgType = "running" + } + + req := c.NewRequest("GET", fmt.Sprintf("/v2/config/environment_variable_groups/%s", evgType)) + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + evg := EnvironmentVariableGroup{} + err = json.NewDecoder(resp.Body).Decode(&evg) + return evg, err +} + +func (c *Client) SetRunningEnvironmentVariableGroup(evg EnvironmentVariableGroup) error { + return c.setEnvironmentVariableGroup(evg, true) +} + +func (c *Client) SetStagingEnvironmentVariableGroup(evg EnvironmentVariableGroup) error { + return c.setEnvironmentVariableGroup(evg, false) +} + +func (c *Client) setEnvironmentVariableGroup(evg EnvironmentVariableGroup, running bool) error { + evgType := "staging" + if running { + evgType = "running" + } + + marshalled, err := json.Marshal(evg) + if err != nil { + return err + } + + req := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/config/environment_variable_groups/%s", evgType), bytes.NewBuffer(marshalled)) + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/error.go b/vendor/github.com/cloudfoundry-community/go-cfclient/error.go new file mode 100644 index 0000000..e4decdf --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/error.go @@ -0,0 +1,54 @@ +package cfclient + +//go:generate go run gen_error.go + +import ( + "fmt" +) + +type CloudFoundryError struct { + Code int `json:"code"` + ErrorCode string `json:"error_code"` + Description string `json:"description"` +} + +type CloudFoundryErrorsV3 struct { + Errors []CloudFoundryErrorV3 `json:"errors"` +} + +type CloudFoundryErrorV3 struct { + Code int `json:"code"` + Title string `json:"title"` + Detail string `json:"detail"` +} + +// CF APIs v3 can return multiple errors, we take the first one and convert it into a V2 model +func NewCloudFoundryErrorFromV3Errors(cfErrorsV3 CloudFoundryErrorsV3) CloudFoundryError { + if len(cfErrorsV3.Errors) == 0 { + return CloudFoundryError{ + 0, + "GO-Client-No-Errors", + "No Errors in response from V3", + } + } + + return CloudFoundryError{ + cfErrorsV3.Errors[0].Code, + cfErrorsV3.Errors[0].Title, + cfErrorsV3.Errors[0].Detail, + } +} + +func (cfErr CloudFoundryError) Error() string { + return fmt.Sprintf("cfclient error (%s|%d): %s", cfErr.ErrorCode, cfErr.Code, cfErr.Description) +} + +type CloudFoundryHTTPError struct { + StatusCode int + Status string + Body []byte +} + +func (e CloudFoundryHTTPError) Error() string { + return fmt.Sprintf("cfclient: HTTP error (%d): %s", e.StatusCode, e.Status) +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/events.go b/vendor/github.com/cloudfoundry-community/go-cfclient/events.go new file mode 100644 index 0000000..6c42c56 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/events.go @@ -0,0 +1,95 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/url" + + "github.com/pkg/errors" +) + +// EventsResponse is a type that wraps a collection of event resources. +type EventsResponse struct { + TotalResults int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []EventResource `json:"resources"` +} + +// EventResource is a type that contains metadata and the entity for an event. +type EventResource struct { + Meta Meta `json:"metadata"` + Entity Event `json:"entity"` +} + +// Event is a type that contains event data. +type Event struct { + GUID string `json:"guid"` + Type string `json:"type"` + CreatedAt string `json:"created_at"` + Actor string `json:"actor"` + ActorType string `json:"actor_type"` + ActorName string `json:"actor_name"` + ActorUsername string `json:"actor_username"` + Actee string `json:"actee"` + ActeeType string `json:"actee_type"` + ActeeName string `json:"actee_name"` + OrganizationGUID string `json:"organization_guid"` + SpaceGUID string `json:"space_guid"` + Metadata map[string]interface{} `json:"metadata"` + c *Client +} + +// ListEventsByQuery lists all events matching the provided query. +func (c *Client) ListEventsByQuery(query url.Values) ([]Event, error) { + var events []Event + requestURL := fmt.Sprintf("/v2/events?%s", query.Encode()) + for { + var eventResp EventsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "error requesting events") + } + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&eventResp); err != nil { + return nil, errors.Wrap(err, "error unmarshaling events") + } + for _, e := range eventResp.Resources { + e.Entity.GUID = e.Meta.Guid + e.Entity.CreatedAt = e.Meta.CreatedAt + e.Entity.c = c + events = append(events, e.Entity) + } + requestURL = eventResp.NextURL + if requestURL == "" { + break + } + } + return events, nil +} + +// ListEvents lists all unfiltered events. +func (c *Client) ListEvents() ([]Event, error) { + return c.ListEventsByQuery(nil) +} + +// TotalEventsByQuery returns the number of events matching the provided query. +func (c *Client) TotalEventsByQuery(query url.Values) (int, error) { + r := c.NewRequest("GET", fmt.Sprintf("/v2/events?%s", query.Encode())) + resp, err := c.DoRequest(r) + if err != nil { + return 0, errors.Wrap(err, "error requesting events") + } + defer resp.Body.Close() + var apiResp EventsResponse + if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { + return 0, errors.Wrap(err, "error unmarshaling events") + } + return apiResp.TotalResults, nil +} + +// TotalEvents returns the number of unfiltered events. +func (c *Client) TotalEvents() (int, error) { + return c.TotalEventsByQuery(nil) +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/gen_error.go b/vendor/github.com/cloudfoundry-community/go-cfclient/gen_error.go new file mode 100644 index 0000000..2ffcbad --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/gen_error.go @@ -0,0 +1,151 @@ +//go:build tools +// +build tools + +package main + +import ( + "bytes" + "go/format" + "io/ioutil" + "log" + "net/http" + "sort" + "strings" + "text/template" + "time" + + "gopkg.in/yaml.v2" +) + +type ( + CFCode int + HTTPCode int +) + +type Definition struct { + CFCode `yaml:"-"` + Name string `yaml:"name"` + HTTPCode `yaml:"http_code"` + Message string `yaml:"message"` +} + +func main() { + log.SetFlags(log.Lshortfile) + const url = "https://raw.githubusercontent.com/cloudfoundry/cloud_controller_ng/master/errors/v2.yml" + resp, err := http.Get(url) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + + var m map[CFCode]Definition + + if err := yaml.Unmarshal(body, &m); err != nil { + log.Fatal(err) + } + + var definitions []Definition + + for c, d := range m { + d.CFCode = c + definitions = append(definitions, d) + } + + sort.Slice(definitions, func(i, j int) bool { + return definitions[i].CFCode < definitions[j].CFCode + }) + + buf := &bytes.Buffer{} + + if err := packageTemplate.Execute(buf, struct { + Timestamp time.Time + Definitions []Definition + }{ + Timestamp: time.Now(), + Definitions: definitions, + }); err != nil { + log.Fatal(err) + } + + dst, err := format.Source(buf.Bytes()) + if err != nil { + log.Printf("%s", buf.Bytes()) + log.Fatal(err) + } + + if err := ioutil.WriteFile("cf_error.go", dst, 0600); err != nil { + log.Fatal(err) + } +} + +// destutter ensures that s does not end in "Error". +func destutter(s string) string { + return strings.TrimSuffix(s, "Error") +} + +// cleanMessage removes any characters which will cause go generate to fail +func cleanMessage(s string) string { + return strings.Replace(s, "\n", "", -1) +} + +var packageTemplate = template.Must(template.New("").Funcs(template.FuncMap{ + "destutter": destutter, + "cleanMessage": cleanMessage, +}).Parse(` +package cfclient + +// Code generated by go generate. DO NOT EDIT. +// This file was generated by robots at +// {{ .Timestamp }} + +import ( + stderrors "errors" + + pkgerrors "github.com/pkg/errors" +) + +{{- range .Definitions }} +{{$isMethod := printf "Is%sError" (.Name | destutter) }} +{{$newMethod := printf "New%sError" (.Name | destutter) }} +// {{ $newMethod }} returns a new CloudFoundryError +// that {{ $isMethod }} will return true for +func {{ $newMethod }}() CloudFoundryError { + return CloudFoundryError{ + Code: {{ .CFCode }}, + ErrorCode: "CF-{{ .Name }}", + Description: "{{ .Message | cleanMessage }}", + } +} + +// {{ $isMethod }} returns a boolean indicating whether +// the error is known to report the Cloud Foundry error: +// - Cloud Foundry code: {{ .CFCode }} +// - HTTP code: {{ .HTTPCode }} +// - message: {{ printf "%q" .Message }} +func Is{{ .Name | destutter }}Error(err error) bool { + cferr, ok := cloudFoundryError(err) + if !ok { + return false + } + return cferr.Code == {{ .CFCode }} +} +{{- end }} + +func cloudFoundryError(err error) (cferr CloudFoundryError, ok bool) { + type causer interface { + Cause() error + } + if _, isCauser := err.(causer); isCauser { + cause := pkgerrors.Cause(err) + cferr, ok = cause.(CloudFoundryError) + } else { + ok = stderrors.As(err, &cferr) + } + return cferr, ok +} +`)) diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/go.mod b/vendor/github.com/cloudfoundry-community/go-cfclient/go.mod new file mode 100644 index 0000000..8ad3106 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/go.mod @@ -0,0 +1,19 @@ +module github.com/cloudfoundry-community/go-cfclient + +go 1.15 + +require ( + github.com/Masterminds/semver v1.4.2 + github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect + github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 + github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 // indirect + github.com/pkg/errors v0.8.1 + github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 + golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1 + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/go.sum b/vendor/github.com/cloudfoundry-community/go-cfclient/go.sum new file mode 100644 index 0000000..215107b --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/go.sum @@ -0,0 +1,55 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 h1:YFh+sjyJTMQSYjKwM4dFKhJPJC/wfo98tPUc17HdoYw= +github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI= +github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= +github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1 h1:VeAkjQVzKLmu+JnFcK96TPbkuaTIqwGGAzQ9hgwPjVg= +golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/info.go b/vendor/github.com/cloudfoundry-community/go-cfclient/info.go new file mode 100644 index 0000000..5e204d4 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/info.go @@ -0,0 +1,104 @@ +package cfclient + +import ( + "encoding/json" + + "github.com/Masterminds/semver" + "github.com/pkg/errors" +) + +// Info is metadata about a Cloud Foundry deployment +type Info struct { + Name string `json:"name"` + Build string `json:"build"` + Support string `json:"support"` + Version int `json:"version"` + Description string `json:"description"` + AuthorizationEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + MinCLIVersion string `json:"min_cli_version"` + MinRecommendedCLIVersion string `json:"min_recommended_cli_version"` + APIVersion string `json:"api_version"` + AppSSHEndpoint string `json:"app_ssh_endpoint"` + AppSSHHostKeyFingerprint string `json:"app_ssh_host_key_fingerprint"` + AppSSHOauthClient string `json:"app_ssh_oauth_client"` + DopplerLoggingEndpoint string `json:"doppler_logging_endpoint"` + RoutingEndpoint string `json:"routing_endpoint"` + User string `json:"user,omitempty"` +} + +type V3Version struct { + Links struct { + CCV3 struct { + Meta struct { + Version string `json:"version"` + } `json:"meta"` + } `json:"cloud_controller_v3"` + } `json:"links"` +} + +// GetInfo retrieves Info from the Cloud Controller API +func (c *Client) GetInfo() (*Info, error) { + r := c.NewRequest("GET", "/v2/info") + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting info") + } + defer resp.Body.Close() + var i Info + err = json.NewDecoder(resp.Body).Decode(&i) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling info") + } + return &i, nil +} + +func (c *Client) SupportsMetadataAPI() (bool, error) { + r := c.NewRequest("GET", "/") + resp, err := c.DoRequest(r) + if err != nil { + return false, errors.Wrap(err, "Error requesting info") + } + defer resp.Body.Close() + var v3 V3Version + err = json.NewDecoder(resp.Body).Decode(&v3) + if err != nil { + return false, errors.Wrap(err, "Error unmarshalling info") + } + + minimumSupportedVersion := semver.MustParse("3.66.0") + actualVersion, err := semver.NewVersion(v3.Links.CCV3.Meta.Version) + if err != nil { + return false, errors.Wrap(err, "Error parsing semver") + } + if !actualVersion.LessThan(minimumSupportedVersion) { + return true, nil + } + + return false, nil +} + +func (c *Client) SupportsSpaceSupporterRole() (bool, error) { + r := c.NewRequest("GET", "/") + resp, err := c.DoRequest(r) + if err != nil { + return false, errors.Wrap(err, "Error requesting info") + } + defer resp.Body.Close() + var v3 V3Version + err = json.NewDecoder(resp.Body).Decode(&v3) + if err != nil { + return false, errors.Wrap(err, "Error unmarshalling info") + } + + minimumSupportedVersion := semver.MustParse("3.102.0") + actualVersion, err := semver.NewVersion(v3.Links.CCV3.Meta.Version) + if err != nil { + return false, errors.Wrap(err, "Error parsing semver") + } + if !actualVersion.LessThan(minimumSupportedVersion) { + return true, nil + } + + return false, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/isolationsegments.go b/vendor/github.com/cloudfoundry-community/go-cfclient/isolationsegments.go new file mode 100644 index 0000000..5635df6 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/isolationsegments.go @@ -0,0 +1,263 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +type IsolationSegment struct { + GUID string `json:"guid"` + Name string `json:"name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + c *Client +} + +type IsolationSegementResponse struct { + GUID string `json:"guid"` + Name string `json:"name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Links struct { + Self struct { + Href string `json:"href"` + } `json:"self"` + Spaces struct { + Href string `json:"href"` + } `json:"spaces"` + Organizations struct { + Href string `json:"href"` + } `json:"organizations"` + } `json:"links"` +} + +type ListIsolationSegmentsResponse struct { + Pagination Pagination `json:"pagination"` + Resources []IsolationSegementResponse `json:"resources"` +} + +func (c *Client) CreateIsolationSegment(name string) (*IsolationSegment, error) { + req := c.NewRequest("POST", "/v3/isolation_segments") + req.obj = map[string]interface{}{ + "name": name, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating isolation segment") + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating isolation segment %s, response code: %d", name, resp.StatusCode) + } + return respBodyToIsolationSegment(resp.Body, c) +} + +func respBodyToIsolationSegment(body io.ReadCloser, c *Client) (*IsolationSegment, error) { + bodyRaw, err := ioutil.ReadAll(body) + if err != nil { + return nil, err + } + isr := IsolationSegementResponse{} + err = json.Unmarshal(bodyRaw, &isr) + if err != nil { + return nil, err + } + + return &IsolationSegment{ + GUID: isr.GUID, + Name: isr.Name, + CreatedAt: isr.CreatedAt, + UpdatedAt: isr.UpdatedAt, + c: c, + }, nil +} + +func (c *Client) GetIsolationSegmentByGUID(guid string) (*IsolationSegment, error) { + var isr IsolationSegementResponse + r := c.NewRequest("GET", "/v3/isolation_segments/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting isolation segment by GUID") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading isolation segment response body") + } + + err = json.Unmarshal(resBody, &isr) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling isolation segment response") + } + return &IsolationSegment{Name: isr.Name, GUID: isr.GUID, CreatedAt: isr.CreatedAt, UpdatedAt: isr.UpdatedAt, c: c}, nil +} + +func (c *Client) ListIsolationSegmentsByQuery(query url.Values) ([]IsolationSegment, error) { + var iss []IsolationSegment + requestURL := "/v3/isolation_segments" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + for { + var isr ListIsolationSegmentsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting isolation segments") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading isolation segment request") + } + + err = json.Unmarshal(resBody, &isr) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling isolation segment") + } + + for _, is := range isr.Resources { + iss = append(iss, IsolationSegment{ + Name: is.Name, + GUID: is.GUID, + CreatedAt: is.CreatedAt, + UpdatedAt: is.UpdatedAt, + c: c, + }) + } + + requestURL = isr.Pagination.Next.Href + if requestURL == "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, err + } + } + return iss, nil +} + +func (c *Client) ListIsolationSegments() ([]IsolationSegment, error) { + return c.ListIsolationSegmentsByQuery(nil) +} + +// TODO listOrgsForIsolationSegments +// TODO listSpacesForIsolationSegments +// TODO setDefaultIsolationSegmentForOrg + +func (c *Client) DeleteIsolationSegmentByGUID(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v3/isolation_segments/%s", guid))) + if err != nil { + return errors.Wrap(err, "Error during sending DELETE request for isolation segments") + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("Error deleting isolation segment %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (i *IsolationSegment) Delete() error { + return i.c.DeleteIsolationSegmentByGUID(i.GUID) +} + +func (c *Client) AddIsolationSegmentToOrg(isolationSegmentGUID, orgGUID string) error { + isoSegment := IsolationSegment{GUID: isolationSegmentGUID, c: c} + return isoSegment.AddOrg(orgGUID) +} + +func (c *Client) RemoveIsolationSegmentFromOrg(isolationSegmentGUID, orgGUID string) error { + isoSegment := IsolationSegment{GUID: isolationSegmentGUID, c: c} + return isoSegment.RemoveOrg(orgGUID) +} + +func (c *Client) AddIsolationSegmentToSpace(isolationSegmentGUID, spaceGUID string) error { + isoSegment := IsolationSegment{GUID: isolationSegmentGUID, c: c} + return isoSegment.AddSpace(spaceGUID) +} + +func (c *Client) RemoveIsolationSegmentFromSpace(isolationSegmentGUID, spaceGUID string) error { + isoSegment := IsolationSegment{GUID: isolationSegmentGUID, c: c} + return isoSegment.RemoveSpace(spaceGUID) +} + +func (i *IsolationSegment) AddOrg(orgGuid string) error { + if i == nil || i.c == nil { + return errors.New("No communication handle.") + } + req := i.c.NewRequest("POST", fmt.Sprintf("/v3/isolation_segments/%s/relationships/organizations", i.GUID)) + type Entry struct { + GUID string `json:"guid"` + } + req.obj = map[string]interface{}{ + "data": []Entry{{GUID: orgGuid}}, + } + resp, err := i.c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error during adding org to isolation segment") + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Error adding org %s to isolation segment %s, response code: %d", orgGuid, i.Name, resp.StatusCode) + } + return nil +} + +func (i *IsolationSegment) RemoveOrg(orgGuid string) error { + if i == nil || i.c == nil { + return errors.New("No communication handle.") + } + req := i.c.NewRequest("DELETE", fmt.Sprintf("/v3/isolation_segments/%s/relationships/organizations/%s", i.GUID, orgGuid)) + resp, err := i.c.DoRequest(req) + if err != nil { + return errors.Wrapf(err, "Error during removing org %s in isolation segment %s", orgGuid, i.Name) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("Error deleting org %s in isolation segment %s, response code: %d", orgGuid, i.Name, resp.StatusCode) + } + return nil +} + +func (i *IsolationSegment) AddSpace(spaceGuid string) error { + if i == nil || i.c == nil { + return errors.New("No communication handle.") + } + req := i.c.NewRequest("PUT", fmt.Sprintf("/v2/spaces/%s", spaceGuid)) + req.obj = map[string]interface{}{ + "isolation_segment_guid": i.GUID, + } + resp, err := i.c.DoRequest(req) + if err != nil { + return errors.Wrapf(err, "Error during adding space %s to isolation segment %s", spaceGuid, i.Name) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("Error adding space to isolation segment %s, response code: %d", i.Name, resp.StatusCode) + } + return nil +} + +func (i *IsolationSegment) RemoveSpace(spaceGuid string) error { + if i == nil || i.c == nil { + return errors.New("No communication handle.") + } + req := i.c.NewRequest("DELETE", fmt.Sprintf("/v2/spaces/%s/isolation_segment", spaceGuid)) + resp, err := i.c.DoRequest(req) + if err != nil { + return errors.Wrapf(err, "Error during deleting space %s in isolation segment %s", spaceGuid, i.Name) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("Error deleting space %s from isolation segment %s, response code: %d", spaceGuid, i.Name, resp.StatusCode) + } + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/metadata.go b/vendor/github.com/cloudfoundry-community/go-cfclient/metadata.go new file mode 100644 index 0000000..661944b --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/metadata.go @@ -0,0 +1,165 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "github.com/pkg/errors" +) + +type MetadataHolder struct { + Metadata Metadata `json:"metadata"` +} + +type Metadata struct { + Annotations map[string]interface{} `json:"annotations"` + Labels map[string]interface{} `json:"labels"` +} + +func (m *Metadata) AddAnnotation(key string, value string) { + if m.Annotations == nil { + m.Annotations = make(map[string]interface{}) + } + m.Annotations[key] = value +} + +func (m *Metadata) RemoveAnnotation(key string) { + if m.Annotations == nil { + m.Annotations = make(map[string]interface{}) + } + m.Annotations[key] = nil +} + +func (m *Metadata) AddLabel(prefix, key string, value string) { + if m.Labels == nil { + m.Labels = make(map[string]interface{}) + } + if len(prefix) > 0 { + m.Labels[fmt.Sprintf("%s/%s", prefix, key)] = value + } else { + m.Labels[key] = value + } +} + +func (m *Metadata) RemoveLabel(prefix, key string) { + if m.Labels == nil { + m.Labels = make(map[string]interface{}) + } + if len(prefix) > 0 { + m.Labels[fmt.Sprintf("%s/%s", prefix, key)] = nil + } else { + m.Labels[key] = nil + } +} + +func (m *Metadata) Clear() *Metadata { + metadata := &Metadata{} + for key := range m.Annotations { + if strings.Contains(key, "/") { + metadata.RemoveAnnotation(strings.Split(key, "/")[1]) + } + metadata.RemoveAnnotation(key) + } + for key := range m.Labels { + metadata.RemoveLabel("", key) + } + return metadata +} + +func (c *Client) UpdateOrgMetadata(orgGUID string, metadata Metadata) error { + holder := MetadataHolder{} + holder.Metadata = metadata + requestURL := fmt.Sprintf("/v3/organizations/%s", orgGUID) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(holder) + if err != nil { + return err + } + r := c.NewRequestWithBody("PATCH", requestURL, buf) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error updating metadata for org %s, response code: %d", orgGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) UpdateSpaceMetadata(spaceGUID string, metadata Metadata) error { + holder := MetadataHolder{} + holder.Metadata = metadata + requestURL := fmt.Sprintf("/v3/spaces/%s", spaceGUID) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(holder) + if err != nil { + return err + } + r := c.NewRequestWithBody("PATCH", requestURL, buf) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error updating metadata for space %s, response code: %d", spaceGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) OrgMetadata(orgGUID string) (*Metadata, error) { + requestURL := fmt.Sprintf("/v3/organizations/%s", orgGUID) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return &Metadata{}, errors.Wrap(err, "Error requesting space info") + } + defer resp.Body.Close() + return c.handleMetadataResp(resp) +} + +func (c *Client) SpaceMetadata(spaceGUID string) (*Metadata, error) { + requestURL := fmt.Sprintf("/v3/spaces/%s", spaceGUID) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return &Metadata{}, errors.Wrap(err, "Error requesting space info") + } + defer resp.Body.Close() + return c.handleMetadataResp(resp) +} + +func (c *Client) RemoveOrgMetadata(orgGUID string) error { + metadata, err := c.OrgMetadata(orgGUID) + if err != nil { + return err + } + return c.UpdateOrgMetadata(orgGUID, *metadata.Clear()) +} + +func (c *Client) RemoveSpaceMetadata(spaceGUID string) error { + metadata, err := c.SpaceMetadata(spaceGUID) + if err != nil { + return err + } + return c.UpdateSpaceMetadata(spaceGUID, *metadata.Clear()) +} + +func (c *Client) handleMetadataResp(resp *http.Response) (*Metadata, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return &Metadata{}, err + } + var metadataResource MetadataHolder + err = json.Unmarshal(body, &metadataResource) + if err != nil { + return &Metadata{}, err + } + return &metadataResource.Metadata, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/org_quotas.go b/vendor/github.com/cloudfoundry-community/go-cfclient/org_quotas.go new file mode 100644 index 0000000..f9a866e --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/org_quotas.go @@ -0,0 +1,187 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type OrgQuotasResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []OrgQuotasResource `json:"resources"` +} + +type OrgQuotasResource struct { + Meta Meta `json:"metadata"` + Entity OrgQuota `json:"entity"` +} + +type OrgQuotaRequest struct { + Name string `json:"name"` + NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` + TotalServices int `json:"total_services"` + TotalRoutes int `json:"total_routes"` + TotalPrivateDomains int `json:"total_private_domains"` + MemoryLimit int `json:"memory_limit"` + TrialDBAllowed bool `json:"trial_db_allowed"` + InstanceMemoryLimit int `json:"instance_memory_limit"` + AppInstanceLimit int `json:"app_instance_limit"` + AppTaskLimit int `json:"app_task_limit"` + TotalServiceKeys int `json:"total_service_keys"` + TotalReservedRoutePorts int `json:"total_reserved_route_ports"` +} + +type OrgQuota struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` + TotalServices int `json:"total_services"` + TotalRoutes int `json:"total_routes"` + TotalPrivateDomains int `json:"total_private_domains"` + MemoryLimit int `json:"memory_limit"` + TrialDBAllowed bool `json:"trial_db_allowed"` + InstanceMemoryLimit int `json:"instance_memory_limit"` + AppInstanceLimit int `json:"app_instance_limit"` + AppTaskLimit int `json:"app_task_limit"` + TotalServiceKeys int `json:"total_service_keys"` + TotalReservedRoutePorts int `json:"total_reserved_route_ports"` + c *Client +} + +func (c *Client) ListOrgQuotasByQuery(query url.Values) ([]OrgQuota, error) { + var orgQuotas []OrgQuota + requestURL := "/v2/quota_definitions?" + query.Encode() + for { + orgQuotasResp, err := c.getOrgQuotasResponse(requestURL) + if err != nil { + return []OrgQuota{}, err + } + for _, org := range orgQuotasResp.Resources { + org.Entity.Guid = org.Meta.Guid + org.Entity.CreatedAt = org.Meta.CreatedAt + org.Entity.UpdatedAt = org.Meta.UpdatedAt + org.Entity.c = c + orgQuotas = append(orgQuotas, org.Entity) + } + requestURL = orgQuotasResp.NextUrl + if requestURL == "" { + break + } + } + return orgQuotas, nil +} + +func (c *Client) ListOrgQuotas() ([]OrgQuota, error) { + return c.ListOrgQuotasByQuery(nil) +} + +func (c *Client) GetOrgQuotaByName(name string) (OrgQuota, error) { + q := url.Values{} + q.Set("q", "name:"+name) + orgQuotas, err := c.ListOrgQuotasByQuery(q) + if err != nil { + return OrgQuota{}, err + } + if len(orgQuotas) != 1 { + return OrgQuota{}, fmt.Errorf("Unable to find org quota " + name) + } + return orgQuotas[0], nil +} + +func (c *Client) getOrgQuotasResponse(requestURL string) (OrgQuotasResponse, error) { + var orgQuotasResp OrgQuotasResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return OrgQuotasResponse{}, errors.Wrap(err, "Error requesting org quotas") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return OrgQuotasResponse{}, errors.Wrap(err, "Error reading org quotas body") + } + err = json.Unmarshal(resBody, &orgQuotasResp) + if err != nil { + return OrgQuotasResponse{}, errors.Wrap(err, "Error unmarshalling org quotas") + } + return orgQuotasResp, nil +} + +func (c *Client) CreateOrgQuota(orgQuote OrgQuotaRequest) (*OrgQuota, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(orgQuote) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("POST", "/v2/quota_definitions", buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleOrgQuotaResp(resp) +} + +func (c *Client) UpdateOrgQuota(orgQuotaGUID string, orgQuota OrgQuotaRequest) (*OrgQuota, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(orgQuota) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/quota_definitions/%s", orgQuotaGUID), buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleOrgQuotaResp(resp) +} + +func (c *Client) DeleteOrgQuota(guid string, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/quota_definitions/%s?async=%t", guid, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if (async && (resp.StatusCode != http.StatusAccepted)) || (!async && (resp.StatusCode != http.StatusNoContent)) { + return errors.Wrapf(err, "Error deleting organization %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) handleOrgQuotaResp(resp *http.Response) (*OrgQuota, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var orgQuotasResource OrgQuotasResource + err = json.Unmarshal(body, &orgQuotasResource) + if err != nil { + return nil, err + } + return c.mergeOrgQuotaResource(orgQuotasResource), nil +} + +func (c *Client) mergeOrgQuotaResource(orgQuotaResource OrgQuotasResource) *OrgQuota { + orgQuotaResource.Entity.Guid = orgQuotaResource.Meta.Guid + orgQuotaResource.Entity.CreatedAt = orgQuotaResource.Meta.CreatedAt + orgQuotaResource.Entity.UpdatedAt = orgQuotaResource.Meta.UpdatedAt + orgQuotaResource.Entity.c = c + return &orgQuotaResource.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/orgs.go b/vendor/github.com/cloudfoundry-community/go-cfclient/orgs.go new file mode 100644 index 0000000..d2e656b --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/orgs.go @@ -0,0 +1,847 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type OrgResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []OrgResource `json:"resources"` +} + +type OrgResource struct { + Meta Meta `json:"metadata"` + Entity Org `json:"entity"` +} + +type OrgUserResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []UserResource `json:"resources"` +} + +type Org struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + Status string `json:"status"` + QuotaDefinitionGuid string `json:"quota_definition_guid"` + DefaultIsolationSegmentGuid string `json:"default_isolation_segment_guid"` + c *Client +} + +type OrgSummary struct { + Guid string `json:"guid"` + Name string `json:"name"` + Status string `json:"status"` + Spaces []OrgSummarySpaces `json:"spaces"` +} + +type OrgSummarySpaces struct { + Guid string `json:"guid"` + Name string `json:"name"` + ServiceCount int `json:"service_count"` + AppCount int `json:"app_count"` + MemDevTotal int `json:"mem_dev_total"` + MemProdTotal int `json:"mem_prod_total"` +} + +type OrgRequest struct { + Name string `json:"name"` + Status string `json:"status,omitempty"` + QuotaDefinitionGuid string `json:"quota_definition_guid,omitempty"` + DefaultIsolationSegmentGuid string `json:"default_isolation_segment_guid,omitempty"` +} + +func (c *Client) ListOrgsByQuery(query url.Values) ([]Org, error) { + var orgs []Org + requestURL := "/v2/organizations?" + query.Encode() + for { + orgResp, err := c.getOrgResponse(requestURL) + if err != nil { + return []Org{}, err + } + for _, org := range orgResp.Resources { + orgs = append(orgs, c.mergeOrgResource(org)) + } + requestURL = orgResp.NextUrl + if requestURL == "" || query.Get("page") != "" { + break + } + } + return orgs, nil +} + +func (c *Client) ListOrgs() ([]Org, error) { + return c.ListOrgsByQuery(nil) +} + +func (c *Client) GetOrgByName(name string) (Org, error) { + var org Org + q := url.Values{} + q.Set("q", "name:"+name) + orgs, err := c.ListOrgsByQuery(q) + if err != nil { + return org, err + } + if len(orgs) == 0 { + cfErr := NewOrganizationNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return org, cfErr + } + return orgs[0], nil +} + +func (c *Client) GetOrgByGuid(guid string) (Org, error) { + var orgRes OrgResource + r := c.NewRequest("GET", "/v2/organizations/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return Org{}, err + } + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Org{}, err + } + err = json.Unmarshal(body, &orgRes) + if err != nil { + return Org{}, err + } + return c.mergeOrgResource(orgRes), nil +} + +func (c *Client) OrgSpaces(guid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/organizations/%s/spaces", guid), url.Values{}) +} + +func (o *Org) Summary() (OrgSummary, error) { + var orgSummary OrgSummary + requestURL := fmt.Sprintf("/v2/organizations/%s/summary", o.Guid) + r := o.c.NewRequest("GET", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return OrgSummary{}, errors.Wrap(err, "Error requesting org summary") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return OrgSummary{}, errors.Wrap(err, "Error reading org summary body") + } + err = json.Unmarshal(resBody, &orgSummary) + if err != nil { + return OrgSummary{}, errors.Wrap(err, "Error unmarshalling org summary") + } + return orgSummary, nil +} + +func (o *Org) Quota() (*OrgQuota, error) { + var orgQuota *OrgQuota + var orgQuotaResource OrgQuotasResource + if o.QuotaDefinitionGuid == "" { + return nil, nil + } + requestURL := fmt.Sprintf("/v2/quota_definitions/%s", o.QuotaDefinitionGuid) + r := o.c.NewRequest("GET", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return &OrgQuota{}, errors.Wrap(err, "Error requesting org quota") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return &OrgQuota{}, errors.Wrap(err, "Error reading org quota body") + } + err = json.Unmarshal(resBody, &orgQuotaResource) + if err != nil { + return &OrgQuota{}, errors.Wrap(err, "Error unmarshalling org quota") + } + orgQuota = &orgQuotaResource.Entity + orgQuota.Guid = orgQuotaResource.Meta.Guid + orgQuota.c = o.c + return orgQuota, nil +} + +func (c *Client) ListOrgUsersByQuery(orgGUID string, query url.Values) ([]User, error) { + var users []User + requestURL := fmt.Sprintf("/v2/organizations/%s/users?%s", orgGUID, query.Encode()) + for { + omResp, err := c.getOrgUserResponse(requestURL) + if err != nil { + return []User{}, err + } + for _, u := range omResp.Resources { + users = append(users, c.mergeUserResource(u)) + } + requestURL = omResp.NextURL + if requestURL == "" { + break + } + } + return users, nil +} + +func (c *Client) ListOrgUsers(orgGUID string) ([]User, error) { + return c.ListOrgUsersByQuery(orgGUID, nil) +} + +func (c *Client) listOrgRolesByQuery(orgGUID, role string, query url.Values) ([]User, error) { + var users []User + requestURL := fmt.Sprintf("/v2/organizations/%s/%s?%s", orgGUID, role, query.Encode()) + for { + omResp, err := c.getOrgUserResponse(requestURL) + if err != nil { + return []User{}, err + } + for _, u := range omResp.Resources { + users = append(users, c.mergeUserResource(u)) + } + requestURL = omResp.NextURL + if requestURL == "" { + break + } + } + return users, nil +} + +func (c *Client) ListOrgManagersByQuery(orgGUID string, query url.Values) ([]User, error) { + return c.listOrgRolesByQuery(orgGUID, "managers", query) +} + +func (c *Client) ListOrgManagers(orgGUID string) ([]User, error) { + return c.ListOrgManagersByQuery(orgGUID, nil) +} + +func (c *Client) ListOrgAuditorsByQuery(orgGUID string, query url.Values) ([]User, error) { + return c.listOrgRolesByQuery(orgGUID, "auditors", query) +} + +func (c *Client) ListOrgAuditors(orgGUID string) ([]User, error) { + return c.ListOrgAuditorsByQuery(orgGUID, nil) +} + +func (c *Client) ListOrgBillingManagersByQuery(orgGUID string, query url.Values) ([]User, error) { + return c.listOrgRolesByQuery(orgGUID, "billing_managers", query) +} + +func (c *Client) ListOrgBillingManagers(orgGUID string) ([]User, error) { + return c.ListOrgBillingManagersByQuery(orgGUID, nil) +} + +func (c *Client) AssociateOrgManager(orgGUID, userGUID string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateManager(userGUID) +} + +func (c *Client) AssociateOrgManagerByUsername(orgGUID, name string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateManagerByUsername(name) +} + +func (c *Client) AssociateOrgManagerByUsernameAndOrigin(orgGUID, name, origin string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateOrgUser(orgGUID, userGUID string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateUser(userGUID) +} + +func (c *Client) AssociateOrgAuditor(orgGUID, userGUID string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateAuditor(userGUID) +} + +func (c *Client) AssociateOrgUserByUsername(orgGUID, name string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateUserByUsername(name) +} + +func (c *Client) AssociateOrgUserByUsernameAndOrigin(orgGUID, name, origin string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateUserByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateOrgAuditorByUsername(orgGUID, name string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateAuditorByUsername(name) +} + +func (c *Client) AssociateOrgAuditorByUsernameAndOrigin(orgGUID, name, origin string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateAuditorByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateOrgBillingManager(orgGUID, userGUID string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateBillingManager(userGUID) +} + +func (c *Client) AssociateOrgBillingManagerByUsername(orgGUID, name string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateBillingManagerByUsername(name) +} + +func (c *Client) AssociateOrgBillingManagerByUsernameAndOrigin(orgGUID, name, origin string) (Org, error) { + org := Org{Guid: orgGUID, c: c} + return org.AssociateBillingManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveOrgManager(orgGUID, userGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveManager(userGUID) +} + +func (c *Client) RemoveOrgManagerByUsername(orgGUID, name string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveManagerByUsername(name) +} + +func (c *Client) RemoveOrgManagerByUsernameAndOrigin(orgGUID, name, origin string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveOrgUser(orgGUID, userGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveUser(userGUID) +} + +func (c *Client) RemoveOrgAuditor(orgGUID, userGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveAuditor(userGUID) +} + +func (c *Client) RemoveOrgUserByUsername(orgGUID, name string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveUserByUsername(name) +} + +func (c *Client) RemoveOrgUserByUsernameAndOrigin(orgGUID, name, origin string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveUserByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveOrgAuditorByUsername(orgGUID, name string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveAuditorByUsername(name) +} + +func (c *Client) RemoveOrgAuditorByUsernameAndOrigin(orgGUID, name, origin string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveAuditorByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveOrgBillingManager(orgGUID, userGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveBillingManager(userGUID) +} + +func (c *Client) RemoveOrgBillingManagerByUsername(orgGUID, name string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveBillingManagerByUsername(name) +} + +func (c *Client) RemoveOrgBillingManagerByUsernameAndOrigin(orgGUID, name, origin string) error { + org := Org{Guid: orgGUID, c: c} + return org.RemoveBillingManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) ListOrgSpaceQuotas(orgGUID string) ([]SpaceQuota, error) { + org := Org{Guid: orgGUID, c: c} + return org.ListSpaceQuotas() +} + +func (c *Client) ListOrgPrivateDomains(orgGUID string) ([]Domain, error) { + org := Org{Guid: orgGUID, c: c} + return org.ListPrivateDomains() +} + +func (c *Client) ShareOrgPrivateDomain(orgGUID, privateDomainGUID string) (*Domain, error) { + org := Org{Guid: orgGUID, c: c} + return org.SharePrivateDomain(privateDomainGUID) +} + +func (c *Client) UnshareOrgPrivateDomain(orgGUID, privateDomainGUID string) error { + org := Org{Guid: orgGUID, c: c} + return org.UnsharePrivateDomain(privateDomainGUID) +} + +func (o *Org) ListSpaceQuotas() ([]SpaceQuota, error) { + var spaceQuotas []SpaceQuota + requestURL := fmt.Sprintf("/v2/organizations/%s/space_quota_definitions", o.Guid) + for { + spaceQuotasResp, err := o.c.getSpaceQuotasResponse(requestURL) + if err != nil { + return []SpaceQuota{}, err + } + for _, resource := range spaceQuotasResp.Resources { + spaceQuotas = append(spaceQuotas, *o.c.mergeSpaceQuotaResource(resource)) + } + requestURL = spaceQuotasResp.NextUrl + if requestURL == "" { + break + } + } + return spaceQuotas, nil +} + +func (o *Org) ListPrivateDomains() ([]Domain, error) { + var domains []Domain + requestURL := fmt.Sprintf("/v2/organizations/%s/private_domains", o.Guid) + for { + domainsResp, err := o.c.getDomainsResponse(requestURL) + if err != nil { + return []Domain{}, err + } + for _, resource := range domainsResp.Resources { + domains = append(domains, *o.c.mergeDomainResource(resource)) + } + requestURL = domainsResp.NextUrl + if requestURL == "" { + break + } + } + return domains, nil +} + +func (o *Org) SharePrivateDomain(privateDomainGUID string) (*Domain, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/private_domains/%s", o.Guid, privateDomainGUID) + r := o.c.NewRequest("PUT", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, errors.Wrapf(err, "Error sharing domain %s for org %s, response code: %d", privateDomainGUID, o.Guid, resp.StatusCode) + } + return o.c.handleDomainResp(resp) +} + +func (o *Org) UnsharePrivateDomain(privateDomainGUID string) error { + requestURL := fmt.Sprintf("/v2/organizations/%s/private_domains/%s", o.Guid, privateDomainGUID) + r := o.c.NewRequest("DELETE", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error unsharing domain %s for org %s, response code: %d", privateDomainGUID, o.Guid, resp.StatusCode) + } + return nil +} + +func (o *Org) associateRole(userGUID, role string) (Org, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/%s/%s", o.Guid, role, userGUID) + r := o.c.NewRequest("PUT", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error associating %s %s, response code: %d", role, userGUID, resp.StatusCode) + } + return o.c.handleOrgResp(resp) +} + +func (o *Org) associateRoleByUsernameAndOrigin(name, role, origin string) (Org, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/%s", o.Guid, role) + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return Org{}, err + } + r := o.c.NewRequestWithBody("PUT", requestURL, buf) + resp, err := o.c.DoRequest(r) + if err != nil { + return Org{}, err + } + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error associating %s %s, response code: %d", role, name, resp.StatusCode) + } + return o.c.handleOrgResp(resp) +} + +func (o *Org) AssociateManager(userGUID string) (Org, error) { + return o.associateRole(userGUID, "managers") +} + +func (o *Org) AssociateManagerByUsername(name string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "managers", "") +} + +func (o *Org) AssociateManagerByUsernameAndOrigin(name, origin string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "managers", origin) +} + +func (o *Org) AssociateUser(userGUID string) (Org, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/users/%s", o.Guid, userGUID) + r := o.c.NewRequest("PUT", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error associating user %s, response code: %d", userGUID, resp.StatusCode) + } + return o.c.handleOrgResp(resp) +} + +func (o *Org) AssociateAuditor(userGUID string) (Org, error) { + return o.associateRole(userGUID, "auditors") +} + +func (o *Org) AssociateAuditorByUsername(name string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "auditors", "") +} + +func (o *Org) AssociateAuditorByUsernameAndOrigin(name, origin string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "auditors", origin) +} + +func (o *Org) AssociateBillingManager(userGUID string) (Org, error) { + return o.associateRole(userGUID, "billing_managers") +} + +func (o *Org) AssociateBillingManagerByUsername(name string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "billing_managers", "") +} +func (o *Org) AssociateBillingManagerByUsernameAndOrigin(name, origin string) (Org, error) { + return o.associateRoleByUsernameAndOrigin(name, "billing_managers", origin) +} + +func (o *Org) AssociateUserByUsername(name string) (Org, error) { + return o.associateUserByUsernameAndOrigin(name, "") +} + +func (o *Org) AssociateUserByUsernameAndOrigin(name, origin string) (Org, error) { + return o.associateUserByUsernameAndOrigin(name, origin) +} + +func (o *Org) associateUserByUsernameAndOrigin(name, origin string) (Org, error) { + requestURL := fmt.Sprintf("/v2/organizations/%s/users", o.Guid) + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return Org{}, err + } + r := o.c.NewRequestWithBody("PUT", requestURL, buf) + resp, err := o.c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error associating user %s, response code: %d", name, resp.StatusCode) + } + return o.c.handleOrgResp(resp) +} + +func (o *Org) removeRole(userGUID, role string) error { + requestURL := fmt.Sprintf("/v2/organizations/%s/%s/%s", o.Guid, role, userGUID) + r := o.c.NewRequest("DELETE", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing %s %s, response code: %d", role, userGUID, resp.StatusCode) + } + return nil +} + +func (o *Org) removeRoleByUsernameAndOrigin(name, role, origin string) error { + var requestURL string + var method string + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + requestURL = fmt.Sprintf("/v2/organizations/%s/%s/remove", o.Guid, role) + method = "POST" + payload["origin"] = origin + } else { + requestURL = fmt.Sprintf("/v2/organizations/%s/%s", o.Guid, role) + method = "DELETE" + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return err + } + + r := o.c.NewRequestWithBody(method, requestURL, buf) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing manager %s, response code: %d", name, resp.StatusCode) + } + return nil +} + +func (o *Org) RemoveManager(userGUID string) error { + return o.removeRole(userGUID, "managers") +} + +func (o *Org) RemoveManagerByUsername(name string) error { + return o.removeRoleByUsernameAndOrigin(name, "managers", "") +} +func (o *Org) RemoveManagerByUsernameAndOrigin(name, origin string) error { + return o.removeRoleByUsernameAndOrigin(name, "managers", origin) +} + +func (o *Org) RemoveAuditor(userGUID string) error { + return o.removeRole(userGUID, "auditors") +} + +func (o *Org) RemoveAuditorByUsername(name string) error { + return o.removeRoleByUsernameAndOrigin(name, "auditors", "") +} +func (o *Org) RemoveAuditorByUsernameAndOrigin(name, origin string) error { + return o.removeRoleByUsernameAndOrigin(name, "auditors", origin) +} + +func (o *Org) RemoveBillingManager(userGUID string) error { + return o.removeRole(userGUID, "billing_managers") +} + +func (o *Org) RemoveBillingManagerByUsername(name string) error { + return o.removeRoleByUsernameAndOrigin(name, "billing_managers", "") +} + +func (o *Org) RemoveBillingManagerByUsernameAndOrigin(name, origin string) error { + return o.removeRoleByUsernameAndOrigin(name, "billing_managers", origin) +} + +func (o *Org) RemoveUser(userGUID string) error { + requestURL := fmt.Sprintf("/v2/organizations/%s/users/%s", o.Guid, userGUID) + r := o.c.NewRequest("DELETE", requestURL) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing user %s, response code: %d", userGUID, resp.StatusCode) + } + return nil +} + +func (o *Org) RemoveUserByUsername(name string) error { + return o.removeUserByUsernameAndOrigin(name, "") +} + +func (o *Org) RemoveUserByUsernameAndOrigin(name, origin string) error { + return o.removeUserByUsernameAndOrigin(name, origin) +} + +func (o *Org) removeUserByUsernameAndOrigin(name, origin string) error { + var requestURL string + var method string + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + requestURL = fmt.Sprintf("/v2/organizations/%s/users/remove", o.Guid) + method = "POST" + } else { + requestURL = fmt.Sprintf("/v2/organizations/%s/users", o.Guid) + method = "DELETE" + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return err + } + r := o.c.NewRequestWithBody(method, requestURL, buf) + resp, err := o.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing user %s, response code: %d", name, resp.StatusCode) + } + return nil +} + +func (c *Client) CreateOrg(req OrgRequest) (Org, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return Org{}, err + } + r := c.NewRequestWithBody("POST", "/v2/organizations", buf) + resp, err := c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error creating organization, response code: %d", resp.StatusCode) + } + return c.handleOrgResp(resp) +} + +func (c *Client) UpdateOrg(orgGUID string, orgRequest OrgRequest) (Org, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(orgRequest) + if err != nil { + return Org{}, err + } + r := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/organizations/%s", orgGUID), buf) + resp, err := c.DoRequest(r) + if err != nil { + return Org{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Org{}, errors.Wrapf(err, "Error updating organization, response code: %d", resp.StatusCode) + } + return c.handleOrgResp(resp) +} + +func (c *Client) DeleteOrg(guid string, recursive, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/organizations/%s?recursive=%t&async=%t", guid, recursive, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting organization %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) getOrgResponse(requestURL string) (OrgResponse, error) { + var orgResp OrgResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return OrgResponse{}, errors.Wrap(err, "Error requesting orgs") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return OrgResponse{}, errors.Wrap(err, "Error reading org request") + } + err = json.Unmarshal(resBody, &orgResp) + if err != nil { + return OrgResponse{}, errors.Wrap(err, "Error unmarshalling org") + } + return orgResp, nil +} + +func (c *Client) fetchOrgs(requestURL string) ([]Org, error) { + var orgs []Org + for { + orgResp, err := c.getOrgResponse(requestURL) + if err != nil { + return []Org{}, err + } + for _, org := range orgResp.Resources { + orgs = append(orgs, c.mergeOrgResource(org)) + } + requestURL = orgResp.NextUrl + if requestURL == "" { + break + } + } + return orgs, nil +} + +func (c *Client) handleOrgResp(resp *http.Response) (Org, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Org{}, err + } + var orgResource OrgResource + err = json.Unmarshal(body, &orgResource) + if err != nil { + return Org{}, err + } + return c.mergeOrgResource(orgResource), nil +} + +func (c *Client) getOrgUserResponse(requestURL string) (OrgUserResponse, error) { + var omResp OrgUserResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return OrgUserResponse{}, errors.Wrap(err, "error requesting org managers") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return OrgUserResponse{}, errors.Wrap(err, "error reading org managers response body") + } + if err := json.Unmarshal(resBody, &omResp); err != nil { + return OrgUserResponse{}, errors.Wrap(err, "error unmarshaling org managers") + } + return omResp, nil +} + +func (c *Client) mergeOrgResource(org OrgResource) Org { + org.Entity.Guid = org.Meta.Guid + org.Entity.CreatedAt = org.Meta.CreatedAt + org.Entity.UpdatedAt = org.Meta.UpdatedAt + org.Entity.c = c + return org.Entity +} + +func (c *Client) DefaultIsolationSegmentForOrg(orgGUID, isolationSegmentGUID string) error { + return c.updateOrgDefaultIsolationSegment(orgGUID, map[string]interface{}{"guid": isolationSegmentGUID}) +} + +func (c *Client) ResetDefaultIsolationSegmentForOrg(orgGUID string) error { + return c.updateOrgDefaultIsolationSegment(orgGUID, nil) +} + +func (c *Client) updateOrgDefaultIsolationSegment(orgGUID string, data interface{}) error { + requestURL := fmt.Sprintf("/v3/organizations/%s/relationships/default_isolation_segment", orgGUID) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(map[string]interface{}{"data": data}) + if err != nil { + return err + } + r := c.NewRequestWithBody("PATCH", requestURL, buf) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error setting default isolation segment for org %s, response code: %d", orgGUID, resp.StatusCode) + } + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/processes.go b/vendor/github.com/cloudfoundry-community/go-cfclient/processes.go new file mode 100644 index 0000000..f3b729d --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/processes.go @@ -0,0 +1,102 @@ +package cfclient + +import ( + "encoding/json" + "net/url" +) + +// ProcessListResponse is the json body returned from the API +type ProcessListResponse struct { + Pagination Pagination `json:"pagination"` + Processes []Process `json:"resources"` +} + +// Process represents a running process in a container. +type Process struct { + GUID string `json:"guid"` + Type string `json:"type"` + Instances int `json:"instances"` + MemoryInMB int `json:"memory_in_mb"` + DiskInMB int `json:"disk_in_mb"` + Ports []int `json:"ports,omitempty"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + HealthCheck struct { + Type string `json:"type"` + Data struct { + Timeout int `json:"timeout"` + InvocationTimeout int `json:"invocation_timeout"` + Endpoint string `json:"endpoint"` + } `json:"data"` + } `json:"health_check"` + Links struct { + Self Link `json:"self"` + Scale Link `json:"scale"` + App Link `json:"app"` + Space Link `json:"space"` + Stats Link `json:"stats"` + } `json:"links"` +} + +// ListAllProcesses will call the v3 processes api +func (c *Client) ListAllProcesses() ([]Process, error) { + return c.ListAllProcessesByQuery(url.Values{}) +} + +// ListAllProcessesByQuery will call the v3 processes api +func (c *Client) ListAllProcessesByQuery(query url.Values) ([]Process, error) { + var allProcesses []Process + + requestURL := "/v3/processes" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + for { + resp, err := c.getProcessPage(requestURL) + if err != nil { + return nil, err + } + + if resp.Pagination.TotalResults == 0 { + return nil, nil + } + + if allProcesses == nil { + allProcesses = make([]Process, 0, resp.Pagination.TotalResults) + } + + allProcesses = append(allProcesses, resp.Processes...) + if resp.Pagination.Next.Href == "" { + break + } + + requestURL = resp.Pagination.Next.Href + if requestURL == "" { + return allProcesses, nil + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, err + } + } + + return allProcesses, nil +} + +func (c *Client) getProcessPage(requestURL string) (*ProcessListResponse, error) { + req := c.NewRequest("GET", requestURL) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + + procResp := new(ProcessListResponse) + defer resp.Body.Close() + err = json.NewDecoder(resp.Body).Decode(procResp) + if err != nil { + return nil, err + } + + return procResp, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/resource_match.go b/vendor/github.com/cloudfoundry-community/go-cfclient/resource_match.go new file mode 100644 index 0000000..70b5413 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/resource_match.go @@ -0,0 +1,45 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "io/ioutil" + + "github.com/pkg/errors" +) + +// The Resource match Api retruns the response in the following data structure +type Resource struct { + Sha1 string `json:"sha1"` + Size int `json:"size"` +} + +// ResourceMatch matches given resource list of SHA / file size pairs against +// the Cloud Controller cache, and reports the subset which describes already +// existing files +func (c *Client) ResourceMatch(resources []Resource) ([]Resource, error) { + + var resourcesList []Resource + emptyResource := make([]Resource, 0) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(resources) + if err != nil { + return emptyResource, errors.Wrapf(err, "Error reading Resource List") + } + r := c.NewRequestWithBody("PUT", "/v2/resource_match", buf) + resp, err := c.DoRequest(r) + if err != nil { + return emptyResource, errors.Wrapf(err, "Error uploading Resource List") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return emptyResource, errors.Wrapf(err, "Error reading Resources http response body") + } + err = json.Unmarshal(resBody, &resourcesList) + if err != nil { + return emptyResource, errors.Wrapf(err, "Error reading Resources http response body") + } + return resourcesList, nil + +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/route_mappings.go b/vendor/github.com/cloudfoundry-community/go-cfclient/route_mappings.go new file mode 100644 index 0000000..c4e327f --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/route_mappings.go @@ -0,0 +1,162 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type RouteMappingRequest struct { + AppGUID string `json:"app_guid"` + RouteGUID string `json:"route_guid"` + AppPort int `json:"app_port"` +} + +type RouteMappingResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []RouteMappingResource `json:"resources"` +} + +type RouteMapping struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + AppPort int `json:"app_port"` + AppGUID string `json:"app_guid"` + RouteGUID string `json:"route_guid"` + AppUrl string `json:"app_url"` + RouteUrl string `json:"route_url"` + c *Client +} + +type RouteMappingResource struct { + Meta Meta `json:"metadata"` + Entity RouteMapping `json:"entity"` +} + +func (c *Client) MappingAppAndRoute(req RouteMappingRequest) (*RouteMapping, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("POST", "/v2/route_mappings", buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleMappingResp(resp) +} + +func (c *Client) ListRouteMappings() ([]*RouteMapping, error) { + return c.ListRouteMappingsByQuery(nil) +} + +func (c *Client) ListRouteMappingsByQuery(query url.Values) ([]*RouteMapping, error) { + var routeMappings []*RouteMapping + var routeMappingsResp RouteMappingResponse + pages := 0 + + requestUrl := "/v2/route_mappings?" + query.Encode() + for { + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting route mappings") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading route mappings request:") + } + + err = json.Unmarshal(resBody, &routeMappingsResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling route mappings") + } + + for _, routeMapping := range routeMappingsResp.Resources { + routeMappings = append(routeMappings, c.mergeRouteMappingResource(routeMapping)) + } + requestUrl = routeMappingsResp.NextUrl + if requestUrl == "" { + break + } + pages++ + totalPages := routeMappingsResp.Pages + if totalPages > 0 && pages >= totalPages { + break + } + } + return routeMappings, nil +} + +func (c *Client) GetRouteMappingByGuid(guid string) (*RouteMapping, error) { + var routeMapping RouteMappingResource + requestUrl := fmt.Sprintf("/v2/route_mappings/%s", guid) + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting route mapping") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading route mapping response body") + } + err = json.Unmarshal(resBody, &routeMapping) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshalling route mapping") + } + routeMapping.Entity.Guid = routeMapping.Meta.Guid + routeMapping.Entity.CreatedAt = routeMapping.Meta.CreatedAt + routeMapping.Entity.UpdatedAt = routeMapping.Meta.UpdatedAt + routeMapping.Entity.c = c + return &routeMapping.Entity, nil +} + +func (c *Client) DeleteRouteMapping(guid string) error { + requestUrl := fmt.Sprintf("/v2/route_mappings/%s?", guid) + resp, err := c.DoRequest(c.NewRequest("DELETE", requestUrl)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting route mapping %s, response code %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) handleMappingResp(resp *http.Response) (*RouteMapping, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var mappingResource RouteMappingResource + err = json.Unmarshal(body, &mappingResource) + if err != nil { + return nil, err + } + return c.mergeRouteMappingResource(mappingResource), nil +} + +func (c *Client) mergeRouteMappingResource(mapping RouteMappingResource) *RouteMapping { + mapping.Entity.Guid = mapping.Meta.Guid + mapping.Entity.CreatedAt = mapping.Meta.CreatedAt + mapping.Entity.UpdatedAt = mapping.Meta.UpdatedAt + mapping.Entity.c = c + return &mapping.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/routes.go b/vendor/github.com/cloudfoundry-community/go-cfclient/routes.go new file mode 100644 index 0000000..6b222d7 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/routes.go @@ -0,0 +1,209 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type RoutesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []RoutesResource `json:"resources"` +} + +type RoutesResource struct { + Meta Meta `json:"metadata"` + Entity Route `json:"entity"` +} + +type RouteRequest struct { + DomainGuid string `json:"domain_guid"` + SpaceGuid string `json:"space_guid"` + Host string `json:"host"` // required for http routes + Path string `json:"path"` + Port int `json:"port"` +} + +type Route struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Host string `json:"host"` + Path string `json:"path"` + DomainGuid string `json:"domain_guid"` + DomainURL string `json:"domain_url"` + SpaceGuid string `json:"space_guid"` + ServiceInstanceGuid string `json:"service_instance_guid"` + Port int `json:"port"` + c *Client +} + +// CreateRoute creates a regular http route +func (c *Client) CreateRoute(routeRequest RouteRequest) (Route, error) { + routesResource, err := c.createRoute("/v2/routes", routeRequest) + if nil != err { + return Route{}, err + } + return c.mergeRouteResource(routesResource), nil +} + +// CreateTcpRoute creates a TCP route +func (c *Client) CreateTcpRoute(routeRequest RouteRequest) (Route, error) { + routesResource, err := c.createRoute("/v2/routes?generate_port=true", routeRequest) + if nil != err { + return Route{}, err + } + return c.mergeRouteResource(routesResource), nil +} + +// BindRoute associates the specified route with the application +func (c *Client) BindRoute(routeGUID, appGUID string) error { + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/routes/%s/apps/%s", routeGUID, appGUID))) + if err != nil { + return errors.Wrapf(err, "Error binding route %s to app %s", routeGUID, appGUID) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("Error binding route %s to app %s, response code: %d", routeGUID, appGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) GetRouteByGuid(guid string) (Route, error) { + var route RoutesResource + + r := c.NewRequest("GET", fmt.Sprintf("/v2/routes/%s", guid)) + resp, err := c.DoRequest(r) + if err != nil { + return route.Entity, errors.Wrap(err, "Error requesting route") + } + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(&route) + if err != nil { + return route.Entity, errors.Wrap(err, "Error unmarshalling route response body") + } + + route.Entity.Guid = route.Meta.Guid + route.Entity.CreatedAt = route.Meta.CreatedAt + route.Entity.UpdatedAt = route.Meta.UpdatedAt + route.Entity.c = c + return route.Entity, nil +} + +func (c *Client) ListRoutesByQuery(query url.Values) ([]Route, error) { + return c.fetchRoutes("/v2/routes?" + query.Encode()) +} + +func (c *Client) fetchRoutes(requestUrl string) ([]Route, error) { + var routes []Route + for { + routesResp, err := c.getRoutesResponse(requestUrl) + if err != nil { + return []Route{}, err + } + for _, route := range routesResp.Resources { + route.Entity.Guid = route.Meta.Guid + route.Entity.CreatedAt = route.Meta.CreatedAt + route.Entity.UpdatedAt = route.Meta.UpdatedAt + route.Entity.c = c + routes = append(routes, route.Entity) + } + requestUrl = routesResp.NextUrl + if requestUrl == "" { + break + } + } + return routes, nil +} + +func (c *Client) ListRoutes() ([]Route, error) { + return c.ListRoutesByQuery(nil) +} + +func (r *Route) Domain() (*Domain, error) { + req := r.c.NewRequest("GET", r.DomainURL) + resp, err := r.c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "requesting domain for route "+r.DomainURL) + } + + defer resp.Body.Close() + var domain DomainResource + if err = json.NewDecoder(resp.Body).Decode(&domain); err != nil { + return nil, errors.Wrap(err, "unmarshalling domain") + } + + return r.c.mergeDomainResource(domain), nil +} + +func (c *Client) getRoutesResponse(requestUrl string) (RoutesResponse, error) { + var routesResp RoutesResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return RoutesResponse{}, errors.Wrap(err, "Error requesting routes") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return RoutesResponse{}, errors.Wrap(err, "Error reading routes body") + } + err = json.Unmarshal(resBody, &routesResp) + if err != nil { + return RoutesResponse{}, errors.Wrap(err, "Error unmarshalling routes") + } + return routesResp, nil +} + +func (c *Client) createRoute(requestUrl string, routeRequest RouteRequest) (RoutesResource, error) { + var routeResp RoutesResource + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(routeRequest) + if err != nil { + return RoutesResource{}, errors.Wrap(err, "Error creating route - failed to serialize request body") + } + r := c.NewRequestWithBody("POST", requestUrl, buf) + resp, err := c.DoRequest(r) + if err != nil { + return RoutesResource{}, errors.Wrap(err, "Error creating route") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return RoutesResource{}, errors.Wrap(err, "Error creating route") + } + err = json.Unmarshal(resBody, &routeResp) + if err != nil { + return RoutesResource{}, errors.Wrap(err, "Error unmarshalling routes") + } + routeResp.Entity.c = c + return routeResp, nil +} + +func (c *Client) DeleteRoute(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/routes/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting route %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) mergeRouteResource(rr RoutesResource) Route { + rr.Entity.Guid = rr.Meta.Guid + rr.Entity.CreatedAt = rr.Meta.CreatedAt + rr.Entity.UpdatedAt = rr.Meta.UpdatedAt + rr.Entity.c = c + return rr.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/secgroups.go b/vendor/github.com/cloudfoundry-community/go-cfclient/secgroups.go new file mode 100644 index 0000000..5d6db1e --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/secgroups.go @@ -0,0 +1,576 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "reflect" + "strings" + + "github.com/Masterminds/semver" + "github.com/pkg/errors" +) + +type SecGroupResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SecGroupResource `json:"resources"` +} + +type SecGroupCreateResponse struct { + Code int `json:"code"` + ErrorCode string `json:"error_code"` + Description string `json:"description"` +} + +type SecGroupResource struct { + Meta Meta `json:"metadata"` + Entity SecGroup `json:"entity"` +} + +type SecGroup struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Rules []SecGroupRule `json:"rules"` + Running bool `json:"running_default"` + Staging bool `json:"staging_default"` + SpacesURL string `json:"spaces_url"` + StagingSpacesURL string `json:"staging_spaces_url"` + SpacesData []SpaceResource `json:"spaces"` + StagingSpacesData []SpaceResource `json:"staging_spaces"` + c *Client +} + +type SecGroupRule struct { + Protocol string `json:"protocol"` + Ports string `json:"ports,omitempty"` // e.g. "4000-5000,9142" + Destination string `json:"destination"` // CIDR Format + Description string `json:"description,omitempty"` // Optional description + Code int `json:"code"` // ICMP code + Type int `json:"type"` // ICMP type. Only valid if Protocol=="icmp" + Log bool `json:"log,omitempty"` // If true, log this rule +} + +var MinStagingSpacesVersion *semver.Version = getMinStagingSpacesVersion() + +func (c *Client) ListSecGroups() (secGroups []SecGroup, err error) { + requestURL := "/v2/security_groups?inline-relations-depth=1" + for requestURL != "" { + var secGroupResp SecGroupResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling sec group") + } + + for _, secGroup := range secGroupResp.Resources { + secGroup.Entity.Guid = secGroup.Meta.Guid + secGroup.Entity.CreatedAt = secGroup.Meta.CreatedAt + secGroup.Entity.UpdatedAt = secGroup.Meta.UpdatedAt + secGroup.Entity.c = c + for i, space := range secGroup.Entity.SpacesData { + space.Entity.Guid = space.Meta.Guid + secGroup.Entity.SpacesData[i] = space + } + if len(secGroup.Entity.SpacesData) == 0 { + spaces, err := secGroup.Entity.ListSpaceResources() + if err != nil { + return nil, err + } + secGroup.Entity.SpacesData = append(secGroup.Entity.SpacesData, spaces...) + } + if len(secGroup.Entity.StagingSpacesData) == 0 { + spaces, err := secGroup.Entity.ListStagingSpaceResources() + if err != nil { + return nil, err + } + secGroup.Entity.StagingSpacesData = append(secGroup.Entity.SpacesData, spaces...) + } + secGroups = append(secGroups, secGroup.Entity) + } + + requestURL = secGroupResp.NextUrl + resp.Body.Close() + } + return secGroups, nil +} + +func (c *Client) ListRunningSecGroups() ([]SecGroup, error) { + secGroups := make([]SecGroup, 0) + requestURL := "/v2/config/running_security_groups" + for requestURL != "" { + var secGroupResp SecGroupResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + + if err != nil { + return nil, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling sec group") + } + + for _, secGroup := range secGroupResp.Resources { + secGroup.Entity.Guid = secGroup.Meta.Guid + secGroup.Entity.CreatedAt = secGroup.Meta.CreatedAt + secGroup.Entity.UpdatedAt = secGroup.Meta.UpdatedAt + secGroup.Entity.c = c + + secGroups = append(secGroups, secGroup.Entity) + } + + requestURL = secGroupResp.NextUrl + resp.Body.Close() + } + return secGroups, nil +} + +func (c *Client) ListStagingSecGroups() ([]SecGroup, error) { + secGroups := make([]SecGroup, 0) + requestURL := "/v2/config/staging_security_groups" + for requestURL != "" { + var secGroupResp SecGroupResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + + if err != nil { + return nil, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling sec group") + } + + for _, secGroup := range secGroupResp.Resources { + secGroup.Entity.Guid = secGroup.Meta.Guid + secGroup.Entity.CreatedAt = secGroup.Meta.CreatedAt + secGroup.Entity.UpdatedAt = secGroup.Meta.UpdatedAt + secGroup.Entity.c = c + + secGroups = append(secGroups, secGroup.Entity) + } + + requestURL = secGroupResp.NextUrl + resp.Body.Close() + } + return secGroups, nil +} + +func (c *Client) GetSecGroupByName(name string) (secGroup SecGroup, err error) { + requestURL := "/v2/security_groups?q=name:" + name + var secGroupResp SecGroupResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + + if err != nil { + return secGroup, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return secGroup, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return secGroup, errors.Wrap(err, "Error unmarshaling sec group") + } + if len(secGroupResp.Resources) == 0 { + cfErr := NewSecurityGroupNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return secGroup, cfErr + } + secGroup = secGroupResp.Resources[0].Entity + secGroup.Guid = secGroupResp.Resources[0].Meta.Guid + secGroup.CreatedAt = secGroupResp.Resources[0].Meta.CreatedAt + secGroup.UpdatedAt = secGroupResp.Resources[0].Meta.UpdatedAt + secGroup.c = c + + resp.Body.Close() + return secGroup, nil +} + +func (secGroup *SecGroup) ListSpaceResources() ([]SpaceResource, error) { + var spaceResources []SpaceResource + requestURL := secGroup.SpacesURL + for requestURL != "" { + spaceResp, err := secGroup.c.getSpaceResponse(requestURL) + if err != nil { + return []SpaceResource{}, err + } + for i, spaceRes := range spaceResp.Resources { + spaceRes.Entity.Guid = spaceRes.Meta.Guid + spaceRes.Entity.CreatedAt = spaceRes.Meta.CreatedAt + spaceRes.Entity.UpdatedAt = spaceRes.Meta.UpdatedAt + spaceResp.Resources[i] = spaceRes + } + spaceResources = append(spaceResources, spaceResp.Resources...) + requestURL = spaceResp.NextUrl + } + return spaceResources, nil +} + +func (secGroup *SecGroup) ListStagingSpaceResources() ([]SpaceResource, error) { + var spaceResources []SpaceResource + requestURL := secGroup.StagingSpacesURL + for requestURL != "" { + spaceResp, err := secGroup.c.getSpaceResponse(requestURL) + if err != nil { + // if this is a 404, let's make sure that it's not because we're on a legacy system + if cause := errors.Cause(err); cause != nil { + if httpErr, ok := cause.(CloudFoundryHTTPError); ok { + if httpErr.StatusCode == http.StatusNotFound { + info, infoErr := secGroup.c.GetInfo() + if infoErr != nil { + return nil, infoErr + } + + apiVersion, versionErr := semver.NewVersion(info.APIVersion) + if versionErr != nil { + return nil, versionErr + } + + if MinStagingSpacesVersion.GreaterThan(apiVersion) { + // this is probably not really an error, we're just trying to use a non-existent api + return nil, nil + } + } + } + } + + return []SpaceResource{}, err + } + for i, spaceRes := range spaceResp.Resources { + spaceRes.Entity.Guid = spaceRes.Meta.Guid + spaceRes.Entity.CreatedAt = spaceRes.Meta.CreatedAt + spaceRes.Entity.UpdatedAt = spaceRes.Meta.UpdatedAt + spaceResp.Resources[i] = spaceRes + } + spaceResources = append(spaceResources, spaceResp.Resources...) + requestURL = spaceResp.NextUrl + } + return spaceResources, nil +} + +/* +CreateSecGroup contacts the CF endpoint for creating a new security group. +name: the name to give to the created security group +rules: A slice of rule objects that describe the rules that this security group enforces. + This can technically be nil or an empty slice - we won't judge you +spaceGuids: The security group will be associated with the spaces specified by the contents of this slice. + If nil, the security group will not be associated with any spaces initially. +*/ +func (c *Client) CreateSecGroup(name string, rules []SecGroupRule, spaceGuids []string) (*SecGroup, error) { + return c.secGroupCreateHelper("/v2/security_groups", "POST", name, rules, spaceGuids) +} + +/* +UpdateSecGroup contacts the CF endpoint to update an existing security group. +guid: identifies the security group that you would like to update. +name: the new name to give to the security group +rules: A slice of rule objects that describe the rules that this security group enforces. + If this is left nil, the rules will not be changed. +spaceGuids: The security group will be associated with the spaces specified by the contents of this slice. + If nil, the space associations will not be changed. +*/ +func (c *Client) UpdateSecGroup(guid, name string, rules []SecGroupRule, spaceGuids []string) (*SecGroup, error) { + return c.secGroupCreateHelper("/v2/security_groups/"+guid, "PUT", name, rules, spaceGuids) +} + +/* +DeleteSecGroup contacts the CF endpoint to delete an existing security group. +guid: Indentifies the security group to be deleted. +*/ +func (c *Client) DeleteSecGroup(guid string) error { + // Perform the DELETE and check for errors + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/security_groups/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +GetSecGroup contacts the CF endpoint for fetching the info for a particular security group. +guid: Identifies the security group to fetch information from +*/ +func (c *Client) GetSecGroup(guid string) (*SecGroup, error) { + // Perform the GET and check for errors + resp, err := c.DoRequest(c.NewRequest("GET", "/v2/security_groups/"+guid)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + // get the json out of the response body + return respBodyToSecGroup(resp.Body, c) +} + +/* +BindSecGroup contacts the CF endpoint to associate a space with a security group +secGUID: identifies the security group to add a space to +spaceGUID: identifies the space to associate +*/ +func (c *Client) BindSecGroup(secGUID, spaceGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/security_groups/%s/spaces/%s", secGUID, spaceGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +BindSpaceStagingSecGroup contacts the CF endpoint to associate a space with a security group for staging functions only +secGUID: identifies the security group to add a space to +spaceGUID: identifies the space to associate +*/ +func (c *Client) BindStagingSecGroupToSpace(secGUID, spaceGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/security_groups/%s/staging_spaces/%s", secGUID, spaceGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +BindRunningSecGroup contacts the CF endpoint to associate a security group +secGUID: identifies the security group to add a space to +*/ +func (c *Client) BindRunningSecGroup(secGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/config/running_security_groups/%s", secGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +UnbindRunningSecGroup contacts the CF endpoint to dis-associate a security group +secGUID: identifies the security group to add a space to +*/ +func (c *Client) UnbindRunningSecGroup(secGUID string) error { + // Perform the DELETE and check for errors + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/config/running_security_groups/%s", secGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +BindStagingSecGroup contacts the CF endpoint to associate a space with a security group +secGUID: identifies the security group to add a space to +*/ +func (c *Client) BindStagingSecGroup(secGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/config/staging_security_groups/%s", secGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +UnbindStagingSecGroup contacts the CF endpoint to dis-associate a space with a security group +secGUID: identifies the security group to add a space to +*/ +func (c *Client) UnbindStagingSecGroup(secGUID string) error { + // Perform the DELETE and check for errors + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/config/staging_security_groups/%s", secGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +/* +UnbindSecGroup contacts the CF endpoint to dissociate a space from a security group +secGUID: identifies the security group to remove a space from +spaceGUID: identifies the space to dissociate from the security group +*/ +func (c *Client) UnbindSecGroup(secGUID, spaceGUID string) error { + // Perform the DELETE and check for errors + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/security_groups/%s/spaces/%s", secGUID, spaceGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +// Reads most security group response bodies into a SecGroup object +func respBodyToSecGroup(body io.ReadCloser, c *Client) (*SecGroup, error) { + // get the json from the response body + bodyRaw, err := ioutil.ReadAll(body) + if err != nil { + return nil, errors.Wrap(err, "Could not read response body") + } + jStruct := SecGroupResource{} + // make it a SecGroup + err = json.Unmarshal(bodyRaw, &jStruct) + if err != nil { + return nil, errors.Wrap(err, "Could not unmarshal response body as json") + } + // pull a few extra fields from other places + ret := jStruct.Entity + ret.Guid = jStruct.Meta.Guid + ret.CreatedAt = jStruct.Meta.CreatedAt + ret.UpdatedAt = jStruct.Meta.UpdatedAt + ret.c = c + return &ret, nil +} + +func convertStructToMap(st interface{}) map[string]interface{} { + reqRules := make(map[string]interface{}) + + v := reflect.ValueOf(st) + t := reflect.TypeOf(st) + + for i := 0; i < v.NumField(); i++ { + key := strings.ToLower(t.Field(i).Name) + typ := v.FieldByName(t.Field(i).Name).Kind().String() + structTag := t.Field(i).Tag.Get("json") + jsonName := strings.TrimSpace(strings.Split(structTag, ",")[0]) + value := v.FieldByName(t.Field(i).Name) + + // if jsonName is not empty use it for the key + if jsonName != "" { + key = jsonName + } + + switch typ { + case "string": + if !(value.String() == "" && strings.Contains(structTag, "omitempty")) { + reqRules[key] = value.String() + } + case "int": + reqRules[key] = value.Int() + default: + reqRules[key] = value.Interface() + } + } + + return reqRules +} + +// Create and Update secGroup pretty much do the same thing, so this function abstracts those out. +func (c *Client) secGroupCreateHelper(url, method, name string, rules []SecGroupRule, spaceGuids []string) (*SecGroup, error) { + reqRules := make([]map[string]interface{}, len(rules)) + + for i, rule := range rules { + reqRules[i] = convertStructToMap(rule) + protocol := strings.ToLower(reqRules[i]["protocol"].(string)) + + // if not icmp protocol need to remove the Code/Type fields + if protocol != "icmp" { + delete(reqRules[i], "code") + delete(reqRules[i], "type") + } + } + + req := c.NewRequest(method, url) + // set up request body + inputs := map[string]interface{}{ + "name": name, + "rules": reqRules, + } + + if spaceGuids != nil { + inputs["space_guids"] = spaceGuids + } + req.obj = inputs + // fire off the request and check for problems + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { // Both create and update should give 201 CREATED + var response SecGroupCreateResponse + + bodyRaw, _ := ioutil.ReadAll(resp.Body) + + err = json.Unmarshal(bodyRaw, &response) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling response") + } + + return nil, fmt.Errorf(`Request failed CF API returned with status code %d +------------------------------- +Error Code %s +Code %d +Description %s`, + resp.StatusCode, response.ErrorCode, response.Code, response.Description) + } + // get the json from the response body + return respBodyToSecGroup(resp.Body, c) +} + +func getMinStagingSpacesVersion() *semver.Version { + v, _ := semver.NewVersion("2.68.0") + return v +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/service_bindings.go b/vendor/github.com/cloudfoundry-community/go-cfclient/service_bindings.go new file mode 100644 index 0000000..b495897 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/service_bindings.go @@ -0,0 +1,181 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceBindingsResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + Resources []ServiceBindingResource `json:"resources"` + NextUrl string `json:"next_url"` +} + +type ServiceBindingResource struct { + Meta Meta `json:"metadata"` + Entity ServiceBinding `json:"entity"` +} + +type ServiceBinding struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + AppGuid string `json:"app_guid"` + ServiceInstanceGuid string `json:"service_instance_guid"` + Credentials interface{} `json:"credentials"` + BindingOptions interface{} `json:"binding_options"` + GatewayData interface{} `json:"gateway_data"` + GatewayName string `json:"gateway_name"` + SyslogDrainUrl string `json:"syslog_drain_url"` + VolumeMounts interface{} `json:"volume_mounts"` + AppUrl string `json:"app_url"` + ServiceInstanceUrl string `json:"service_instance_url"` + c *Client +} + +func (c *Client) ListServiceBindingsByQuery(query url.Values) ([]ServiceBinding, error) { + var serviceBindings []ServiceBinding + requestUrl := "/v2/service_bindings?" + query.Encode() + + for { + var serviceBindingsResp ServiceBindingsResponse + + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service bindings") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading service bindings request:") + } + + err = json.Unmarshal(resBody, &serviceBindingsResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling service bindings") + } + for _, serviceBinding := range serviceBindingsResp.Resources { + serviceBinding.Entity.Guid = serviceBinding.Meta.Guid + serviceBinding.Entity.CreatedAt = serviceBinding.Meta.CreatedAt + serviceBinding.Entity.UpdatedAt = serviceBinding.Meta.UpdatedAt + serviceBinding.Entity.c = c + serviceBindings = append(serviceBindings, serviceBinding.Entity) + } + requestUrl = serviceBindingsResp.NextUrl + if requestUrl == "" { + break + } + } + + return serviceBindings, nil +} + +func (c *Client) ListServiceBindings() ([]ServiceBinding, error) { + return c.ListServiceBindingsByQuery(nil) +} + +func (c *Client) GetServiceBindingByGuid(guid string) (ServiceBinding, error) { + var serviceBinding ServiceBindingResource + r := c.NewRequest("GET", "/v2/service_bindings/"+url.QueryEscape(guid)) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceBinding{}, errors.Wrap(err, "Error requesting serving binding") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceBinding{}, errors.Wrap(err, "Error reading service binding response body") + } + err = json.Unmarshal(resBody, &serviceBinding) + if err != nil { + return ServiceBinding{}, errors.Wrap(err, "Error unmarshalling service binding") + } + serviceBinding.Entity.Guid = serviceBinding.Meta.Guid + serviceBinding.Entity.CreatedAt = serviceBinding.Meta.CreatedAt + serviceBinding.Entity.UpdatedAt = serviceBinding.Meta.UpdatedAt + serviceBinding.Entity.c = c + return serviceBinding.Entity, nil +} + +func (c *Client) ServiceBindingByGuid(guid string) (ServiceBinding, error) { + return c.GetServiceBindingByGuid(guid) +} + +func (c *Client) DeleteServiceBinding(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/service_bindings/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting service binding %s, response code %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) CreateServiceBinding(appGUID, serviceInstanceGUID string) (*ServiceBinding, error) { + req := c.NewRequest("POST", "/v2/service_bindings") + req.obj = map[string]interface{}{ + "app_guid": appGUID, + "service_instance_guid": serviceInstanceGUID, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, errors.Wrapf(err, "Error binding app %s to service instance %s, response code %d", appGUID, serviceInstanceGUID, resp.StatusCode) + } + return c.handleServiceBindingResp(resp) +} + +func (c *Client) CreateRouteServiceBinding(routeGUID, serviceInstanceGUID string) error { + req := c.NewRequest("PUT", fmt.Sprintf("/v2/user_provided_service_instances/%s/routes/%s", serviceInstanceGUID, routeGUID)) + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return errors.Wrapf(err, "Error binding route %s to service instance %s, response code %d", routeGUID, serviceInstanceGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) DeleteRouteServiceBinding(routeGUID, serviceInstanceGUID string) error { + req := c.NewRequest("DELETE", fmt.Sprintf("/v2/service_instances/%s/routes/%s", serviceInstanceGUID, routeGUID)) + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error deleting bound route %s from service instance %s, response code %d", routeGUID, serviceInstanceGUID, resp.StatusCode) + } + return nil +} + +func (c *Client) handleServiceBindingResp(resp *http.Response) (*ServiceBinding, error) { + defer resp.Body.Close() + var sb ServiceBindingResource + err := json.NewDecoder(resp.Body).Decode(&sb) + if err != nil { + return nil, err + } + return c.mergeServiceBindingResource(sb), nil +} + +func (c *Client) mergeServiceBindingResource(serviceBinding ServiceBindingResource) *ServiceBinding { + serviceBinding.Entity.Guid = serviceBinding.Meta.Guid + serviceBinding.Entity.c = c + return &serviceBinding.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/service_brokers.go b/vendor/github.com/cloudfoundry-community/go-cfclient/service_brokers.go new file mode 100644 index 0000000..a3392b2 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/service_brokers.go @@ -0,0 +1,208 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceBrokerResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServiceBrokerResource `json:"resources"` +} + +type ServiceBrokerResource struct { + Meta Meta `json:"metadata"` + Entity ServiceBroker `json:"entity"` +} + +type UpdateServiceBrokerRequest struct { + Name string `json:"name"` + BrokerURL string `json:"broker_url"` + Username string `json:"auth_username"` + Password string `json:"auth_password"` +} + +type CreateServiceBrokerRequest struct { + Name string `json:"name"` + BrokerURL string `json:"broker_url"` + Username string `json:"auth_username"` + Password string `json:"auth_password"` + SpaceGUID string `json:"space_guid,omitempty"` +} + +type ServiceBroker struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + BrokerURL string `json:"broker_url"` + Username string `json:"auth_username"` + Password string `json:"auth_password"` + SpaceGUID string `json:"space_guid,omitempty"` +} + +func (c *Client) DeleteServiceBroker(guid string) error { + requestURL := fmt.Sprintf("/v2/service_brokers/%s", guid) + r := c.NewRequest("DELETE", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting service broker %s, response code: %d", guid, resp.StatusCode) + } + return nil + +} + +func (c *Client) UpdateServiceBroker(guid string, usb UpdateServiceBrokerRequest) (ServiceBroker, error) { + var serviceBrokerResource ServiceBrokerResource + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(usb) + if err != nil { + return ServiceBroker{}, err + } + req := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/service_brokers/%s", guid), buf) + resp, err := c.DoRequest(req) + if err != nil { + return ServiceBroker{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return ServiceBroker{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceBroker{}, err + } + err = json.Unmarshal(body, &serviceBrokerResource) + if err != nil { + return ServiceBroker{}, err + } + serviceBrokerResource.Entity.Guid = serviceBrokerResource.Meta.Guid + return serviceBrokerResource.Entity, nil +} + +func (c *Client) CreateServiceBroker(csb CreateServiceBrokerRequest) (ServiceBroker, error) { + var serviceBrokerResource ServiceBrokerResource + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(csb) + if err != nil { + return ServiceBroker{}, err + } + req := c.NewRequestWithBody("POST", "/v2/service_brokers", buf) + resp, err := c.DoRequest(req) + if err != nil { + return ServiceBroker{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return ServiceBroker{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceBroker{}, err + } + err = json.Unmarshal(body, &serviceBrokerResource) + if err != nil { + return ServiceBroker{}, err + } + + serviceBrokerResource.Entity.Guid = serviceBrokerResource.Meta.Guid + return serviceBrokerResource.Entity, nil +} + +func (c *Client) ListServiceBrokersByQuery(query url.Values) ([]ServiceBroker, error) { + var sbs []ServiceBroker + requestURL := "/v2/service_brokers?" + query.Encode() + for { + serviceBrokerResp, err := c.getServiceBrokerResponse(requestURL) + if err != nil { + return []ServiceBroker{}, err + } + for _, sb := range serviceBrokerResp.Resources { + sb.Entity.Guid = sb.Meta.Guid + sb.Entity.CreatedAt = sb.Meta.CreatedAt + sb.Entity.UpdatedAt = sb.Meta.UpdatedAt + sbs = append(sbs, sb.Entity) + } + requestURL = serviceBrokerResp.NextUrl + if requestURL == "" { + break + } + } + return sbs, nil +} + +func (c *Client) ListServiceBrokers() ([]ServiceBroker, error) { + return c.ListServiceBrokersByQuery(nil) +} + +func (c *Client) GetServiceBrokerByGuid(guid string) (ServiceBroker, error) { + var serviceBrokerRes ServiceBrokerResource + r := c.NewRequest("GET", "/v2/service_brokers/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceBroker{}, err + } + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return ServiceBroker{}, err + } + err = json.Unmarshal(body, &serviceBrokerRes) + if err != nil { + return ServiceBroker{}, err + } + serviceBrokerRes.Entity.Guid = serviceBrokerRes.Meta.Guid + serviceBrokerRes.Entity.CreatedAt = serviceBrokerRes.Meta.CreatedAt + serviceBrokerRes.Entity.UpdatedAt = serviceBrokerRes.Meta.UpdatedAt + return serviceBrokerRes.Entity, nil +} + +func (c *Client) GetServiceBrokerByName(name string) (ServiceBroker, error) { + q := url.Values{} + q.Set("q", "name:"+name) + sbs, err := c.ListServiceBrokersByQuery(q) + if err != nil { + return ServiceBroker{}, err + } + if len(sbs) == 0 { + cfErr := NewServiceBrokerNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return ServiceBroker{}, cfErr + } + return sbs[0], nil +} + +func (c *Client) getServiceBrokerResponse(requestURL string) (ServiceBrokerResponse, error) { + var serviceBrokerResp ServiceBrokerResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceBrokerResponse{}, errors.Wrap(err, "Error requesting Service Brokers") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return ServiceBrokerResponse{}, errors.Wrap(err, "Error reading Service Broker request") + } + err = json.Unmarshal(resBody, &serviceBrokerResp) + if err != nil { + return ServiceBrokerResponse{}, errors.Wrap(err, "Error unmarshalling Service Broker") + } + return serviceBrokerResp, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/service_instances.go b/vendor/github.com/cloudfoundry-community/go-cfclient/service_instances.go new file mode 100644 index 0000000..070c262 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/service_instances.go @@ -0,0 +1,234 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceInstancesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServiceInstanceResource `json:"resources"` +} + +type ServiceInstanceRequest struct { + Name string `json:"name"` + SpaceGuid string `json:"space_guid"` + ServicePlanGuid string `json:"service_plan_guid"` + Parameters map[string]interface{} `json:"parameters,omitempty"` + Tags []string `json:"tags,omitempty"` +} + +type ServiceInstanceUpdateRequest struct { + Name string `json:"name,omitempty"` + ServicePlanGuid string `json:"service_plan_guid,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` + Tags []string `json:"tags,omitempty"` +} + +type ServiceInstanceResource struct { + Meta Meta `json:"metadata"` + Entity ServiceInstance `json:"entity"` +} + +type ServiceInstance struct { + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Credentials map[string]interface{} `json:"credentials"` + ServicePlanGuid string `json:"service_plan_guid"` + SpaceGuid string `json:"space_guid"` + DashboardUrl string `json:"dashboard_url"` + Type string `json:"type"` + LastOperation LastOperation `json:"last_operation"` + Tags []string `json:"tags"` + ServiceGuid string `json:"service_guid"` + SpaceUrl string `json:"space_url"` + ServicePlanUrl string `json:"service_plan_url"` + ServiceBindingsUrl string `json:"service_bindings_url"` + ServiceKeysUrl string `json:"service_keys_url"` + ServiceInstanceParametersUrl string `json:"service_instance_parameters_url"` + SharedFromUrl string `json:"shared_from_url"` + SharedToUrl string `json:"shared_to_url"` + RoutesUrl string `json:"routes_url"` + ServiceUrl string `json:"service_url"` + Guid string `json:"guid"` + c *Client +} + +type LastOperation struct { + Type string `json:"type"` + State string `json:"state"` + Description string `json:"description"` + UpdatedAt string `json:"updated_at"` + CreatedAt string `json:"created_at"` +} + +func (c *Client) ListServiceInstancesByQuery(query url.Values) ([]ServiceInstance, error) { + var instances []ServiceInstance + + requestUrl := "/v2/service_instances?" + query.Encode() + for { + sir, err := c.getServiceInstancesResponse(requestUrl) + if err != nil { + return instances, err + } + for _, instance := range sir.Resources { + instances = append(instances, c.mergeServiceInstance(instance)) + } + requestUrl = sir.NextUrl + if requestUrl == "" || query.Get("page") != "" { + break + } + } + return instances, nil +} + +func (c *Client) ListServiceInstances() ([]ServiceInstance, error) { + return c.ListServiceInstancesByQuery(nil) +} + +func (c *Client) GetServiceInstanceParams(guid string) (map[string]interface{}, error) { + req := c.NewRequest("GET", "/v2/service_instances/"+guid+"/parameters") + res, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service instance parameters") + } + + defer res.Body.Close() + + var result map[string]interface{} + err = json.NewDecoder(res.Body).Decode(&result) + if err != nil { + return nil, errors.Wrap(err, "Error JSON parsing service instance parameters") + } + + return result, nil +} + +func (c *Client) GetServiceInstanceByGuid(guid string) (ServiceInstance, error) { + var sir ServiceInstanceResource + req := c.NewRequest("GET", "/v2/service_instances/"+guid) + res, err := c.DoRequest(req) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error requesting service instance") + } + defer res.Body.Close() + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error reading service instance response") + } + err = json.Unmarshal(data, &sir) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error JSON parsing service instance response") + } + return c.mergeServiceInstance(sir), nil +} + +func (c *Client) ServiceInstanceByGuid(guid string) (ServiceInstance, error) { + return c.GetServiceInstanceByGuid(guid) +} + +func (c *Client) getServiceInstancesResponse(requestUrl string) (ServiceInstancesResponse, error) { + var serviceInstancesResponse ServiceInstancesResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceInstancesResponse{}, errors.Wrap(err, "Error requesting service instances") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return ServiceInstancesResponse{}, errors.Wrap(err, "Error reading service instance request") + } + err = json.Unmarshal(resBody, &serviceInstancesResponse) + if err != nil { + return ServiceInstancesResponse{}, errors.Wrap(err, "Error unmarshalling service instance") + } + return serviceInstancesResponse, nil +} + +func (c *Client) mergeServiceInstance(instance ServiceInstanceResource) ServiceInstance { + instance.Entity.Guid = instance.Meta.Guid + instance.Entity.CreatedAt = instance.Meta.CreatedAt + instance.Entity.UpdatedAt = instance.Meta.UpdatedAt + instance.Entity.c = c + return instance.Entity +} + +func (c *Client) CreateServiceInstance(req ServiceInstanceRequest) (ServiceInstance, error) { + var sir ServiceInstanceResource + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return ServiceInstance{}, err + } + + r := c.NewRequestWithBody("POST", "/v2/service_instances?accepts_incomplete=true", buf) + + res, err := c.DoRequest(r) + if err != nil { + return ServiceInstance{}, err + } + + defer res.Body.Close() + if res.StatusCode != http.StatusAccepted && res.StatusCode != http.StatusCreated { + return ServiceInstance{}, errors.Wrapf(err, "Error creating service, response code: %d", res.StatusCode) + } + + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error reading service instance response") + } + + err = json.Unmarshal(data, &sir) + if err != nil { + return ServiceInstance{}, errors.Wrap(err, "Error JSON parsing service instance response") + } + + return c.mergeServiceInstance(sir), nil +} + +func (c *Client) UpdateSI(serviceInstanceGuid string, req ServiceInstanceUpdateRequest, async bool) error { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return err + } + return c.UpdateServiceInstance(serviceInstanceGuid, buf, async) +} + +func (c *Client) UpdateServiceInstance(serviceInstanceGuid string, updatedConfiguration io.Reader, async bool) error { + u := fmt.Sprintf("/v2/service_instances/%s?accepts_incomplete=%t", serviceInstanceGuid, async) + resp, err := c.DoRequest(c.NewRequestWithBody("PUT", u, updatedConfiguration)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusAccepted { + return errors.Wrapf(err, "Error updating service instance %s, response code %d", serviceInstanceGuid, resp.StatusCode) + } + return nil +} + +func (c *Client) DeleteServiceInstance(guid string, recursive, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/service_instances/%s?recursive=%t&accepts_incomplete=%t&async=%t", guid, recursive, async, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusAccepted { + return errors.Wrapf(err, "Error deleting service instance %s, response code %d", guid, resp.StatusCode) + } + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/service_keys.go b/vendor/github.com/cloudfoundry-community/go-cfclient/service_keys.go new file mode 100644 index 0000000..5c0806d --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/service_keys.go @@ -0,0 +1,204 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceKeysResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + Resources []ServiceKeyResource `json:"resources"` + NextUrl string `json:"next_url"` +} + +type ServiceKeyResource struct { + Meta Meta `json:"metadata"` + Entity ServiceKey `json:"entity"` +} + +type CreateServiceKeyRequest struct { + Name string `json:"name"` + ServiceInstanceGuid string `json:"service_instance_guid"` + Parameters interface{} `json:"parameters,omitempty"` +} + +type ServiceKey struct { + Name string `json:"name"` + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + ServiceInstanceGuid string `json:"service_instance_guid"` + Credentials interface{} `json:"credentials"` + ServiceInstanceUrl string `json:"service_instance_url"` + c *Client +} + +func (c *Client) ListServiceKeysByQuery(query url.Values) ([]ServiceKey, error) { + var serviceKeys []ServiceKey + requestUrl := "/v2/service_keys?" + query.Encode() + + for { + var serviceKeysResp ServiceKeysResponse + + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service keys") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading service keys request:") + } + + err = json.Unmarshal(resBody, &serviceKeysResp) + if err != nil { + return nil, errors.Wrapf(err, "Error unmarshaling service keys: %q", string(resBody)) + } + for _, serviceKey := range serviceKeysResp.Resources { + serviceKey.Entity.Guid = serviceKey.Meta.Guid + serviceKey.Entity.CreatedAt = serviceKey.Meta.CreatedAt + serviceKey.Entity.UpdatedAt = serviceKey.Meta.UpdatedAt + serviceKey.Entity.c = c + serviceKeys = append(serviceKeys, serviceKey.Entity) + } + + requestUrl = serviceKeysResp.NextUrl + if requestUrl == "" { + break + } + } + + return serviceKeys, nil +} + +func (c *Client) GetServiceKeyByGuid(guid string) (ServiceKey, error) { + var serviceKey ServiceKeyResource + r := c.NewRequest("GET", "/v2/service_keys/"+url.QueryEscape(guid)) + resp, err := c.DoRequest(r) + if err != nil { + return ServiceKey{}, errors.Wrap(err, "Error requesting serving Key") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceKey{}, errors.Wrap(err, "Error reading service Key response body") + } + err = json.Unmarshal(resBody, &serviceKey) + if err != nil { + return ServiceKey{}, errors.Wrap(err, "Error unmarshalling service Key") + } + serviceKey.Entity.Guid = serviceKey.Meta.Guid + serviceKey.Entity.c = c + return serviceKey.Entity, nil +} + +func (c *Client) ListServiceKeys() ([]ServiceKey, error) { + return c.ListServiceKeysByQuery(nil) +} + +func (c *Client) GetServiceKeyByName(name string) (ServiceKey, error) { + var serviceKey ServiceKey + q := url.Values{} + q.Set("q", "name:"+name) + serviceKeys, err := c.ListServiceKeysByQuery(q) + if err != nil { + return serviceKey, err + } + if len(serviceKeys) == 0 { + cfErr := NewServiceKeyNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, name) + return ServiceKey{}, cfErr + } + return serviceKeys[0], nil +} + +// GetServiceKeyByInstanceGuid is deprecated in favor of GetServiceKeysByInstanceGuid +func (c *Client) GetServiceKeyByInstanceGuid(guid string) (ServiceKey, error) { + q := url.Values{} + q.Set("q", "service_instance_guid:"+guid) + serviceKeys, err := c.ListServiceKeysByQuery(q) + if err != nil { + return ServiceKey{}, err + } + if len(serviceKeys) == 0 { + cfErr := NewServiceKeyNotFoundError() + return ServiceKey{}, cfErr + } + return serviceKeys[0], nil +} + +// GetServiceKeysByInstanceGuid returns the service keys for a service instance. +// If none are found, it returns an error. +func (c *Client) GetServiceKeysByInstanceGuid(guid string) ([]ServiceKey, error) { + q := url.Values{} + q.Set("q", "service_instance_guid:"+guid) + serviceKeys, err := c.ListServiceKeysByQuery(q) + if err != nil { + return serviceKeys, err + } + if len(serviceKeys) == 0 { + cfErr := NewServiceKeyNotFoundError() + return serviceKeys, cfErr + } + return serviceKeys, nil +} + +// CreateServiceKey creates a service key from the request. If a service key +// exists already, it returns an error containing `CF-ServiceKeyNameTaken` +func (c *Client) CreateServiceKey(csr CreateServiceKeyRequest) (ServiceKey, error) { + var serviceKeyResource ServiceKeyResource + + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(csr) + if err != nil { + return ServiceKey{}, err + } + req := c.NewRequestWithBody("POST", "/v2/service_keys", buf) + resp, err := c.DoRequest(req) + if err != nil { + return ServiceKey{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return ServiceKey{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceKey{}, err + } + err = json.Unmarshal(body, &serviceKeyResource) + if err != nil { + return ServiceKey{}, err + } + + return c.mergeServiceKey(serviceKeyResource), nil +} + +// DeleteServiceKey removes a service key instance +func (c *Client) DeleteServiceKey(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/service_keys/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting service instance key %s, response code %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) mergeServiceKey(key ServiceKeyResource) ServiceKey { + key.Entity.Guid = key.Meta.Guid + key.Entity.CreatedAt = key.Meta.CreatedAt + key.Entity.UpdatedAt = key.Meta.UpdatedAt + key.Entity.c = c + return key.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/service_plan_visibilities.go b/vendor/github.com/cloudfoundry-community/go-cfclient/service_plan_visibilities.go new file mode 100644 index 0000000..b875b37 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/service_plan_visibilities.go @@ -0,0 +1,174 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type ServicePlanVisibilitiesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServicePlanVisibilityResource `json:"resources"` +} + +type ServicePlanVisibilityResource struct { + Meta Meta `json:"metadata"` + Entity ServicePlanVisibility `json:"entity"` +} + +type ServicePlanVisibility struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + ServicePlanGuid string `json:"service_plan_guid"` + OrganizationGuid string `json:"organization_guid"` + ServicePlanUrl string `json:"service_plan_url"` + OrganizationUrl string `json:"organization_url"` + c *Client +} + +func (c *Client) ListServicePlanVisibilitiesByQuery(query url.Values) ([]ServicePlanVisibility, error) { + var servicePlanVisibilities []ServicePlanVisibility + requestUrl := "/v2/service_plan_visibilities?" + query.Encode() + for { + var servicePlanVisibilitiesResp ServicePlanVisibilitiesResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service plan visibilities") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading service plan visibilities request:") + } + + err = json.Unmarshal(resBody, &servicePlanVisibilitiesResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling service plan visibilities") + } + for _, servicePlanVisibility := range servicePlanVisibilitiesResp.Resources { + servicePlanVisibility.Entity.Guid = servicePlanVisibility.Meta.Guid + servicePlanVisibility.Entity.CreatedAt = servicePlanVisibility.Meta.CreatedAt + servicePlanVisibility.Entity.UpdatedAt = servicePlanVisibility.Meta.UpdatedAt + servicePlanVisibility.Entity.c = c + servicePlanVisibilities = append(servicePlanVisibilities, servicePlanVisibility.Entity) + } + requestUrl = servicePlanVisibilitiesResp.NextUrl + if requestUrl == "" { + break + } + } + return servicePlanVisibilities, nil +} + +func (c *Client) ListServicePlanVisibilities() ([]ServicePlanVisibility, error) { + return c.ListServicePlanVisibilitiesByQuery(nil) +} + +func (c *Client) GetServicePlanVisibilityByGuid(guid string) (ServicePlanVisibility, error) { + r := c.NewRequest("GET", "/v2/service_plan_visibilities/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return ServicePlanVisibility{}, err + } + defer resp.Body.Close() + return respBodyToServicePlanVisibility(resp.Body, c) +} + +// a uniqueID is the id of the service in the catalog and not in cf internal db +func (c *Client) CreateServicePlanVisibilityByUniqueId(uniqueId string, organizationGuid string) (ServicePlanVisibility, error) { + q := url.Values{} + q.Set("q", fmt.Sprintf("unique_id:%s", uniqueId)) + plans, err := c.ListServicePlansByQuery(q) + if err != nil { + return ServicePlanVisibility{}, errors.Wrap(err, fmt.Sprintf("Couldn't find a service plan with unique_id: %s", uniqueId)) + } + return c.CreateServicePlanVisibility(plans[0].Guid, organizationGuid) +} + +func (c *Client) CreateServicePlanVisibility(servicePlanGuid string, organizationGuid string) (ServicePlanVisibility, error) { + req := c.NewRequest("POST", "/v2/service_plan_visibilities") + req.obj = map[string]interface{}{ + "service_plan_guid": servicePlanGuid, + "organization_guid": organizationGuid, + } + resp, err := c.DoRequest(req) + if err != nil { + return ServicePlanVisibility{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return ServicePlanVisibility{}, errors.Wrapf(err, "Error creating service plan visibility, response code: %d", resp.StatusCode) + } + return respBodyToServicePlanVisibility(resp.Body, c) +} + +func (c *Client) DeleteServicePlanVisibilityByPlanAndOrg(servicePlanGuid string, organizationGuid string, async bool) error { + q := url.Values{} + q.Set("q", fmt.Sprintf("organization_guid:%s;service_plan_guid:%s", organizationGuid, servicePlanGuid)) + plans, err := c.ListServicePlanVisibilitiesByQuery(q) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("Couldn't find a service plan visibility for service plan %s and org %s", servicePlanGuid, organizationGuid)) + } + if len(plans) != 1 { + return fmt.Errorf("Query for a service plan visibility did not return exactly one result when searching for a service plan visibility for service plan %s and org %s", + servicePlanGuid, organizationGuid) + } + return c.DeleteServicePlanVisibility(plans[0].Guid, async) +} + +func (c *Client) DeleteServicePlanVisibility(guid string, async bool) error { + req := c.NewRequest("DELETE", fmt.Sprintf("/v2/service_plan_visibilities/%s?async=%v", guid, async)) + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting service plan visibility, response code: %d", resp.StatusCode) + } + return nil +} + +func (c *Client) UpdateServicePlanVisibility(guid string, servicePlanGuid string, organizationGuid string) (ServicePlanVisibility, error) { + req := c.NewRequest("PUT", "/v2/service_plan_visibilities/"+guid) + req.obj = map[string]interface{}{ + "service_plan_guid": servicePlanGuid, + "organization_guid": organizationGuid, + } + resp, err := c.DoRequest(req) + if err != nil { + return ServicePlanVisibility{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return ServicePlanVisibility{}, errors.Wrapf(err, "Error updating service plan visibility, response code: %d", resp.StatusCode) + } + return respBodyToServicePlanVisibility(resp.Body, c) +} + +func respBodyToServicePlanVisibility(body io.ReadCloser, c *Client) (ServicePlanVisibility, error) { + bodyRaw, err := ioutil.ReadAll(body) + if err != nil { + return ServicePlanVisibility{}, err + } + servicePlanVisibilityRes := ServicePlanVisibilityResource{} + err = json.Unmarshal(bodyRaw, &servicePlanVisibilityRes) + if err != nil { + return ServicePlanVisibility{}, err + } + servicePlanVisibility := servicePlanVisibilityRes.Entity + servicePlanVisibility.Guid = servicePlanVisibilityRes.Meta.Guid + servicePlanVisibility.CreatedAt = servicePlanVisibilityRes.Meta.CreatedAt + servicePlanVisibility.UpdatedAt = servicePlanVisibilityRes.Meta.UpdatedAt + servicePlanVisibility.c = c + return servicePlanVisibility, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/service_plans.go b/vendor/github.com/cloudfoundry-community/go-cfclient/service_plans.go new file mode 100644 index 0000000..2fa0961 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/service_plans.go @@ -0,0 +1,131 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + + "github.com/pkg/errors" +) + +type ServicePlansResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServicePlanResource `json:"resources"` +} + +type ServicePlanResource struct { + Meta Meta `json:"metadata"` + Entity ServicePlan `json:"entity"` +} + +type ServicePlan struct { + Name string `json:"name"` + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Free bool `json:"free"` + Description string `json:"description"` + ServiceGuid string `json:"service_guid"` + Extra interface{} `json:"extra"` + UniqueId string `json:"unique_id"` + Public bool `json:"public"` + Active bool `json:"active"` + Bindable bool `json:"bindable"` + PlanUpdateable bool `json:"plan_updateable"` + ServiceUrl string `json:"service_url"` + ServiceInstancesUrl string `json:"service_instances_url"` + c *Client +} + +func (c *Client) ListServicePlansByQuery(query url.Values) ([]ServicePlan, error) { + var servicePlans []ServicePlan + requestURL := "/v2/service_plans?" + query.Encode() + for { + var servicePlansResp ServicePlansResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting service plans") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading service plans request:") + } + err = json.Unmarshal(resBody, &servicePlansResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling service plans") + } + for _, servicePlan := range servicePlansResp.Resources { + servicePlan.Entity.Guid = servicePlan.Meta.Guid + servicePlan.Entity.CreatedAt = servicePlan.Meta.CreatedAt + servicePlan.Entity.UpdatedAt = servicePlan.Meta.UpdatedAt + servicePlan.Entity.c = c + servicePlans = append(servicePlans, servicePlan.Entity) + } + requestURL = servicePlansResp.NextUrl + if requestURL == "" { + break + } + } + return servicePlans, nil +} + +func (c *Client) ListServicePlans() ([]ServicePlan, error) { + return c.ListServicePlansByQuery(nil) +} + +func (c *Client) GetServicePlanByGUID(guid string) (*ServicePlan, error) { + var ( + plan *ServicePlan + planResponse ServicePlanResource + ) + + r := c.NewRequest("GET", "/v2/service_plans/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + + err = json.Unmarshal(body, &planResponse) + if err != nil { + return nil, err + } + + planResponse.Entity.Guid = planResponse.Meta.Guid + planResponse.Entity.CreatedAt = planResponse.Meta.CreatedAt + planResponse.Entity.UpdatedAt = planResponse.Meta.UpdatedAt + plan = &planResponse.Entity + + return plan, nil +} + +func (c *Client) MakeServicePlanPublic(servicePlanGUID string) error { + return c.setPlanGlobalVisibility(servicePlanGUID, true) +} + +func (c *Client) MakeServicePlanPrivate(servicePlanGUID string) error { + return c.setPlanGlobalVisibility(servicePlanGUID, false) +} + +func (c *Client) setPlanGlobalVisibility(servicePlanGUID string, public bool) error { + bodyString := fmt.Sprintf(`{"public": %t}`, public) + req := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/service_plans/%s", servicePlanGUID), bytes.NewBufferString(bodyString)) + + resp, err := c.DoRequest(req) + if err != nil { + return err + } + defer resp.Body.Close() + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/service_usage_events.go b/vendor/github.com/cloudfoundry-community/go-cfclient/service_usage_events.go new file mode 100644 index 0000000..17fb8a2 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/service_usage_events.go @@ -0,0 +1,72 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/url" + + "github.com/pkg/errors" +) + +type ServiceUsageEvent struct { + GUID string `json:"guid"` + CreatedAt string `json:"created_at"` + State string `json:"state"` + OrgGUID string `json:"org_guid"` + SpaceGUID string `json:"space_guid"` + SpaceName string `json:"space_name"` + ServiceInstanceGUID string `json:"service_instance_guid"` + ServiceInstanceName string `json:"service_instance_name"` + ServiceInstanceType string `json:"service_instance_type"` + ServicePlanGUID string `json:"service_plan_guid"` + ServicePlanName string `json:"service_plan_name"` + ServiceGUID string `json:"service_guid"` + ServiceLabel string `json:"service_label"` + c *Client +} + +type ServiceUsageEventsResponse struct { + TotalResults int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []ServiceUsageEventResource `json:"resources"` +} + +type ServiceUsageEventResource struct { + Meta Meta `json:"metadata"` + Entity ServiceUsageEvent `json:"entity"` +} + +// ListServiceUsageEventsByQuery lists all events matching the provided query. +func (c *Client) ListServiceUsageEventsByQuery(query url.Values) ([]ServiceUsageEvent, error) { + var serviceUsageEvents []ServiceUsageEvent + requestURL := fmt.Sprintf("/v2/service_usage_events?%s", query.Encode()) + for { + var serviceUsageEventsResponse ServiceUsageEventsResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "error requesting events") + } + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&serviceUsageEventsResponse); err != nil { + return nil, errors.Wrap(err, "error unmarshaling events") + } + for _, e := range serviceUsageEventsResponse.Resources { + e.Entity.GUID = e.Meta.Guid + e.Entity.CreatedAt = e.Meta.CreatedAt + e.Entity.c = c + serviceUsageEvents = append(serviceUsageEvents, e.Entity) + } + requestURL = serviceUsageEventsResponse.NextURL + if requestURL == "" { + break + } + } + return serviceUsageEvents, nil +} + +// ListServiceUsageEvents lists all unfiltered events. +func (c *Client) ListServiceUsageEvents() ([]ServiceUsageEvent, error) { + return c.ListServiceUsageEventsByQuery(nil) +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/services.go b/vendor/github.com/cloudfoundry-community/go-cfclient/services.go new file mode 100644 index 0000000..b93e4c8 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/services.go @@ -0,0 +1,128 @@ +package cfclient + +import ( + "encoding/json" + "io/ioutil" + "net/url" + + "github.com/pkg/errors" +) + +type ServicesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []ServicesResource `json:"resources"` +} + +type ServicesResource struct { + Meta Meta `json:"metadata"` + Entity Service `json:"entity"` +} + +type Service struct { + Guid string `json:"guid"` + Label string `json:"label"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Description string `json:"description"` + Active bool `json:"active"` + Bindable bool `json:"bindable"` + ServiceBrokerGuid string `json:"service_broker_guid"` + ServiceBrokerName string `json:"service_broker_name"` + PlanUpdateable bool `json:"plan_updateable"` + Tags []string `json:"tags"` + UniqueID string `json:"unique_id"` + Extra string `json:"extra"` + Requires []string `json:"requires"` + InstancesRetrievable bool `json:"instances_retrievable"` + BindingsRetrievable bool `json:"bindings_retrievable"` + c *Client +} + +type ServiceSummary struct { + Guid string `json:"guid"` + Name string `json:"name"` + BoundAppCount int `json:"bound_app_count"` + DashboardURL string `json:"dashboard_url"` + ServiceBrokerName string `json:"service_broker_name"` + MaintenanceInfo MaintenanceInfo `json:"maintenance_info"` + ServicePlan struct { + Guid string `json:"guid"` + Name string `json:"name"` + MaintenanceInfo MaintenanceInfo `json:"maintenance_info"` + Service struct { + Guid string `json:"guid"` + Label string `json:"label"` + Provider string `json:"provider"` + Version string `json:"version"` + } `json:"service"` + } `json:"service_plan"` +} + +type MaintenanceInfo struct { + Version string `json:"version"` + Description string `json:"description"` +} + +func (c *Client) GetServiceByGuid(guid string) (Service, error) { + var serviceRes ServicesResource + r := c.NewRequest("GET", "/v2/services/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return Service{}, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return Service{}, err + } + err = json.Unmarshal(body, &serviceRes) + if err != nil { + return Service{}, err + } + serviceRes.Entity.Guid = serviceRes.Meta.Guid + serviceRes.Entity.CreatedAt = serviceRes.Meta.CreatedAt + serviceRes.Entity.UpdatedAt = serviceRes.Meta.UpdatedAt + return serviceRes.Entity, nil + +} + +func (c *Client) ListServicesByQuery(query url.Values) ([]Service, error) { + var services []Service + requestURL := "/v2/services?" + query.Encode() + for { + var serviceResp ServicesResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting services") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading services request:") + } + + err = json.Unmarshal(resBody, &serviceResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling services") + } + for _, service := range serviceResp.Resources { + service.Entity.Guid = service.Meta.Guid + service.Entity.CreatedAt = service.Meta.CreatedAt + service.Entity.UpdatedAt = service.Meta.UpdatedAt + service.Entity.c = c + services = append(services, service.Entity) + } + requestURL = serviceResp.NextUrl + if requestURL == "" { + break + } + } + return services, nil +} + +func (c *Client) ListServices() ([]Service, error) { + return c.ListServicesByQuery(nil) +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/space_quotas.go b/vendor/github.com/cloudfoundry-community/go-cfclient/space_quotas.go new file mode 100644 index 0000000..216ba50 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/space_quotas.go @@ -0,0 +1,187 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type SpaceQuotasResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SpaceQuotasResource `json:"resources"` +} + +type SpaceQuotasResource struct { + Meta Meta `json:"metadata"` + Entity SpaceQuota `json:"entity"` +} + +type SpaceQuotaRequest struct { + Name string `json:"name"` + OrganizationGuid string `json:"organization_guid"` + NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` + TotalServices int `json:"total_services"` + TotalRoutes int `json:"total_routes"` + MemoryLimit int `json:"memory_limit"` + InstanceMemoryLimit int `json:"instance_memory_limit"` + AppInstanceLimit int `json:"app_instance_limit"` + AppTaskLimit int `json:"app_task_limit"` + TotalServiceKeys int `json:"total_service_keys"` + TotalReservedRoutePorts int `json:"total_reserved_route_ports"` +} + +type SpaceQuota struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Name string `json:"name"` + OrganizationGuid string `json:"organization_guid"` + NonBasicServicesAllowed bool `json:"non_basic_services_allowed"` + TotalServices int `json:"total_services"` + TotalRoutes int `json:"total_routes"` + MemoryLimit int `json:"memory_limit"` + InstanceMemoryLimit int `json:"instance_memory_limit"` + AppInstanceLimit int `json:"app_instance_limit"` + AppTaskLimit int `json:"app_task_limit"` + TotalServiceKeys int `json:"total_service_keys"` + TotalReservedRoutePorts int `json:"total_reserved_route_ports"` + c *Client +} + +func (c *Client) ListSpaceQuotasByQuery(query url.Values) ([]SpaceQuota, error) { + var spaceQuotas []SpaceQuota + requestUrl := "/v2/space_quota_definitions?" + query.Encode() + for { + spaceQuotasResp, err := c.getSpaceQuotasResponse(requestUrl) + if err != nil { + return []SpaceQuota{}, err + } + for _, space := range spaceQuotasResp.Resources { + space.Entity.Guid = space.Meta.Guid + space.Entity.CreatedAt = space.Meta.CreatedAt + space.Entity.UpdatedAt = space.Meta.UpdatedAt + space.Entity.c = c + spaceQuotas = append(spaceQuotas, space.Entity) + } + requestUrl = spaceQuotasResp.NextUrl + if requestUrl == "" { + break + } + } + return spaceQuotas, nil +} + +func (c *Client) ListSpaceQuotas() ([]SpaceQuota, error) { + return c.ListSpaceQuotasByQuery(nil) +} + +func (c *Client) GetSpaceQuotaByName(name string) (SpaceQuota, error) { + q := url.Values{} + q.Set("q", "name:"+name) + spaceQuotas, err := c.ListSpaceQuotasByQuery(q) + if err != nil { + return SpaceQuota{}, err + } + if len(spaceQuotas) != 1 { + return SpaceQuota{}, fmt.Errorf("Unable to find space quota " + name) + } + return spaceQuotas[0], nil +} + +func (c *Client) getSpaceQuotasResponse(requestUrl string) (SpaceQuotasResponse, error) { + var spaceQuotasResp SpaceQuotasResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return SpaceQuotasResponse{}, errors.Wrap(err, "Error requesting space quotas") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return SpaceQuotasResponse{}, errors.Wrap(err, "Error reading space quotas body") + } + err = json.Unmarshal(resBody, &spaceQuotasResp) + if err != nil { + return SpaceQuotasResponse{}, errors.Wrap(err, "Error unmarshalling space quotas") + } + return spaceQuotasResp, nil +} + +func (c *Client) AssignSpaceQuota(quotaGUID, spaceGUID string) error { + // Perform the PUT and check for errors + resp, err := c.DoRequest(c.NewRequest("PUT", fmt.Sprintf("/v2/space_quota_definitions/%s/spaces/%s", quotaGUID, spaceGUID))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { // 201 + return fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return nil +} + +func (c *Client) CreateSpaceQuota(spaceQuote SpaceQuotaRequest) (*SpaceQuota, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(spaceQuote) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("POST", "/v2/space_quota_definitions", buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleSpaceQuotaResp(resp) +} + +func (c *Client) UpdateSpaceQuota(spaceQuotaGUID string, spaceQuote SpaceQuotaRequest) (*SpaceQuota, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(spaceQuote) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/space_quota_definitions/%s", spaceQuotaGUID), buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleSpaceQuotaResp(resp) +} + +func (c *Client) handleSpaceQuotaResp(resp *http.Response) (*SpaceQuota, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var spaceQuotasResource SpaceQuotasResource + err = json.Unmarshal(body, &spaceQuotasResource) + if err != nil { + return nil, err + } + return c.mergeSpaceQuotaResource(spaceQuotasResource), nil +} + +func (c *Client) mergeSpaceQuotaResource(spaceQuote SpaceQuotasResource) *SpaceQuota { + spaceQuote.Entity.Guid = spaceQuote.Meta.Guid + spaceQuote.Entity.CreatedAt = spaceQuote.Meta.CreatedAt + spaceQuote.Entity.UpdatedAt = spaceQuote.Meta.UpdatedAt + spaceQuote.Entity.c = c + return &spaceQuote.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/spaces.go b/vendor/github.com/cloudfoundry-community/go-cfclient/spaces.go new file mode 100644 index 0000000..c85e5b0 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/spaces.go @@ -0,0 +1,829 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/pkg/errors" +) + +type SpaceRequest struct { + Name string `json:"name"` + OrganizationGuid string `json:"organization_guid"` + DeveloperGuid []string `json:"developer_guids,omitempty"` + ManagerGuid []string `json:"manager_guids,omitempty"` + AuditorGuid []string `json:"auditor_guids,omitempty"` + DomainGuid []string `json:"domain_guids,omitempty"` + SecurityGroupGuids []string `json:"security_group_guids,omitempty"` + SpaceQuotaDefGuid string `json:"space_quota_definition_guid,omitempty"` + IsolationSegmentGuid string `json:"isolation_segment_guid,omitempty"` + AllowSSH bool `json:"allow_ssh"` +} + +type SpaceResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SpaceResource `json:"resources"` +} + +type SpaceResource struct { + Meta Meta `json:"metadata"` + Entity Space `json:"entity"` +} + +type ServicePlanEntity struct { + Name string `json:"name"` + Free bool `json:"free"` + Public bool `json:"public"` + Active bool `json:"active"` + Description string `json:"description"` + ServiceOfferingGUID string `json:"service_guid"` + ServiceOffering ServiceOfferingResource `json:"service"` +} + +type ServiceOfferingExtra struct { + DisplayName string `json:"displayName"` + DocumentationURL string `json:"documentationURL"` + LongDescription string `json:"longDescription"` +} + +type ServiceOfferingEntity struct { + Label string + Description string + Provider string `json:"provider"` + BrokerGUID string `json:"service_broker_guid"` + Requires []string `json:"requires"` + ServicePlans []interface{} `json:"service_plans"` + Extra ServiceOfferingExtra +} + +type ServiceOfferingResource struct { + Metadata Meta + Entity ServiceOfferingEntity +} + +type ServiceOfferingResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + PrevUrl string `json:"prev_url"` + Resources []ServiceOfferingResource `json:"resources"` +} + +type SpaceUserResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextURL string `json:"next_url"` + Resources []UserResource `json:"resources"` +} + +type Space struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + OrganizationGuid string `json:"organization_guid"` + OrgURL string `json:"organization_url"` + OrgData OrgResource `json:"organization"` + QuotaDefinitionGuid string `json:"space_quota_definition_guid"` + IsolationSegmentGuid string `json:"isolation_segment_guid"` + AllowSSH bool `json:"allow_ssh"` + c *Client +} + +type SpaceSummary struct { + Guid string `json:"guid"` + Name string `json:"name"` + Apps []AppSummary `json:"apps"` + Services []ServiceSummary `json:"services"` +} + +type SpaceRoleResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []SpaceRoleResource `json:"resources"` +} + +type SpaceRoleResource struct { + Meta Meta `json:"metadata"` + Entity SpaceRole `json:"entity"` +} + +type SpaceRole struct { + Guid string `json:"guid"` + Admin bool `json:"admin"` + Active bool `json:"active"` + DefaultSpaceGuid string `json:"default_space_guid"` + Username string `json:"username"` + SpaceRoles []string `json:"space_roles"` + SpacesUrl string `json:"spaces_url"` + OrganizationsUrl string `json:"organizations_url"` + ManagedOrganizationsUrl string `json:"managed_organizations_url"` + BillingManagedOrganizationsUrl string `json:"billing_managed_organizations_url"` + AuditedOrganizationsUrl string `json:"audited_organizations_url"` + ManagedSpacesUrl string `json:"managed_spaces_url"` + AuditedSpacesUrl string `json:"audited_spaces_url"` + c *Client +} + +func (s *Space) Org() (Org, error) { + var orgResource OrgResource + r := s.c.NewRequest("GET", s.OrgURL) + resp, err := s.c.DoRequest(r) + if err != nil { + return Org{}, errors.Wrap(err, "Error requesting org") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return Org{}, errors.Wrap(err, "Error reading org request") + } + + err = json.Unmarshal(resBody, &orgResource) + if err != nil { + return Org{}, errors.Wrap(err, "Error unmarshaling org") + } + return s.c.mergeOrgResource(orgResource), nil +} + +func (s *Space) Quota() (*SpaceQuota, error) { + var spaceQuota *SpaceQuota + var spaceQuotaResource SpaceQuotasResource + if s.QuotaDefinitionGuid == "" { + return nil, nil + } + requestUrl := fmt.Sprintf("/v2/space_quota_definitions/%s", s.QuotaDefinitionGuid) + r := s.c.NewRequest("GET", requestUrl) + resp, err := s.c.DoRequest(r) + if err != nil { + return &SpaceQuota{}, errors.Wrap(err, "Error requesting space quota") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return &SpaceQuota{}, errors.Wrap(err, "Error reading space quota body") + } + err = json.Unmarshal(resBody, &spaceQuotaResource) + if err != nil { + return &SpaceQuota{}, errors.Wrap(err, "Error unmarshalling space quota") + } + spaceQuota = &spaceQuotaResource.Entity + spaceQuota.Guid = spaceQuotaResource.Meta.Guid + spaceQuota.c = s.c + return spaceQuota, nil +} + +func (s *Space) Summary() (SpaceSummary, error) { + var spaceSummary SpaceSummary + requestUrl := fmt.Sprintf("/v2/spaces/%s/summary", s.Guid) + r := s.c.NewRequest("GET", requestUrl) + resp, err := s.c.DoRequest(r) + if err != nil { + return SpaceSummary{}, errors.Wrap(err, "Error requesting space summary") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return SpaceSummary{}, errors.Wrap(err, "Error reading space summary body") + } + err = json.Unmarshal(resBody, &spaceSummary) + if err != nil { + return SpaceSummary{}, errors.Wrap(err, "Error unmarshalling space summary") + } + return spaceSummary, nil +} + +func (s *Space) Roles() ([]SpaceRole, error) { + var roles []SpaceRole + requestUrl := fmt.Sprintf("/v2/spaces/%s/user_roles", s.Guid) + for { + rolesResp, err := s.c.getSpaceRolesResponse(requestUrl) + if err != nil { + return roles, err + } + for _, role := range rolesResp.Resources { + role.Entity.Guid = role.Meta.Guid + role.Entity.c = s.c + roles = append(roles, role.Entity) + } + requestUrl = rolesResp.NextUrl + if requestUrl == "" { + break + } + } + return roles, nil +} + +func (c *Client) CreateSpace(req SpaceRequest) (Space, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return Space{}, err + } + r := c.NewRequestWithBody("POST", "/v2/spaces", buf) + resp, err := c.DoRequest(r) + if err != nil { + return Space{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Space{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleSpaceResp(resp) +} + +func (c *Client) UpdateSpace(spaceGUID string, req SpaceRequest) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.Update(req) +} + +func (c *Client) DeleteSpace(guid string, recursive, async bool) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/spaces/%s?recursive=%t&async=%t", guid, recursive, async))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting space %s, response code: %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) ListSpaceManagersByQuery(spaceGUID string, query url.Values) ([]User, error) { + return c.listSpaceUsersByRoleAndQuery(spaceGUID, "managers", query) +} + +func (c *Client) ListSpaceManagers(spaceGUID string) ([]User, error) { + return c.ListSpaceManagersByQuery(spaceGUID, nil) +} + +func (c *Client) ListSpaceAuditorsByQuery(spaceGUID string, query url.Values) ([]User, error) { + return c.listSpaceUsersByRoleAndQuery(spaceGUID, "auditors", query) +} + +func (c *Client) ListSpaceAuditors(spaceGUID string) ([]User, error) { + return c.ListSpaceAuditorsByQuery(spaceGUID, nil) +} + +func (c *Client) ListSpaceDevelopersByQuery(spaceGUID string, query url.Values) ([]User, error) { + return c.listSpaceUsersByRoleAndQuery(spaceGUID, "developers", query) +} + +func (c *Client) listSpaceUsersByRoleAndQuery(spaceGUID, role string, query url.Values) ([]User, error) { + var users []User + requestURL := fmt.Sprintf("/v2/spaces/%s/%s?%s", spaceGUID, role, query.Encode()) + for { + userResp, err := c.getUserResponse(requestURL) + if err != nil { + return []User{}, err + } + for _, u := range userResp.Resources { + users = append(users, c.mergeUserResource(u)) + } + requestURL = userResp.NextUrl + if requestURL == "" { + break + } + } + return users, nil +} + +func (c *Client) ListSpaceDevelopers(spaceGUID string) ([]User, error) { + return c.ListSpaceDevelopersByQuery(spaceGUID, nil) +} + +func (c *Client) ListSpaceServiceInstances(spaceGUID string) ([]ServiceInstance, error) { + return c.ListSpaceServiceInstancesByQuery(spaceGUID, nil) +} + +func (c *Client) ListSpaceServiceInstancesByQuery(spaceGUID string, query url.Values) ([]ServiceInstance, error) { + var instances []ServiceInstance + requestURL := fmt.Sprintf("/v2/spaces/%s/service_instances?%s", spaceGUID, query.Encode()) + for { + res, err := c.getServiceInstancesResponse(requestURL) + if err != nil { + return instances, err + } + for _, instance := range res.Resources { + instances = append(instances, c.mergeServiceInstance(instance)) + } + requestURL = res.NextUrl + if requestURL == "" || query.Get("page") != "" { + break + } + } + return instances, nil +} + +func (c *Client) AssociateSpaceDeveloper(spaceGUID, userGUID string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateDeveloper(userGUID) +} + +func (c *Client) AssociateSpaceDeveloperByUsername(spaceGUID, name string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateDeveloperByUsername(name) +} + +func (c *Client) AssociateSpaceDeveloperByUsernameAndOrigin(spaceGUID, name, origin string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateDeveloperByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveSpaceDeveloper(spaceGUID, userGUID string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveDeveloper(userGUID) +} + +func (c *Client) RemoveSpaceDeveloperByUsername(spaceGUID, name string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveDeveloperByUsername(name) +} + +func (c *Client) RemoveSpaceDeveloperByUsernameAndOrigin(spaceGUID, name, origin string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveDeveloperByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateSpaceAuditor(spaceGUID, userGUID string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateAuditor(userGUID) +} + +func (c *Client) AssociateSpaceAuditorByUsername(spaceGUID, name string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateAuditorByUsername(name) +} + +func (c *Client) AssociateSpaceAuditorByUsernameAndOrigin(spaceGUID, name, origin string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateAuditorByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveSpaceAuditor(spaceGUID, userGUID string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveAuditor(userGUID) +} + +func (c *Client) RemoveSpaceAuditorByUsername(spaceGUID, name string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveAuditorByUsername(name) +} + +func (c *Client) RemoveSpaceAuditorByUsernameAndOrigin(spaceGUID, name, origin string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveAuditorByUsernameAndOrigin(name, origin) +} + +func (c *Client) AssociateSpaceManager(spaceGUID, userGUID string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateManager(userGUID) +} + +func (c *Client) AssociateSpaceManagerByUsername(spaceGUID, name string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateManagerByUsername(name) +} + +func (c *Client) AssociateSpaceManagerByUsernameAndOrigin(spaceGUID, name, origin string) (Space, error) { + space := Space{Guid: spaceGUID, c: c} + return space.AssociateManagerByUsernameAndOrigin(name, origin) +} + +func (c *Client) RemoveSpaceManager(spaceGUID, userGUID string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveManager(userGUID) +} + +func (c *Client) RemoveSpaceManagerByUsername(spaceGUID, name string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveManagerByUsername(name) +} + +func (c *Client) RemoveSpaceManagerByUsernameAndOrigin(spaceGUID, name, origin string) error { + space := Space{Guid: spaceGUID, c: c} + return space.RemoveManagerByUsernameAndOrigin(name, origin) +} + +func (s *Space) AssociateDeveloper(userGUID string) (Space, error) { + return s.associateRole(userGUID, "developers") +} + +func (s *Space) AssociateDeveloperByUsername(name string) (Space, error) { + return s.associateUserByRole(name, "developers", "") +} + +func (s *Space) AssociateDeveloperByUsernameAndOrigin(name, origin string) (Space, error) { + return s.associateUserByRole(name, "developers", origin) +} + +func (s *Space) RemoveDeveloper(userGUID string) error { + return s.removeRole(userGUID, "developers") +} + +func (s *Space) RemoveDeveloperByUsername(name string) error { + return s.removeUserByRole(name, "developers", "") +} + +func (s *Space) RemoveDeveloperByUsernameAndOrigin(name, origin string) error { + return s.removeUserByRole(name, "developers", origin) +} + +func (s *Space) AssociateAuditor(userGUID string) (Space, error) { + return s.associateRole(userGUID, "auditors") +} + +func (s *Space) AssociateAuditorByUsername(name string) (Space, error) { + return s.associateUserByRole(name, "auditors", "") +} + +func (s *Space) AssociateAuditorByUsernameAndOrigin(name, origin string) (Space, error) { + return s.associateUserByRole(name, "auditors", origin) +} + +func (s *Space) RemoveAuditor(userGUID string) error { + return s.removeRole(userGUID, "auditors") +} + +func (s *Space) RemoveAuditorByUsername(name string) error { + return s.removeUserByRole(name, "auditors", "") +} + +func (s *Space) RemoveAuditorByUsernameAndOrigin(name, origin string) error { + return s.removeUserByRole(name, "auditors", origin) +} + +func (s *Space) AssociateManager(userGUID string) (Space, error) { + return s.associateRole(userGUID, "managers") +} + +func (s *Space) AssociateManagerByUsername(name string) (Space, error) { + return s.associateUserByRole(name, "managers", "") +} + +func (s *Space) AssociateManagerByUsernameAndOrigin(name, origin string) (Space, error) { + return s.associateUserByRole(name, "managers", origin) +} + +func (s *Space) RemoveManager(userGUID string) error { + return s.removeRole(userGUID, "managers") +} + +func (s *Space) RemoveManagerByUsername(name string) error { + return s.removeUserByRole(name, "managers", "") +} +func (s *Space) RemoveManagerByUsernameAndOrigin(name, origin string) error { + return s.removeUserByRole(name, "managers", origin) +} + +func (s *Space) associateRole(userGUID, role string) (Space, error) { + requestUrl := fmt.Sprintf("/v2/spaces/%s/%s/%s", s.Guid, role, userGUID) + r := s.c.NewRequest("PUT", requestUrl) + resp, err := s.c.DoRequest(r) + if err != nil { + return Space{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Space{}, errors.Wrapf(err, "Error associating %s %s, response code: %d", role, userGUID, resp.StatusCode) + } + return s.c.handleSpaceResp(resp) +} + +func (s *Space) associateUserByRole(name, role, origin string) (Space, error) { + requestUrl := fmt.Sprintf("/v2/spaces/%s/%s", s.Guid, role) + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return Space{}, err + } + r := s.c.NewRequestWithBody("PUT", requestUrl, buf) + resp, err := s.c.DoRequest(r) + if err != nil { + return Space{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Space{}, errors.Wrapf(err, "Error associating %s %s, response code: %d", role, name, resp.StatusCode) + } + return s.c.handleSpaceResp(resp) +} + +func (s *Space) removeRole(userGUID, role string) error { + requestUrl := fmt.Sprintf("/v2/spaces/%s/%s/%s", s.Guid, role, userGUID) + r := s.c.NewRequest("DELETE", requestUrl) + resp, err := s.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error removing %s %s, response code: %d", role, userGUID, resp.StatusCode) + } + return nil +} + +func (s *Space) removeUserByRole(name, role, origin string) error { + var requestURL string + var method string + + buf := bytes.NewBuffer(nil) + payload := make(map[string]string) + payload["username"] = name + if origin != "" { + payload["origin"] = origin + requestURL = fmt.Sprintf("/v2/spaces/%s/%s/remove", s.Guid, role) + method = "POST" + } else { + requestURL = fmt.Sprintf("/v2/spaces/%s/%s", s.Guid, role) + method = "DELETE" + } + err := json.NewEncoder(buf).Encode(payload) + if err != nil { + return err + } + r := s.c.NewRequestWithBody(method, requestURL, buf) + resp, err := s.c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error removing %s %s, response code: %d", role, name, resp.StatusCode) + } + return nil +} + +func (c *Client) ListSpaceSecGroups(spaceGUID string) (secGroups []SecGroup, err error) { + space := Space{Guid: spaceGUID, c: c} + return space.ListSecGroups() +} + +func (s *Space) ListSecGroups() (secGroups []SecGroup, err error) { + requestURL := fmt.Sprintf("/v2/spaces/%s/security_groups?inline-relations-depth=1", s.Guid) + for requestURL != "" { + var secGroupResp SecGroupResponse + r := s.c.NewRequest("GET", requestURL) + resp, err := s.c.DoRequest(r) + + if err != nil { + return nil, errors.Wrap(err, "Error requesting sec groups") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading sec group response body") + } + + err = json.Unmarshal(resBody, &secGroupResp) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling sec group") + } + + for _, secGroup := range secGroupResp.Resources { + secGroup.Entity.Guid = secGroup.Meta.Guid + secGroup.Entity.c = s.c + for i, space := range secGroup.Entity.SpacesData { + space.Entity.Guid = space.Meta.Guid + secGroup.Entity.SpacesData[i] = space + } + if len(secGroup.Entity.SpacesData) == 0 { + spaces, err := secGroup.Entity.ListSpaceResources() + if err != nil { + return nil, err + } + secGroup.Entity.SpacesData = append(secGroup.Entity.SpacesData, spaces...) + } + secGroups = append(secGroups, secGroup.Entity) + } + + requestURL = secGroupResp.NextUrl + resp.Body.Close() + } + return secGroups, nil +} + +func (s *Space) GetServiceOfferings() (ServiceOfferingResponse, error) { + var response ServiceOfferingResponse + requestURL := fmt.Sprintf("/v2/spaces/%s/services", s.Guid) + req := s.c.NewRequest("GET", requestURL) + + resp, err := s.c.DoRequest(req) + if err != nil { + return ServiceOfferingResponse{}, errors.Wrap(err, "Error requesting service offerings") + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ServiceOfferingResponse{}, errors.Wrap(err, "Error reading service offering response") + } + + err = json.Unmarshal(body, &response) + if err != nil { + return ServiceOfferingResponse{}, errors.Wrap(err, "Error unmarshalling service offering response") + } + + return response, nil +} + +func (s *Space) Update(req SpaceRequest) (Space, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return Space{}, err + } + r := s.c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/spaces/%s", s.Guid), buf) + resp, err := s.c.DoRequest(r) + if err != nil { + return Space{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return Space{}, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return s.c.handleSpaceResp(resp) +} + +func (c *Client) ListSpacesByQuery(query url.Values) ([]Space, error) { + return c.fetchSpaces("/v2/spaces", query) +} + +func (c *Client) ListSpacesByOrgGuid(orgGuid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/organizations/%s/spaces", orgGuid), url.Values{}) +} + +func (c *Client) ListSpaces() ([]Space, error) { + return c.ListSpacesByQuery(nil) +} + +func (c *Client) fetchSpaces(path string, query url.Values) ([]Space, error) { + requestUrl := path + "?" + query.Encode() + var spaces []Space + for { + spaceResp, err := c.getSpaceResponse(requestUrl) + if err != nil { + return []Space{}, err + } + for _, space := range spaceResp.Resources { + spaces = append(spaces, c.mergeSpaceResource(space)) + } + requestUrl = spaceResp.NextUrl + if requestUrl == "" || query.Get("page") != "" { + break + } + } + return spaces, nil +} + +func (c *Client) GetSpaceByName(spaceName string, orgGuid string) (Space, error) { + query := url.Values{} + query.Add("q", fmt.Sprintf("organization_guid:%s", orgGuid)) + query.Add("q", fmt.Sprintf("name:%s", spaceName)) + spaces, err := c.ListSpacesByQuery(query) + if err != nil { + return Space{}, err + } + + if len(spaces) == 0 { + cfErr := NewSpaceNotFoundError() + cfErr.Description = fmt.Sprintf(cfErr.Description, spaceName) + return Space{}, cfErr + } + + return spaces[0], nil + +} + +func (c *Client) GetSpaceByGuid(spaceGUID string) (Space, error) { + requestUrl := fmt.Sprintf("/v2/spaces/%s", spaceGUID) + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return Space{}, errors.Wrap(err, "Error requesting space info") + } + defer resp.Body.Close() + return c.handleSpaceResp(resp) +} + +func (c *Client) getSpaceResponse(requestUrl string) (SpaceResponse, error) { + var spaceResp SpaceResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return SpaceResponse{}, errors.Wrap(err, "Error requesting spaces") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return SpaceResponse{}, errors.Wrap(err, "Error reading space request") + } + err = json.Unmarshal(resBody, &spaceResp) + if err != nil { + return SpaceResponse{}, errors.Wrap(err, "Error unmarshalling space") + } + return spaceResp, nil +} + +func (c *Client) getSpaceRolesResponse(requestUrl string) (SpaceRoleResponse, error) { + var roleResp SpaceRoleResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return roleResp, errors.Wrap(err, "Error requesting space roles") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return roleResp, errors.Wrap(err, "Error reading space roles request") + } + err = json.Unmarshal(resBody, &roleResp) + if err != nil { + return roleResp, errors.Wrap(err, "Error unmarshalling space roles") + } + return roleResp, nil +} + +func (c *Client) handleSpaceResp(resp *http.Response) (Space, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Space{}, err + } + var spaceResource SpaceResource + err = json.Unmarshal(body, &spaceResource) + if err != nil { + return Space{}, err + } + return c.mergeSpaceResource(spaceResource), nil +} + +func (c *Client) mergeSpaceResource(space SpaceResource) Space { + space.Entity.Guid = space.Meta.Guid + space.Entity.CreatedAt = space.Meta.CreatedAt + space.Entity.UpdatedAt = space.Meta.UpdatedAt + space.Entity.c = c + return space.Entity +} + +type serviceOfferingExtra ServiceOfferingExtra + +func (resource *ServiceOfferingExtra) UnmarshalJSON(rawData []byte) error { + if string(rawData) == "null" { + return nil + } + + extra := serviceOfferingExtra{} + + unquoted, err := strconv.Unquote(string(rawData)) + if err != nil { + return err + } + + err = json.Unmarshal([]byte(unquoted), &extra) + if err != nil { + return err + } + + *resource = ServiceOfferingExtra(extra) + + return nil +} + +func (c *Client) IsolationSegmentForSpace(spaceGUID, isolationSegmentGUID string) error { + return c.updateSpaceIsolationSegment(spaceGUID, map[string]interface{}{"guid": isolationSegmentGUID}) +} + +func (c *Client) ResetIsolationSegmentForSpace(spaceGUID string) error { + return c.updateSpaceIsolationSegment(spaceGUID, nil) +} + +func (c *Client) updateSpaceIsolationSegment(spaceGUID string, data interface{}) error { + requestURL := fmt.Sprintf("/v3/spaces/%s/relationships/isolation_segment", spaceGUID) + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(map[string]interface{}{"data": data}) + if err != nil { + return err + } + r := c.NewRequestWithBody("PATCH", requestURL, buf) + resp, err := c.DoRequest(r) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "Error setting isolation segment for space %s, response code: %d", spaceGUID, resp.StatusCode) + } + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/stacks.go b/vendor/github.com/cloudfoundry-community/go-cfclient/stacks.go new file mode 100644 index 0000000..fe3fc9c --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/stacks.go @@ -0,0 +1,103 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + + "github.com/pkg/errors" +) + +type StacksResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []StacksResource `json:"resources"` +} + +type StacksResource struct { + Meta Meta `json:"metadata"` + Entity Stack `json:"entity"` +} + +type Stack struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Description string `json:"description"` + c *Client +} + +func (c *Client) ListStacksByQuery(query url.Values) ([]Stack, error) { + var stacks []Stack + requestURL := "/v2/stacks?" + query.Encode() + for { + stacksResp, err := c.getStacksResponse(requestURL) + if err != nil { + return []Stack{}, err + } + for _, stack := range stacksResp.Resources { + stack.Entity.Guid = stack.Meta.Guid + stack.Entity.CreatedAt = stack.Meta.CreatedAt + stack.Entity.UpdatedAt = stack.Meta.UpdatedAt + stack.Entity.c = c + stacks = append(stacks, stack.Entity) + } + requestURL = stacksResp.NextUrl + if requestURL == "" { + break + } + } + return stacks, nil +} + +func (c *Client) ListStacks() ([]Stack, error) { + return c.ListStacksByQuery(nil) +} + +func (c *Client) GetStackByGuid(stackGUID string) (Stack, error) { + var stacksRes StacksResource + requestURL := fmt.Sprintf("/v2/stacks/%s", stackGUID) + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return Stack{}, errors.Wrap(err, "Error requesting stack info") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return Stack{}, errors.Wrap(err, "Error reading stack body") + } + err = json.Unmarshal(resBody, &stacksRes) + if err != nil { + return Stack{}, errors.Wrap(err, "Error unmarshalling stack") + } + + stacksRes.Entity.Guid = stacksRes.Meta.Guid + stacksRes.Entity.CreatedAt = stacksRes.Meta.CreatedAt + stacksRes.Entity.UpdatedAt = stacksRes.Meta.UpdatedAt + stacksRes.Entity.c = c + + return stacksRes.Entity, nil +} + +func (c *Client) getStacksResponse(requestURL string) (StacksResponse, error) { + var stacksResp StacksResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return StacksResponse{}, errors.Wrap(err, "Error requesting stacks") + } + resBody, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return StacksResponse{}, errors.Wrap(err, "Error reading stacks body") + } + err = json.Unmarshal(resBody, &stacksResp) + if err != nil { + return StacksResponse{}, errors.Wrap(err, "Error unmarshalling stacks") + } + return stacksResp, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/stats.go b/vendor/github.com/cloudfoundry-community/go-cfclient/stats.go new file mode 100644 index 0000000..2bd86e7 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/stats.go @@ -0,0 +1,59 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "github.com/pkg/errors" + "net/http" +) + +// StatsGetResponse is the json body returned from the API +type StatsGetResponse struct { + Stats []Stats `json:"resources"` +} + +// Stats represents the stats of a process +type Stats struct { + Type string `json:"type"` + Index int `json:"index"` + State string `json:"state"` + Usage struct { + Time string `json:"time"` + CPU float64 `json:"cpu"` + Mem int `json:"mem"` + Disk int `json:"disk"` + } `json:"usage"` + Host string `json:"host"` + InstancePorts []struct { + External int `json:"external"` + Internal int `json:"internal"` + ExternalTLSProxyPort int `json:"external_tls_proxy_port"` + InternalTLSProxyPort int `json:"internal_tls_proxy_port"` + } `json:"instance_ports"` + Uptime int `json:"uptime"` + MemQuota int `json:"mem_quota"` + DiskQuota int `json:"disk_quota"` + FdsQuota int `json:"fds_quota"` + IsolationSegment string `json:"isolation_segment"` + Details string `json:"details"` +} + +func (c *Client) GetProcessStats(processGUID string) ([]Stats, error) { + req := c.NewRequest("GET", "/v3/processes/"+processGUID+"/stats") + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "error getting stats for v3 process") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error getting stats with GUID [%s], response code: %d", processGUID, resp.StatusCode) + } + + statsResp := new(StatsGetResponse) + err = json.NewDecoder(resp.Body).Decode(statsResp) + if err != nil { + return nil, fmt.Errorf("error decoding stats with GUID [%s], response code: %d", processGUID, resp.StatusCode) + } + return statsResp.Stats, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/tasks.go b/vendor/github.com/cloudfoundry-community/go-cfclient/tasks.go new file mode 100644 index 0000000..538b5e0 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/tasks.go @@ -0,0 +1,211 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +// TaskListResponse is the JSON response from the API. +type TaskListResponse struct { + Pagination Pagination `json:"pagination"` + Tasks []Task `json:"resources"` +} + +// Task is a description of a task element. +type Task struct { + GUID string `json:"guid"` + SequenceID int `json:"sequence_id"` + Name string `json:"name"` + Command string `json:"command"` + State string `json:"state"` + MemoryInMb int `json:"memory_in_mb"` + DiskInMb int `json:"disk_in_mb"` + Result struct { + FailureReason string `json:"failure_reason"` + } `json:"result"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DropletGUID string `json:"droplet_guid"` + Relationships struct { + App V3ToOneRelationship `json:"app"` + } `json:"relationships"` + Links struct { + Self Link `json:"self"` + App Link `json:"app"` + Droplet Link `json:"droplet"` + } `json:"links"` +} + +// TaskRequest is a v3 JSON object as described in: +// http://v3-apidocs.cloudfoundry.org/version/3.0.0/index.html#create-a-task +type TaskRequest struct { + Command string `json:"command"` + Name string `json:"name"` + MemoryInMegabyte int `json:"memory_in_mb"` + DiskInMegabyte int `json:"disk_in_mb"` + DropletGUID string `json:"droplet_guid"` +} + +// ListTasks returns all tasks the user has access to. +// See http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks +func (c *Client) ListTasks() ([]Task, error) { + return c.ListTasksByQuery(nil) +} + +// ListTasksByQuery returns all tasks the user has access to, with query parameters. +// See http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks +func (c *Client) ListTasksByQuery(query url.Values) ([]Task, error) { + return c.taskListHelper("/v3/tasks", query) +} + +// TasksByApp returns task structures which aligned to an app identified by the given guid. +// See: http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks-for-an-app +func (c *Client) TasksByApp(guid string) ([]Task, error) { + return c.TasksByAppByQuery(guid, url.Values{}) +} + +// TasksByAppByQuery returns task structures which aligned to an app identified by the given guid +// and filtered by the given query parameters. +// See: http://v3-apidocs.cloudfoundry.org/version/3.12.0/index.html#list-tasks-for-an-app +func (c *Client) TasksByAppByQuery(guid string, query url.Values) ([]Task, error) { + uri := fmt.Sprintf("/v3/apps/%s/tasks", guid) + return c.taskListHelper(uri, query) +} + +func (c *Client) taskListHelper(requestURL string, query url.Values) ([]Task, error) { + var tasks []Task + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 tasks") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 tasks, response code: %d", resp.StatusCode) + } + + var data TaskListResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 tasks") + } + + tasks = append(tasks, data.Tasks...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 tasks") + } + } + + return tasks, nil +} + +func createReader(tr TaskRequest) (io.Reader, error) { + rmap := make(map[string]string) + rmap["command"] = tr.Command + if tr.Name != "" { + rmap["name"] = tr.Name + } + // setting droplet GUID causing issues + if tr.MemoryInMegabyte != 0 { + rmap["memory_in_mb"] = fmt.Sprintf("%d", tr.MemoryInMegabyte) + } + if tr.DiskInMegabyte != 0 { + rmap["disk_in_mb"] = fmt.Sprintf("%d", tr.DiskInMegabyte) + } + + bodyReader := bytes.NewBuffer(nil) + enc := json.NewEncoder(bodyReader) + if err := enc.Encode(rmap); err != nil { + return nil, errors.Wrap(err, "Error during encoding task request") + } + return bodyReader, nil +} + +// CreateTask creates a new task in CF system and returns its structure. +func (c *Client) CreateTask(tr TaskRequest) (task Task, err error) { + bodyReader, err := createReader(tr) + if err != nil { + return task, err + } + + request := fmt.Sprintf("/v3/apps/%s/tasks", tr.DropletGUID) + req := c.NewRequestWithBody("POST", request, bodyReader) + + resp, err := c.DoRequest(req) + if err != nil { + return task, errors.Wrap(err, "Error creating task") + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return task, errors.Wrap(err, "Error reading task after creation") + } + + err = json.Unmarshal(body, &task) + if err != nil { + return task, errors.Wrap(err, "Error unmarshaling task") + } + return task, err +} + +// GetTaskByGuid returns a task structure by requesting it with the tasks GUID. +func (c *Client) GetTaskByGuid(guid string) (task Task, err error) { + request := fmt.Sprintf("/v3/tasks/%s", guid) + req := c.NewRequest("GET", request) + + resp, err := c.DoRequest(req) + if err != nil { + return task, errors.Wrap(err, "Error requesting task") + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return task, errors.Wrap(err, "Error reading task") + } + + err = json.Unmarshal(body, &task) + if err != nil { + return task, errors.Wrap(err, "Error unmarshaling task") + } + return task, err +} + +func (c *Client) TaskByGuid(guid string) (task Task, err error) { + return c.GetTaskByGuid(guid) +} + +// TerminateTask cancels a task identified by its GUID. +func (c *Client) TerminateTask(guid string) error { + req := c.NewRequest("PUT", fmt.Sprintf("/v3/tasks/%s/cancel", guid)) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error terminating task") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return errors.Wrapf(err, "Failed terminating task, response status code %d", resp.StatusCode) + } + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/tools.go b/vendor/github.com/cloudfoundry-community/go-cfclient/tools.go new file mode 100644 index 0000000..896ad60 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/tools.go @@ -0,0 +1,5 @@ +package cfclient + +import ( + _ "gopkg.in/yaml.v2" +) diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/types.go b/vendor/github.com/cloudfoundry-community/go-cfclient/types.go new file mode 100644 index 0000000..279106b --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/types.go @@ -0,0 +1,8 @@ +package cfclient + +type Meta struct { + Guid string `json:"guid"` + Url string `json:"url"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/user_provided_service_instances.go b/vendor/github.com/cloudfoundry-community/go-cfclient/user_provided_service_instances.go new file mode 100644 index 0000000..7b398a5 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/user_provided_service_instances.go @@ -0,0 +1,189 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type UserProvidedServiceInstancesResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []UserProvidedServiceInstanceResource `json:"resources"` +} + +type UserProvidedServiceInstanceResource struct { + Meta Meta `json:"metadata"` + Entity UserProvidedServiceInstance `json:"entity"` +} + +type UserProvidedServiceInstance struct { + Guid string `json:"guid"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Credentials map[string]interface{} `json:"credentials"` + SpaceGuid string `json:"space_guid"` + Type string `json:"type"` + Tags []string `json:"tags"` + SpaceUrl string `json:"space_url"` + ServiceBindingsUrl string `json:"service_bindings_url"` + RoutesUrl string `json:"routes_url"` + RouteServiceUrl string `json:"route_service_url"` + SyslogDrainUrl string `json:"syslog_drain_url"` + c *Client +} + +type UserProvidedServiceInstanceRequest struct { + Name string `json:"name"` + Credentials map[string]interface{} `json:"credentials"` + SpaceGuid string `json:"space_guid"` + Tags []string `json:"tags"` + RouteServiceUrl string `json:"route_service_url"` + SyslogDrainUrl string `json:"syslog_drain_url"` +} + +func (c *Client) ListUserProvidedServiceInstancesByQuery(query url.Values) ([]UserProvidedServiceInstance, error) { + var instances []UserProvidedServiceInstance + + requestURL := "/v2/user_provided_service_instances?" + query.Encode() + for { + var sir UserProvidedServiceInstancesResponse + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting user provided service instances") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "Error reading user provided service instances request:") + } + + err = json.Unmarshal(resBody, &sir) + if err != nil { + return nil, errors.Wrap(err, "Error unmarshaling user provided service instances") + } + for _, instance := range sir.Resources { + instance.Entity.Guid = instance.Meta.Guid + instance.Entity.CreatedAt = instance.Meta.CreatedAt + instance.Entity.UpdatedAt = instance.Meta.UpdatedAt + instance.Entity.c = c + instances = append(instances, instance.Entity) + } + + requestURL = sir.NextUrl + if requestURL == "" { + break + } + } + return instances, nil +} + +func (c *Client) ListUserProvidedServiceInstances() ([]UserProvidedServiceInstance, error) { + return c.ListUserProvidedServiceInstancesByQuery(nil) +} + +func (c *Client) GetUserProvidedServiceInstanceByGuid(guid string) (UserProvidedServiceInstance, error) { + var sir UserProvidedServiceInstanceResource + req := c.NewRequest("GET", "/v2/user_provided_service_instances/"+guid) + res, err := c.DoRequest(req) + if err != nil { + return UserProvidedServiceInstance{}, errors.Wrap(err, "Error requesting user provided service instance") + } + defer res.Body.Close() + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return UserProvidedServiceInstance{}, errors.Wrap(err, "Error reading user provided service instance response") + } + err = json.Unmarshal(data, &sir) + if err != nil { + return UserProvidedServiceInstance{}, errors.Wrap(err, "Error JSON parsing user provided service instance response") + } + sir.Entity.Guid = sir.Meta.Guid + sir.Entity.CreatedAt = sir.Meta.CreatedAt + sir.Entity.UpdatedAt = sir.Meta.UpdatedAt + sir.Entity.c = c + return sir.Entity, nil +} + +func (c *Client) UserProvidedServiceInstanceByGuid(guid string) (UserProvidedServiceInstance, error) { + return c.GetUserProvidedServiceInstanceByGuid(guid) +} + +func (c *Client) CreateUserProvidedServiceInstance(req UserProvidedServiceInstanceRequest) (*UserProvidedServiceInstance, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("POST", "/v2/user_provided_service_instances", buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + + return c.handleUserProvidedServiceInstanceResp(resp) +} + +func (c *Client) DeleteUserProvidedServiceInstance(guid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/user_provided_service_instances/%s", guid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting user provided service instance %s, response code %d", guid, resp.StatusCode) + } + return nil +} + +func (c *Client) UpdateUserProvidedServiceInstance(guid string, req UserProvidedServiceInstanceRequest) (*UserProvidedServiceInstance, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return nil, err + } + r := c.NewRequestWithBody("PUT", fmt.Sprintf("/v2/user_provided_service_instances/%s", guid), buf) + resp, err := c.DoRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("CF API returned with status code %d", resp.StatusCode) + } + return c.handleUserProvidedServiceInstanceResp(resp) +} + +func (c *Client) handleUserProvidedServiceInstanceResp(resp *http.Response) (*UserProvidedServiceInstance, error) { + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return nil, err + } + var upsResource UserProvidedServiceInstanceResource + err = json.Unmarshal(body, &upsResource) + if err != nil { + return nil, err + } + return c.mergeUserProvidedServiceInstanceResource(upsResource), nil +} + +func (c *Client) mergeUserProvidedServiceInstanceResource(ups UserProvidedServiceInstanceResource) *UserProvidedServiceInstance { + ups.Entity.Guid = ups.Meta.Guid + ups.Entity.CreatedAt = ups.Meta.CreatedAt + ups.Entity.UpdatedAt = ups.Meta.UpdatedAt + ups.Entity.c = c + return &ups.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/users.go b/vendor/github.com/cloudfoundry-community/go-cfclient/users.go new file mode 100644 index 0000000..9af6d9d --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/users.go @@ -0,0 +1,205 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type UserRequest struct { + Guid string `json:"guid"` + DefaultSpaceGuid string `json:"default_space_guid,omitempty"` +} + +type Users []User + +type User struct { + Guid string `json:"guid"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Admin bool `json:"admin"` + Active bool `json:"active"` + DefaultSpaceGUID string `json:"default_space_guid"` + Username string `json:"username"` + SpacesURL string `json:"spaces_url"` + OrgsURL string `json:"organizations_url"` + ManagedOrgsURL string `json:"managed_organizations_url"` + BillingManagedOrgsURL string `json:"billing_managed_organizations_url"` + AuditedOrgsURL string `json:"audited_organizations_url"` + ManagedSpacesURL string `json:"managed_spaces_url"` + AuditedSpacesURL string `json:"audited_spaces_url"` + c *Client +} + +type UserResource struct { + Meta Meta `json:"metadata"` + Entity User `json:"entity"` +} + +type UserResponse struct { + Count int `json:"total_results"` + Pages int `json:"total_pages"` + NextUrl string `json:"next_url"` + Resources []UserResource `json:"resources"` +} + +// GetUserByGUID retrieves the user with the provided guid. +func (c *Client) GetUserByGUID(guid string) (User, error) { + var userRes UserResource + r := c.NewRequest("GET", "/v2/users/"+guid) + resp, err := c.DoRequest(r) + if err != nil { + return User{}, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return User{}, err + } + err = json.Unmarshal(body, &userRes) + if err != nil { + return User{}, err + } + return c.mergeUserResource(userRes), nil +} + +func (c *Client) ListUsersByQuery(query url.Values) (Users, error) { + var users []User + requestUrl := "/v2/users?" + query.Encode() + for { + userResp, err := c.getUserResponse(requestUrl) + if err != nil { + return []User{}, err + } + for _, user := range userResp.Resources { + user.Entity.Guid = user.Meta.Guid + user.Entity.CreatedAt = user.Meta.CreatedAt + user.Entity.UpdatedAt = user.Meta.UpdatedAt + user.Entity.c = c + users = append(users, user.Entity) + } + requestUrl = userResp.NextUrl + if requestUrl == "" || query.Get("page") != "" { + break + } + } + return users, nil +} + +func (c *Client) ListUsers() (Users, error) { + return c.ListUsersByQuery(nil) +} + +func (c *Client) ListUserSpaces(userGuid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/spaces", userGuid), url.Values{}) +} + +func (c *Client) ListUserAuditedSpaces(userGuid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/audited_spaces", userGuid), url.Values{}) +} + +func (c *Client) ListUserManagedSpaces(userGuid string) ([]Space, error) { + return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/managed_spaces", userGuid), url.Values{}) +} + +func (c *Client) ListUserOrgs(userGuid string) ([]Org, error) { + return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/organizations", userGuid)) +} + +func (c *Client) ListUserManagedOrgs(userGuid string) ([]Org, error) { + return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/managed_organizations", userGuid)) +} + +func (c *Client) ListUserAuditedOrgs(userGuid string) ([]Org, error) { + return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/audited_organizations", userGuid)) +} + +func (c *Client) ListUserBillingManagedOrgs(userGuid string) ([]Org, error) { + return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/billing_managed_organizations", userGuid)) +} + +func (c *Client) CreateUser(req UserRequest) (User, error) { + buf := bytes.NewBuffer(nil) + err := json.NewEncoder(buf).Encode(req) + if err != nil { + return User{}, err + } + r := c.NewRequestWithBody("POST", "/v2/users", buf) + resp, err := c.DoRequest(r) + if err != nil { + return User{}, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusCreated { + return User{}, errors.Wrapf(err, "Error creating user, response code: %d", resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return User{}, err + } + var userResource UserResource + err = json.Unmarshal(body, &userResource) + if err != nil { + return User{}, err + } + user := userResource.Entity + user.Guid = userResource.Meta.Guid + user.c = c + return user, nil +} + +func (c *Client) DeleteUser(userGuid string) error { + resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/users/%s", userGuid))) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusNoContent { + return errors.Wrapf(err, "Error deleting user %s, response code: %d", userGuid, resp.StatusCode) + } + return nil +} + +func (u Users) GetUserByUsername(username string) User { + for _, user := range u { + if user.Username == username { + return user + } + } + return User{} +} + +func (c *Client) getUserResponse(requestUrl string) (UserResponse, error) { + var userResp UserResponse + r := c.NewRequest("GET", requestUrl) + resp, err := c.DoRequest(r) + if err != nil { + return UserResponse{}, errors.Wrap(err, "Error requesting users") + } + defer resp.Body.Close() + resBody, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return UserResponse{}, errors.Wrap(err, "Error reading user request") + } + err = json.Unmarshal(resBody, &userResp) + if err != nil { + return UserResponse{}, errors.Wrap(err, "Error unmarshalling user") + } + return userResp, nil +} + +func (c *Client) mergeUserResource(u UserResource) User { + u.Entity.Guid = u.Meta.Guid + u.Entity.CreatedAt = u.Meta.CreatedAt + u.Entity.UpdatedAt = u.Meta.UpdatedAt + u.Entity.c = c + return u.Entity +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3apps.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3apps.go new file mode 100644 index 0000000..90a5806 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3apps.go @@ -0,0 +1,285 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type V3App struct { + Name string `json:"name,omitempty"` + State string `json:"state,omitempty"` + Lifecycle V3Lifecycle `json:"lifecycle,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type V3Lifecycle struct { + Type string `json:"type,omitempty"` + BuildpackData V3BuildpackLifecycle `json:"data,omitempty"` +} + +type V3BuildpackLifecycle struct { + Buildpacks []string `json:"buildpacks,omitempty"` + Stack string `json:"stack,omitempty"` +} + +type CreateV3AppRequest struct { + Name string + SpaceGUID string + EnvironmentVariables map[string]string + Lifecycle *V3Lifecycle + Metadata *V3Metadata +} + +type UpdateV3AppRequest struct { + Name string `json:"name"` + Lifecycle *V3Lifecycle `json:"lifecycle"` + Metadata *V3Metadata `json:"metadata"` +} + +func (c *Client) CreateV3App(r CreateV3AppRequest) (*V3App, error) { + req := c.NewRequest("POST", "/v3/apps") + params := map[string]interface{}{ + "name": r.Name, + "relationships": map[string]interface{}{ + "space": V3ToOneRelationship{ + Data: V3Relationship{ + GUID: r.SpaceGUID, + }, + }, + }, + } + if len(r.EnvironmentVariables) > 0 { + params["environment_variables"] = r.EnvironmentVariables + } + if r.Lifecycle != nil { + params["lifecycle"] = r.Lifecycle + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + + req.obj = params + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 app %s, response code: %d", r.Name, resp.StatusCode) + } + + var app V3App + if err := json.NewDecoder(resp.Body).Decode(&app); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app JSON") + } + + return &app, nil +} + +func (c *Client) GetV3AppByGUID(guid string) (*V3App, error) { + req := c.NewRequest("GET", "/v3/apps/"+guid) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 app with GUID [%s], response code: %d", guid, resp.StatusCode) + } + + var app V3App + if err := json.NewDecoder(resp.Body).Decode(&app); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app JSON") + } + + return &app, nil +} + +func (c *Client) StartV3App(guid string) (*V3App, error) { + req := c.NewRequest("POST", "/v3/apps/"+guid+"/actions/start") + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while starting v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error starting v3 app with GUID [%s], response code: %d", guid, resp.StatusCode) + } + + var app V3App + if err := json.NewDecoder(resp.Body).Decode(&app); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app JSON") + } + + return &app, nil +} + +func (c *Client) DeleteV3App(guid string) error { + req := c.NewRequest("DELETE", "/v3/apps/"+guid) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 app with GUID [%s], response code: %d", guid, resp.StatusCode) + } + + return nil +} + +func (c *Client) UpdateV3App(appGUID string, r UpdateV3AppRequest) (*V3App, error) { + req := c.NewRequest("PATCH", "/v3/apps/"+appGUID) + params := make(map[string]interface{}) + if r.Name != "" { + params["name"] = r.Name + } + if r.Lifecycle != nil { + params["lifecycle"] = r.Lifecycle + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + if len(params) > 0 { + req.obj = params + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while updating v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error updating v3 app %s, response code: %d", appGUID, resp.StatusCode) + } + + var app V3App + if err := json.NewDecoder(resp.Body).Decode(&app); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app JSON") + } + + return &app, nil +} + +type listV3AppsResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3App `json:"resources,omitempty"` +} + +func (c *Client) ListV3AppsByQuery(query url.Values) ([]V3App, error) { + var apps []V3App + requestURL := "/v3/apps" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 apps") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 apps, response code: %d", resp.StatusCode) + } + + var data listV3AppsResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 apps") + } + + apps = append(apps, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 apps") + } + } + + return apps, nil +} + +func extractPathFromURL(requestURL string) (string, error) { + url, err := url.Parse(requestURL) + if err != nil { + return "", err + } + result := url.Path + if q := url.Query().Encode(); q != "" { + result = result + "?" + q + } + return result, nil +} + +type V3AppEnvironment struct { + EnvVars map[string]string `json:"environment_variables,omitempty"` + StagingEnv map[string]string `json:"staging_env_json,omitempty"` + RunningEnv map[string]string `json:"running_env_json,omitempty"` + SystemEnvVars map[string]json.RawMessage `json:"system_env_json,omitempty"` // VCAP_SERVICES + AppEnvVars map[string]json.RawMessage `json:"application_env_json,omitempty"` // VCAP_APPLICATION +} + +func (c *Client) GetV3AppEnvironment(appGUID string) (V3AppEnvironment, error) { + var result V3AppEnvironment + + resp, err := c.DoRequest(c.NewRequest("GET", "/v3/apps/"+appGUID+"/env")) + if err != nil { + return result, errors.Wrapf(err, "Error requesting app env for %s", appGUID) + } + + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return result, errors.Wrap(err, "Error parsing JSON for app env") + } + + return result, nil +} + +type V3EnvVar struct { + Var map[string]*string `json:"var"` +} + +type v3EnvVarResponse struct { + V3EnvVar + Links map[string]Link `json:"links"` +} + +func (c *Client) SetV3AppEnvVariables(appGUID string, envRequest V3EnvVar) (V3EnvVar, error) { + var result v3EnvVarResponse + + req := c.NewRequest("PATCH", "/v3/apps/"+appGUID+"/environment_variables") + req.obj = envRequest + + resp, err := c.DoRequest(req) + if err != nil { + return result.V3EnvVar, errors.Wrapf(err, "Error setting app env variables for %s", appGUID) + } + + defer resp.Body.Close() + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return result.V3EnvVar, errors.Wrap(err, "Error parsing JSON for app env") + } + + return result.V3EnvVar, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3build.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3build.go new file mode 100644 index 0000000..432f891 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3build.go @@ -0,0 +1,77 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +type V3Build struct { + State string `json:"state,omitempty"` + Error string `json:"error,omitempty"` + Lifecycle V3Lifecycle `json:"lifecycle,omitempty"` + Package V3Relationship `json:"package,omitempty"` + Droplet V3Relationship `json:"droplet,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + CreatedBy V3CreatedBy `json:"created_by,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type V3CreatedBy struct { + GUID string `json:"guid,omitempty"` + Name string `json:"name,omitempty"` + Email string `json:"email,omitempty"` +} + +func (c *Client) GetV3BuildByGUID(buildGUID string) (*V3Build, error) { + resp, err := c.DoRequest(c.NewRequest("GET", "/v3/builds/"+buildGUID)) + if err != nil { + return nil, errors.Wrap(err, "Error getting V3 build") + } + defer resp.Body.Close() + + var build V3Build + if err := json.NewDecoder(resp.Body).Decode(&build); err != nil { + return nil, errors.Wrap(err, "Error reading V3 build JSON") + } + + return &build, nil +} + +func (c *Client) CreateV3Build(packageGUID string, lifecycle *V3Lifecycle, metadata *V3Metadata) (*V3Build, error) { + req := c.NewRequest("POST", "/v3/builds") + params := map[string]interface{}{ + "package": map[string]interface{}{ + "guid": packageGUID, + }, + } + if lifecycle != nil { + params["lifecycle"] = lifecycle + } + if metadata != nil { + params["metadata"] = metadata + } + req.obj = params + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 build") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 build, response code: %d", resp.StatusCode) + } + + var build V3Build + if err := json.NewDecoder(resp.Body).Decode(&build); err != nil { + return nil, errors.Wrap(err, "Error reading V3 Build JSON") + } + + return &build, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3deployments.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3deployments.go new file mode 100644 index 0000000..2314e95 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3deployments.go @@ -0,0 +1,132 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +type CreateV3DeploymentOptionalParameters struct { + Droplet *V3Relationship `json:"droplet,omitempty"` + Revision *V3DeploymentRevision `json:"revision,omitempty"` + Strategy *string `json:"strategy,omitempty"` + Metadata *V3Metadata `json:"metadata,omitempty"` +} + +type createV3DeploymentRequest struct { + *CreateV3DeploymentOptionalParameters `json:",inline"` + Relationships struct { + App V3ToOneRelationship `json:"app"` + } `json:"relationships"` +} + +type V3DeploymentRevision struct { + GUID string `json:"guid"` + Version int `json:"version"` +} + +type V3ProcessReference struct { + GUID string `json:"guid"` + Type string `type:"type"` +} + +type V3DeploymentStatus struct { + Value string `json:"value"` + Reason string `json:"reason"` + Details map[string]string `json:"details"` +} + +type V3Deployment struct { + GUID string `json:"guid"` + State string `json:"state"` + Status V3DeploymentStatus `json:"status"` + Strategy string `json:"strategy"` + Droplet V3Relationship `json:"droplet"` + PreviousDroplet V3Relationship `json:"previous_droplet"` + NewProcesses []V3ProcessReference `json:"new_processes"` + Revision V3DeploymentRevision `json:"revision"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` +} + +func (c *Client) GetV3Deployment(deploymentGUID string) (*V3Deployment, error) { + req := c.NewRequest("GET", "/v3/deployments/"+deploymentGUID) + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error getting deployment") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting deployment with GUID [%s], response code: %d", deploymentGUID, resp.StatusCode) + } + + var r V3Deployment + if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, errors.Wrap(err, "Error reading deployment response JSON") + } + + return &r, nil +} + +func (c *Client) CreateV3Deployment(appGUID string, optionalParams *CreateV3DeploymentOptionalParameters) (*V3Deployment, error) { + // validate the params + if optionalParams != nil { + if optionalParams.Droplet != nil && optionalParams.Revision != nil { + return nil, errors.New("droplet and revision cannot both be set") + } + } + + requestBody := createV3DeploymentRequest{} + requestBody.CreateV3DeploymentOptionalParameters = optionalParams + + requestBody.Relationships = struct { + App V3ToOneRelationship "json:\"app\"" + }{ + App: V3ToOneRelationship{ + Data: V3Relationship{ + GUID: appGUID, + }, + }, + } + + req := c.NewRequest("POST", "/v3/deployments") + req.obj = requestBody + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error creating deployment") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating deployment for app GUID [%s], response code: %d", appGUID, resp.StatusCode) + } + + var r V3Deployment + if err = json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, errors.Wrap(err, "Error reading deployment response JSON") + } + + return &r, nil +} + +func (c *Client) CancelV3Deployment(deploymentGUID string) error { + req := c.NewRequest("POST", "/v3/deployments/"+deploymentGUID+"/actions/cancel") + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error canceling deployment") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Error canceling deployment [%s], response code: %d", deploymentGUID, resp.StatusCode) + } + + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3domains.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3domains.go new file mode 100644 index 0000000..16b7632 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3domains.go @@ -0,0 +1,68 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +type DomainRelationships struct { + Organization V3ToOneRelationship `json:"organization"` + SharedOrganizations V3ToManyRelationships `json:"shared_organizations"` +} + +type V3Domain struct { + Guid string `json:"guid"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Internal bool `json:"internal"` + Metadata Metadata `json:"metadata"` + Relationships DomainRelationships `json:"relationships"` + Links map[string]Link `json:"links"` +} + +type listV3DomainsResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Domain `json:"resources,omitempty"` +} + +func (c *Client) ListV3Domains(query url.Values) ([]V3Domain, error) { + var domains []V3Domain + requestURL := "/v3/domains" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + resp, err := c.DoRequest(c.NewRequest("GET", requestURL)) + if err != nil { + return nil, errors.Wrapf(err, "Error getting domains") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 app domains, response code: %d", resp.StatusCode) + } + + var data listV3DomainsResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 app domains") + } + + domains = append(domains, data.Resources...) + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 domains") + } + } + return domains, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3droplet.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3droplet.go new file mode 100644 index 0000000..d3a453d --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3droplet.go @@ -0,0 +1,106 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +// V3Droplet is the result of staging an application package. +// There are two types (lifecycles) of droplets: buildpack and +// docker. In the case of buildpacks, the droplet contains the +// bits produced by the buildpack. +type V3Droplet struct { + State string `json:"state,omitempty"` + Error string `json:"error,omitempty"` + Lifecycle V3Lifecycle `json:"lifecycle,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Links map[string]Link `json:"links,omitempty"` + ExecutionMetadata string `json:"execution_metadata,omitempty"` + ProcessTypes map[string]string `json:"process_types,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` + + // Only specified when the droplet is using the Docker lifecycle. + Image string `json:"image,omitempty"` + + // The following fields are specified when the droplet is using + // the buildpack lifecycle. + Checksum struct { + Type string `json:"type,omitempty"` + Value string `json:"value,omitempty"` + } `json:"checksum,omitempty"` + Stack string `json:"stack,omitempty"` + Buildpacks []V3DetectedBuildpack `json:"buildpacks,omitempty"` +} + +type V3DetectedBuildpack struct { + Name string `json:"name,omitempty"` // system buildpack name + BuildpackName string `json:"buildpack_name,omitempty"` // name reported by the buildpack + DetectOutput string `json:"detect_output,omitempty"` // output during detect process + Version string `json:"version,omitempty"` +} + +type CurrentDropletV3Response struct { + Data V3Relationship `json:"data,omitempty"` + Links map[string]Link `json:"links,omitempty"` +} + +func (c *Client) SetCurrentDropletForV3App(appGUID, dropletGUID string) (*CurrentDropletV3Response, error) { + req := c.NewRequest("PATCH", "/v3/apps/"+appGUID+"/relationships/current_droplet") + req.obj = V3ToOneRelationship{Data: V3Relationship{GUID: dropletGUID}} + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error setting droplet for v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error setting droplet for v3 app with GUID [%s], response code: %d", appGUID, resp.StatusCode) + } + + var r CurrentDropletV3Response + if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, errors.Wrap(err, "Error reading droplet response JSON") + } + + return &r, nil +} + +func (c *Client) GetCurrentDropletForV3App(appGUID string) (*V3Droplet, error) { + req := c.NewRequest("GET", "/v3/apps/"+appGUID+"/droplets/current") + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error getting droplet for v3 app") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting droplet for v3 app with GUID [%s], response code: %d", appGUID, resp.StatusCode) + } + + var r V3Droplet + if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + return nil, errors.Wrap(err, "Error reading droplet response JSON") + } + + return &r, nil +} + +func (c *Client) DeleteDroplet(dropletGUID string) error { + req := c.NewRequest("DELETE", "/v3/droplets/"+dropletGUID) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrapf(err, "Error deleting droplet %s", dropletGUID) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting droplet %s with response code %d", dropletGUID, resp.StatusCode) + } + + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3organizations.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3organizations.go new file mode 100644 index 0000000..6eb0d28 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3organizations.go @@ -0,0 +1,178 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type V3Organization struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + Suspended *bool `json:"suspended,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type CreateV3OrganizationRequest struct { + Name string + Suspended *bool `json:"suspended,omitempty"` + Metadata *V3Metadata +} + +type UpdateV3OrganizationRequest struct { + Name string + Suspended *bool `json:"suspended,omitempty"` + Metadata *V3Metadata +} + +func (c *Client) CreateV3Organization(r CreateV3OrganizationRequest) (*V3Organization, error) { + req := c.NewRequest("POST", "/v3/organizations") + params := map[string]interface{}{ + "name": r.Name, + } + if r.Suspended != nil { + params["suspended"] = r.Suspended + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + + req.obj = params + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 organization") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 organization %s, response code: %d", r.Name, resp.StatusCode) + } + + var organization V3Organization + if err := json.NewDecoder(resp.Body).Decode(&organization); err != nil { + return nil, errors.Wrap(err, "Error reading v3 organization JSON") + } + + return &organization, nil +} + +func (c *Client) GetV3OrganizationByGUID(organizationGUID string) (*V3Organization, error) { + req := c.NewRequest("GET", "/v3/organizations/"+organizationGUID) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 organization") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 organization with GUID [%s], response code: %d", organizationGUID, resp.StatusCode) + } + + var organization V3Organization + if err := json.NewDecoder(resp.Body).Decode(&organization); err != nil { + return nil, errors.Wrap(err, "Error reading v3 organization JSON") + } + + return &organization, nil +} + +func (c *Client) DeleteV3Organization(organizationGUID string) error { + req := c.NewRequest("DELETE", "/v3/organizations/"+organizationGUID) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 organization") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 organization with GUID [%s], response code: %d", organizationGUID, resp.StatusCode) + } + + return nil +} + +func (c *Client) UpdateV3Organization(organizationGUID string, r UpdateV3OrganizationRequest) (*V3Organization, error) { + req := c.NewRequest("PATCH", "/v3/organizations/"+organizationGUID) + params := make(map[string]interface{}) + if r.Name != "" { + params["name"] = r.Name + } + if r.Suspended != nil { + params["suspended"] = r.Suspended + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + if len(params) > 0 { + req.obj = params + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while updating v3 organization") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error updating v3 organization %s, response code: %d", organizationGUID, resp.StatusCode) + } + + var organization V3Organization + if err := json.NewDecoder(resp.Body).Decode(&organization); err != nil { + return nil, errors.Wrap(err, "Error reading v3 organization JSON") + } + + return &organization, nil +} + +type listV3OrganizationsResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Organization `json:"resources,omitempty"` +} + +func (c *Client) ListV3OrganizationsByQuery(query url.Values) ([]V3Organization, error) { + var organizations []V3Organization + requestURL := "/v3/organizations" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 organizations") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 organizations, response code: %d", resp.StatusCode) + } + + var data listV3OrganizationsResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 organizations") + } + + organizations = append(organizations, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 organizations") + } + } + + return organizations, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3packages.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3packages.go new file mode 100644 index 0000000..91905a8 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3packages.go @@ -0,0 +1,192 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type V3PackageState string + +const ( + AwaitingUpload V3PackageState = "AWAITING_UPLOAD" + ProcessingUpload V3PackageState = "PROCESSING_UPLOAD" + Ready V3PackageState = "READY" + Failed V3PackageState = "FAILED" + Copying V3PackageState = "COPYING" + Expired V3PackageState = "EXPIRED" +) + +type V3Package struct { + Type string `json:"type,omitempty"` // bits or docker + Data json.RawMessage `json:"data,omitempty"` // depends on value of Type + State V3PackageState `json:"state,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +func (v *V3Package) BitsData() (V3BitsPackage, error) { + var bits V3BitsPackage + if v.Type != "bits" { + return bits, errors.New("this package is not of type bits") + } + + if err := json.Unmarshal(v.Data, &bits); err != nil { + return bits, err + } + + return bits, nil +} + +func (v *V3Package) DockerData() (V3DockerPackage, error) { + var docker V3DockerPackage + if v.Type != "docker" { + return docker, errors.New("this package is not of type docker") + } + + if err := json.Unmarshal(v.Data, &docker); err != nil { + return docker, err + } + + return docker, nil +} + +// V3BitsPackage is the data for V3Packages of type bits. +// It provides an upload link to which a zip file should be uploaded. +type V3BitsPackage struct { + Error string `json:"error,omitempty"` + Checksum struct { + Type string `json:"type,omitempty"` // eg. sha256 + Value string `json:"value,omitempty"` // populated after the bits are uploaded + } `json:"checksum,omitempty"` +} + +// V3DockerPackage is the data for V3Packages of type docker. +// It references a docker image from a registry. +type V3DockerPackage struct { + Image string `json:"image,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + +type listV3PackagesResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Package `json:"resources,omitempty"` +} + +func (c *Client) ListPackagesForAppV3(appGUID string, query url.Values) ([]V3Package, error) { + var packages []V3Package + requestURL := "/v3/apps/" + appGUID + "/packages" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + resp, err := c.DoRequest(c.NewRequest("GET", requestURL)) + if err != nil { + return nil, errors.Wrapf(err, "Error requesting packages for app %s", appGUID) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 app packages, response code: %d", resp.StatusCode) + } + + var data listV3PackagesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 app packages") + } + + packages = append(packages, data.Resources...) + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 packages") + } + } + return packages, nil +} + +// CopyPackageV3 makes a copy of a package that is associated with one app +// and associates the copy with a new app. +func (c *Client) CopyPackageV3(packageGUID, appGUID string) (*V3Package, error) { + req := c.NewRequest("POST", "/v3/packages?source_guid="+packageGUID) + req.obj = map[string]interface{}{ + "relationships": map[string]interface{}{ + "app": V3ToOneRelationship{ + Data: V3Relationship{ + GUID: appGUID, + }, + }, + }, + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while copying v3 package") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error copying v3 package %s, response code: %d", packageGUID, resp.StatusCode) + } + + var pkg V3Package + if err := json.NewDecoder(resp.Body).Decode(&pkg); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app package") + } + + return &pkg, nil +} + +type v3DockerPackageData struct { + Image string `json:"image"` + *DockerCredentials +} + +type createV3DockerPackageRequest struct { + Type string `json:"type"` + Relationships map[string]V3ToOneRelationship `json:"relationships"` + Data v3DockerPackageData `json:"data"` +} + +// CreateV3DockerPackage creates a Docker package +func (c *Client) CreateV3DockerPackage(image string, appGUID string, dockerCredentials *DockerCredentials) (*V3Package, error) { + req := c.NewRequest("POST", "/v3/packages") + req.obj = createV3DockerPackageRequest{ + Type: "docker", + Relationships: map[string]V3ToOneRelationship{ + "app": {Data: V3Relationship{GUID: appGUID}}, + }, + Data: v3DockerPackageData{ + Image: image, + DockerCredentials: dockerCredentials, + }, + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while copying v3 package") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("error creating v3 docker package, response code: %d", resp.StatusCode) + } + + var pkg V3Package + if err := json.NewDecoder(resp.Body).Decode(&pkg); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app package") + } + + return &pkg, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3roles.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3roles.go new file mode 100644 index 0000000..dce289f --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3roles.go @@ -0,0 +1,273 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +// V3Role implements role object. Roles control access to resources in organizations and spaces. Roles are assigned to users. +type V3Role struct { + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Type string `json:"type,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` +} + +type Included struct { + Users []V3User `json:"users,omitempty"` + Organizations []V3Organization `json:"organizations,omitempty"` + Spaces []V3Space `json:"spaces,omitempty"` +} + +type listV3RolesResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Role `json:"resources,omitempty"` + Included Included `json:"included,omitempty"` +} + +type createV3SpaceRoleRequest struct { + RoleType string `json:"type"` + Relationships spaceUserRelationships `json:"relationships"` +} + +type createV3OrganizationRoleRequest struct { + RoleType string `json:"type"` + Relationships orgUserRelationships `json:"relationships"` +} + +type spaceUserRelationships struct { + Space V3ToOneRelationship `json:"space"` + User V3ToOneRelationship `json:"user"` +} + +type orgUserRelationships struct { + Org V3ToOneRelationship `json:"organization"` + User V3ToOneRelationship `json:"user"` +} + +func (c *Client) CreateV3SpaceRole(spaceGUID, userGUID, roleType string) (*V3Role, error) { + spaceRel := V3ToOneRelationship{Data: V3Relationship{GUID: spaceGUID}} + userRel := V3ToOneRelationship{Data: V3Relationship{GUID: userGUID}} + req := c.NewRequest("POST", "/v3/roles") + req.obj = createV3SpaceRoleRequest{ + RoleType: roleType, + Relationships: spaceUserRelationships{Space: spaceRel, User: userRel}, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 role") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("error creating v3 role, response code: %d", resp.StatusCode) + } + + var role V3Role + if err := json.NewDecoder(resp.Body).Decode(&role); err != nil { + return nil, errors.Wrap(err, "Error reading v3 role") + } + + return &role, nil +} + +func (c *Client) CreateV3OrganizationRole(orgGUID, userGUID, roleType string) (*V3Role, error) { + orgRel := V3ToOneRelationship{Data: V3Relationship{GUID: orgGUID}} + userRel := V3ToOneRelationship{Data: V3Relationship{GUID: userGUID}} + req := c.NewRequest("POST", "/v3/roles") + req.obj = createV3OrganizationRoleRequest{ + RoleType: roleType, + Relationships: orgUserRelationships{Org: orgRel, User: userRel}, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 role") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("error creating v3 role, response code: %d", resp.StatusCode) + } + + var role V3Role + if err := json.NewDecoder(resp.Body).Decode(&role); err != nil { + return nil, errors.Wrap(err, "Error reading v3 role") + } + + return &role, nil +} + +// ListV3RolesByQuery retrieves roles based on query +func (c *Client) ListV3RolesByQuery(query url.Values) ([]V3Role, error) { + var roles []V3Role + requestURL, err := url.Parse("/v3/roles") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 space roles") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 space roles, response code: %d", resp.StatusCode) + } + + var data listV3RolesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 space roles") + } + + roles = append(roles, data.Resources...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return roles, nil +} + +func (c *Client) ListV3RoleUsersByQuery(query url.Values) ([]V3User, error) { + var users []V3User + requestURL, err := url.Parse("/v3/roles") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 roles") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 roles, response code: %d", resp.StatusCode) + } + + var data listV3RolesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 roles") + } + + users = append(users, data.Included.Users...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return users, nil +} + +func (c *Client) ListV3RoleAndUsersByQuery(query url.Values) ([]V3Role, []V3User, error) { + var roles []V3Role + var users []V3User + requestURL, err := url.Parse("/v3/roles") + if err != nil { + return nil, nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, nil, errors.Wrap(err, "Error requesting v3 roles") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, nil, fmt.Errorf("Error listing v3 roles, response code: %d", resp.StatusCode) + } + + var data listV3RolesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, nil, errors.Wrap(err, "Error parsing JSON from list v3 roles") + } + + roles = append(roles, data.Resources...) + users = append(users, data.Included.Users...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return roles, users, nil +} + +// ListV3SpaceRolesByGUID retrieves roles based on query +func (c *Client) ListV3SpaceRolesByGUID(spaceGUID string) ([]V3Role, []V3User, error) { + query := url.Values{} + query["space_guids"] = []string{spaceGUID} + query["include"] = []string{"user"} + return c.ListV3RoleAndUsersByQuery(query) +} + +// ListV3SpaceRolesByGUIDAndType retrieves roles based on query +func (c *Client) ListV3SpaceRolesByGUIDAndType(spaceGUID string, roleType string) ([]V3User, error) { + query := url.Values{} + query["space_guids"] = []string{spaceGUID} + query["types"] = []string{roleType} + query["include"] = []string{"user"} + return c.ListV3RoleUsersByQuery(query) +} + +// ListV3SpaceRolesByGUIDAndType retrieves roles based on query +func (c *Client) ListV3OrganizationRolesByGUIDAndType(orgGUID string, roleType string) ([]V3User, error) { + query := url.Values{} + query["organization_guids"] = []string{orgGUID} + query["types"] = []string{roleType} + query["include"] = []string{"user"} + return c.ListV3RoleUsersByQuery(query) +} + +// ListV3OrganizationRolesByGUID retrieves roles based on query +func (c *Client) ListV3OrganizationRolesByGUID(orgGUID string) ([]V3Role, []V3User, error) { + query := url.Values{} + query["organization_guids"] = []string{orgGUID} + query["include"] = []string{"user"} + return c.ListV3RoleAndUsersByQuery(query) +} + +func (c *Client) DeleteV3Role(roleGUID string) error { + req := c.NewRequest("DELETE", "/v3/roles/"+roleGUID) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 role") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 role with GUID [%s], response code: %d", roleGUID, resp.StatusCode) + } + + return nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3routes.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3routes.go new file mode 100644 index 0000000..fe16f22 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3routes.go @@ -0,0 +1,133 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +type V3Route struct { + Guid string `json:"guid"` + Host string `json:"host"` + Path string `json:"path"` + Url string `json:"url"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Metadata Metadata `json:"metadata"` + Destinations []Destination `json:"destinations"` + Relationships map[string]V3ToOneRelationship `json:"relationships"` + Links map[string]Link `json:"links"` +} + +type listV3RouteResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Route `json:"resources,omitempty"` +} + +type Destination struct { + GUID string `json:"guid"` + App struct { + GUID string `json:"guid"` + Process struct { + Type string `json:"type"` + } `json:"process"` + } `json:"app"` + Weight interface{} `json:"weight"` + Port int `json:"port"` + Protocol string `json:"protocol"` +} + +func (c *Client) ListV3Routes() ([]V3Route, error) { + return c.ListV3RoutesByQuery(nil) +} + +func (c *Client) ListV3RoutesByQuery(query url.Values) ([]V3Route, error) { + var routes []V3Route + requestURL := "/v3/routes" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 service instances") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error listing v3 service instances, response code: %d", resp.StatusCode) + } + + var data listV3RouteResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 service instances") + } + + routes = append(routes, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 service instances") + } + } + + return routes, nil +} + +type CreateV3RouteOptionalParameters struct { + Host string `json:"host,omitempty"` + Path string `json:"path,omitempty"` + Metadata Metadata `json:"metadata,omitempty"` +} + +type routeRelationships struct { + Space V3ToOneRelationship `json:"space"` + Domain V3ToOneRelationship `json:"domain"` +} + +type createV3RouteRequest struct { + Relationships routeRelationships `json:"relationships"` + *CreateV3RouteOptionalParameters +} + +func (c *Client) CreateV3Route( + spaceGUID string, + domainGUID string, + opt *CreateV3RouteOptionalParameters, +) (*V3Route, error) { + + spaceRel := V3ToOneRelationship{Data: V3Relationship{GUID: spaceGUID}} + domainRel := V3ToOneRelationship{Data: V3Relationship{GUID: domainGUID}} + + req := c.NewRequest("POST", "/v3/routes") + req.obj = createV3RouteRequest{ + Relationships: routeRelationships{Space: spaceRel, Domain: domainRel}, + CreateV3RouteOptionalParameters: opt, + } + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 route") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("error creating v3 route, response code: %d", resp.StatusCode) + } + + var route V3Route + if err := json.NewDecoder(resp.Body).Decode(&route); err != nil { + return nil, errors.Wrap(err, "Error reading v3 app package") + } + + return &route, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3security_groups.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3security_groups.go new file mode 100644 index 0000000..57c9f4d --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3security_groups.go @@ -0,0 +1,195 @@ +package cfclient + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +// V3SecurityGroup implements the security group object. Security groups are collections of egress traffic rules that can be applied to the staging or running state of applications. +type V3SecurityGroup struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + GloballyEnabled V3GloballyEnabled `json:"globally_enabled,omitempty"` + Rules []V3Rule `json:"rules,omitempty"` + Relationships map[string]V3ToManyRelationships `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` +} + +// V3GloballyEnabled object controls if the group is applied globally to the lifecycle of all applications +type V3GloballyEnabled struct { + Running bool `json:"running,omitempty"` + Staging bool `json:"staging,omitempty"` +} + +// V3Rule is an object that provide a rule that will be applied by a security group +type V3Rule struct { + Protocol string `json:"protocol,omitempty"` + Destination string `json:"destination,omitempty"` + Ports string `json:"ports,omitempty"` + Type *int `json:"type,omitempty"` + Code *int `json:"code,omitempty"` + Description string `json:"description,omitempty"` + Log bool `json:"log,omitempty"` +} + +type listV3SecurityGroupResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3SecurityGroup `json:"resources,omitempty"` +} + +// ListV3SecurityGroupsByQuery retrieves security groups based on query +func (c *Client) ListV3SecurityGroupsByQuery(query url.Values) ([]V3SecurityGroup, error) { + var securityGroups []V3SecurityGroup + requestURL, err := url.Parse("/v3/security_groups") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 security groups") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 security groups, response code: %d", resp.StatusCode) + } + + var data listV3SecurityGroupResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 security groups") + } + + securityGroups = append(securityGroups, data.Resources...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return securityGroups, nil +} + +// CreateV3SecurityGroupRequest implements an object that is passed to CreateV3SecurityGroup method +type CreateV3SecurityGroupRequest struct { + Name string `json:"name"` + GloballyEnabled *V3GloballyEnabled `json:"globally_enabled,omitempty"` + Rules []*V3Rule `json:"rules,omitempty"` + Relationships map[string]V3ToManyRelationships `json:"relationships,omitempty"` +} + +// CreateV3SecurityGroup creates security group from CreateV3SecurityGroupRequest +func (c *Client) CreateV3SecurityGroup(r CreateV3SecurityGroupRequest) (*V3SecurityGroup, error) { + req := c.NewRequest("POST", "/v3/security_groups") + + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(r); err != nil { + return nil, err + } + req.body = buf + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 security group") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 security group %s, response code: %d", r.Name, resp.StatusCode) + } + + var securitygroup V3SecurityGroup + if err := json.NewDecoder(resp.Body).Decode(&securitygroup); err != nil { + return nil, errors.Wrap(err, "Error reading v3 security group JSON") + } + + return &securitygroup, nil +} + +// DeleteV3SecurityGroup deletes security group by GUID +func (c *Client) DeleteV3SecurityGroup(GUID string) error { + req := c.NewRequest("DELETE", "/v3/security_groups/"+GUID) + + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 security group") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 security group with GUID [%s], response code: %d", GUID, resp.StatusCode) + } + return nil +} + +// UpdateV3SecurityGroupRequest implements an object that is passed to UpdateV3SecurityGroup method +type UpdateV3SecurityGroupRequest struct { + Name string `json:"name,omitempty"` + GloballyEnabled *V3GloballyEnabled `json:"globally_enabled,omitempty"` + Rules []*V3Rule `json:"rules,omitempty"` +} + +// UpdateV3SecurityGroup updates security group by GUID and from UpdateV3SecurityGroupRequest +func (c *Client) UpdateV3SecurityGroup(GUID string, r UpdateV3SecurityGroupRequest) (*V3SecurityGroup, error) { + req := c.NewRequest("PATCH", "/v3/security_groups/"+GUID) + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(r); err != nil { + return nil, err + } + req.body = buf + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while updating v3 security group") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error updating v3 security group %s, response code: %d", GUID, resp.StatusCode) + } + + var securityGroup V3SecurityGroup + if err := json.NewDecoder(resp.Body).Decode(&securityGroup); err != nil { + return nil, errors.Wrap(err, "Error reading v3 security group JSON") + } + + return &securityGroup, nil +} + +// GetV3SecurityGroupByGUID retrieves security group base on provided GUID +func (c *Client) GetV3SecurityGroupByGUID(GUID string) (*V3SecurityGroup, error) { + req := c.NewRequest("GET", "/v3/security_groups/"+GUID) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 security group") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 security group with GUID [%s], response code: %d", GUID, resp.StatusCode) + } + + var securityGroup V3SecurityGroup + if err := json.NewDecoder(resp.Body).Decode(&securityGroup); err != nil { + return nil, errors.Wrap(err, "Error reading v3 security group JSON") + } + + return &securityGroup, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3service_credential_bindings.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3service_credential_bindings.go new file mode 100644 index 0000000..0a06c15 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3service_credential_bindings.go @@ -0,0 +1,97 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +// V3ServiceCredentialBindings implements the service credential binding object. a credential binding can be a binding between apps and a service instance or a service key +type V3ServiceCredentialBindings struct { + GUID string `json:"guid"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Type string `json:"type"` + LastOperation LastOperation `json:"last_operation"` + Metadata Metadata `json:"metadata"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links"` +} + +type listV3ServiceCredentialBindingsResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3ServiceCredentialBindings `json:"resources,omitempty"` +} + +// ListV3ServiceCredentialBindings retrieves all service credential bindings +func (c *Client) ListV3ServiceCredentialBindings() ([]V3ServiceCredentialBindings, error) { + return c.ListV3ServiceCredentialBindingsByQuery(nil) +} + +// ListV3ServiceCredentialBindingsByQuery retrieves service credential bindings using a query +func (c *Client) ListV3ServiceCredentialBindingsByQuery(query url.Values) ([]V3ServiceCredentialBindings, error) { + var svcCredentialBindings []V3ServiceCredentialBindings + requestURL := "/v3/service_credential_bindings" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 service credential bindings") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error listing v3 service credential bindings, response code: %d", resp.StatusCode) + } + + var data listV3ServiceCredentialBindingsResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 service credential bindings") + } + + svcCredentialBindings = append(svcCredentialBindings, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 service credential bindings") + } + } + + return svcCredentialBindings, nil +} + +// GetV3ServiceCredentialBindingsByGUID retrieves the service credential binding based on the provided guid +func (c *Client) GetV3ServiceCredentialBindingsByGUID(GUID string) (*V3ServiceCredentialBindings, error) { + requestURL := fmt.Sprintf("/v3/service_credential_bindings/%s", GUID) + req := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(req) + + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 service credential binding") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 service credential binding with GUID [%s], response code: %d", GUID, resp.StatusCode) + } + + var svcCredentialBindings V3ServiceCredentialBindings + if err := json.NewDecoder(resp.Body).Decode(&svcCredentialBindings); err != nil { + return nil, errors.Wrap(err, "Error reading v3 service credential binding JSON") + } + + return &svcCredentialBindings, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3service_instances.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3service_instances.go new file mode 100644 index 0000000..43f4271 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3service_instances.go @@ -0,0 +1,69 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "time" + + "github.com/pkg/errors" +) + +type V3ServiceInstance struct { + Guid string `json:"guid"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Metadata Metadata `json:"metadata"` + Links map[string]Link `json:"links"` +} + +type listV3ServiceInstancesResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3ServiceInstance `json:"resources,omitempty"` +} + +func (c *Client) ListV3ServiceInstances() ([]V3ServiceInstance, error) { + return c.ListV3ServiceInstancesByQuery(nil) +} + +func (c *Client) ListV3ServiceInstancesByQuery(query url.Values) ([]V3ServiceInstance, error) { + var svcInstances []V3ServiceInstance + requestURL := "/v3/service_instances" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 service instances") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error listing v3 service instances, response code: %d", resp.StatusCode) + } + + var data listV3ServiceInstancesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 service instances") + } + + svcInstances = append(svcInstances, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 service instances") + } + } + + return svcInstances, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3spaces.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3spaces.go new file mode 100644 index 0000000..3f5e4da --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3spaces.go @@ -0,0 +1,228 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +type V3Space struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type CreateV3SpaceRequest struct { + Name string + OrgGUID string + Metadata *V3Metadata +} + +type UpdateV3SpaceRequest struct { + Name string + Metadata *V3Metadata +} + +type V3SpaceUsers struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Relationships map[string]V3ToOneRelationship `json:"relationships,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +func (c *Client) CreateV3Space(r CreateV3SpaceRequest) (*V3Space, error) { + req := c.NewRequest("POST", "/v3/spaces") + params := map[string]interface{}{ + "name": r.Name, + "relationships": map[string]interface{}{ + "organization": V3ToOneRelationship{ + Data: V3Relationship{ + GUID: r.OrgGUID, + }, + }, + }, + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + + req.obj = params + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while creating v3 space") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusCreated { + return nil, fmt.Errorf("Error creating v3 space %s, response code: %d", r.Name, resp.StatusCode) + } + + var space V3Space + if err := json.NewDecoder(resp.Body).Decode(&space); err != nil { + return nil, errors.Wrap(err, "Error reading v3 space JSON") + } + + return &space, nil +} + +func (c *Client) GetV3SpaceByGUID(spaceGUID string) (*V3Space, error) { + req := c.NewRequest("GET", "/v3/spaces/"+spaceGUID) + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while getting v3 space") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error getting v3 space with GUID [%s], response code: %d", spaceGUID, resp.StatusCode) + } + + var space V3Space + if err := json.NewDecoder(resp.Body).Decode(&space); err != nil { + return nil, errors.Wrap(err, "Error reading v3 space JSON") + } + + return &space, nil +} + +func (c *Client) DeleteV3Space(spaceGUID string) error { + req := c.NewRequest("DELETE", "/v3/spaces/"+spaceGUID) + resp, err := c.DoRequest(req) + if err != nil { + return errors.Wrap(err, "Error while deleting v3 space") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusAccepted { + return fmt.Errorf("Error deleting v3 space with GUID [%s], response code: %d", spaceGUID, resp.StatusCode) + } + + return nil +} + +func (c *Client) UpdateV3Space(spaceGUID string, r UpdateV3SpaceRequest) (*V3Space, error) { + req := c.NewRequest("PATCH", "/v3/spaces/"+spaceGUID) + params := make(map[string]interface{}) + if r.Name != "" { + params["name"] = r.Name + } + if r.Metadata != nil { + params["metadata"] = r.Metadata + } + if len(params) > 0 { + req.obj = params + } + + resp, err := c.DoRequest(req) + if err != nil { + return nil, errors.Wrap(err, "Error while updating v3 space") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error updating v3 space %s, response code: %d", spaceGUID, resp.StatusCode) + } + + var space V3Space + if err := json.NewDecoder(resp.Body).Decode(&space); err != nil { + return nil, errors.Wrap(err, "Error reading v3 space JSON") + } + + return &space, nil +} + +type listV3SpacesResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Space `json:"resources,omitempty"` +} + +func (c *Client) ListV3SpacesByQuery(query url.Values) ([]V3Space, error) { + var spaces []V3Space + requestURL := "/v3/spaces" + if e := query.Encode(); len(e) > 0 { + requestURL += "?" + e + } + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 spaces") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 spaces, response code: %d", resp.StatusCode) + } + + var data listV3SpacesResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 spaces") + } + + spaces = append(spaces, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" || query.Get("page") != "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 spaces") + } + } + + return spaces, nil +} + +type listV3SpaceUsersResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3User `json:"resources,omitempty"` +} + +// ListV3SpaceUsers lists users by space GUID +func (c *Client) ListV3SpaceUsers(spaceGUID string) ([]V3User, error) { + var users []V3User + requestURL := "/v3/spaces/" + spaceGUID + "/users" + + for { + r := c.NewRequest("GET", requestURL) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 space users") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 space users, response code: %d", resp.StatusCode) + } + + var data listV3SpaceUsersResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 space users") + } + users = append(users, data.Resources...) + + requestURL = data.Pagination.Next.Href + if requestURL == "" { + break + } + requestURL, err = extractPathFromURL(requestURL) + if err != nil { + return nil, errors.Wrap(err, "Error parsing the next page request url for v3 space users") + } + } + + return users, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3stacks.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3stacks.go new file mode 100644 index 0000000..74647d6 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3stacks.go @@ -0,0 +1,66 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +// V3Stack implements stack object. Stacks are the base operating system and file system that your application will execute in. A stack is how you configure applications to run against different operating systems (like Windows or Linux) and different versions of those operating systems. +type V3Stack struct { + Name string `json:"name,omitempty"` + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Description string `json:"description,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type listV3StacksResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3Stack `json:"resources,omitempty"` +} + +// ListV3StacksByQuery retrieves stacks based on query +func (c *Client) ListV3StacksByQuery(query url.Values) ([]V3Stack, error) { + var stacks []V3Stack + requestURL, err := url.Parse("/v3/stacks") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 stacks") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 stacks, response code: %d", resp.StatusCode) + } + + var data listV3StacksResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 stacks") + } + + stacks = append(stacks, data.Resources...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return stacks, nil +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3types.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3types.go new file mode 100644 index 0000000..65bbd4e --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3types.go @@ -0,0 +1,36 @@ +package cfclient + +// Pagination is used by the V3 apis +type Pagination struct { + TotalResults int `json:"total_results"` + TotalPages int `json:"total_pages"` + First Link `json:"first"` + Last Link `json:"last"` + Next Link `json:"next"` + Previous Link `json:"previous"` +} + +// Link is a HATEOAS-style link for v3 apis +type Link struct { + Href string `json:"href"` + Method string `json:"method,omitempty"` +} + +// V3ToOneRelationship is a relationship to a single object +type V3ToOneRelationship struct { + Data V3Relationship `json:"data,omitempty"` +} + +// V3ToManyRelationships is a relationship to multiple objects +type V3ToManyRelationships struct { + Data []V3Relationship `json:"data,omitempty"` +} + +type V3Relationship struct { + GUID string `json:"guid,omitempty"` +} + +type V3Metadata struct { + Labels map[string]string `json:"labels,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` +} diff --git a/vendor/github.com/cloudfoundry-community/go-cfclient/v3users.go b/vendor/github.com/cloudfoundry-community/go-cfclient/v3users.go new file mode 100644 index 0000000..c66aaa5 --- /dev/null +++ b/vendor/github.com/cloudfoundry-community/go-cfclient/v3users.go @@ -0,0 +1,67 @@ +package cfclient + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/pkg/errors" +) + +// V3User implements the user object +type V3User struct { + GUID string `json:"guid,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Username string `json:"username,omitempty"` + PresentationName string `json:"presentation_name,omitempty"` + Origin string `json:"origin,omitempty"` + Links map[string]Link `json:"links,omitempty"` + Metadata V3Metadata `json:"metadata,omitempty"` +} + +type listV3UsersResponse struct { + Pagination Pagination `json:"pagination,omitempty"` + Resources []V3User `json:"resources,omitempty"` +} + +// ListV3UsersByQuery by query +func (c *Client) ListV3UsersByQuery(query url.Values) ([]V3User, error) { + var users []V3User + requestURL, err := url.Parse("/v3/users") + if err != nil { + return nil, err + } + requestURL.RawQuery = query.Encode() + + for { + r := c.NewRequest("GET", fmt.Sprintf("%s?%s", requestURL.Path, requestURL.RawQuery)) + resp, err := c.DoRequest(r) + if err != nil { + return nil, errors.Wrap(err, "Error requesting v3 users") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Error listing v3 users, response code: %d", resp.StatusCode) + } + + var data listV3UsersResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return nil, errors.Wrap(err, "Error parsing JSON from list v3 users") + } + + users = append(users, data.Resources...) + + requestURL, err = url.Parse(data.Pagination.Next.Href) + if err != nil { + return nil, errors.Wrap(err, "Error parsing next page URL") + } + if requestURL.String() == "" { + break + } + } + + return users, nil +} diff --git a/vendor/github.com/ess/debuggable/.gitignore b/vendor/github.com/ess/debuggable/.gitignore new file mode 100644 index 0000000..5c55aef --- /dev/null +++ b/vendor/github.com/ess/debuggable/.gitignore @@ -0,0 +1,4 @@ +*.swp +*~ +/vendor +.coverage diff --git a/vendor/github.com/ess/debuggable/LICENSE b/vendor/github.com/ess/debuggable/LICENSE new file mode 100644 index 0000000..e2279d8 --- /dev/null +++ b/vendor/github.com/ess/debuggable/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Dennis Walters + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/ess/debuggable/README.md b/vendor/github.com/ess/debuggable/README.md new file mode 100644 index 0000000..79eedf6 --- /dev/null +++ b/vendor/github.com/ess/debuggable/README.md @@ -0,0 +1,3 @@ +## History ## + +* v1.0.0 - Initial release diff --git a/vendor/github.com/ess/debuggable/debuggable.go b/vendor/github.com/ess/debuggable/debuggable.go new file mode 100644 index 0000000..09c6746 --- /dev/null +++ b/vendor/github.com/ess/debuggable/debuggable.go @@ -0,0 +1,32 @@ +// package debuggable is a quick-and-dirty shortcut for taking actions in an +// app if the DEBUG environment variable is present. +package debuggable + +import ( + "os" +) + +const ( + envVar = "DEBUG" +) + +// Enabled returns true if debugging is enabled and false otherwise. +func Enabled() bool { + _, present := os.LookupEnv(envVar) + + return present +} + +// Copyright © 2019 Dennis Walters +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. diff --git a/vendor/github.com/ess/debuggable/go.mod b/vendor/github.com/ess/debuggable/go.mod new file mode 100644 index 0000000..aad373b --- /dev/null +++ b/vendor/github.com/ess/debuggable/go.mod @@ -0,0 +1 @@ +module github.com/ess/debuggable diff --git a/vendor/github.com/ess/hype/.gitignore b/vendor/github.com/ess/hype/.gitignore new file mode 100644 index 0000000..2faba77 --- /dev/null +++ b/vendor/github.com/ess/hype/.gitignore @@ -0,0 +1,4 @@ +*~ +*.swp + +/go.sum diff --git a/vendor/github.com/ess/hype/README.md b/vendor/github.com/ess/hype/README.md new file mode 100644 index 0000000..4e96664 --- /dev/null +++ b/vendor/github.com/ess/hype/README.md @@ -0,0 +1,12 @@ +# hype # + +## History ## + +* v1.1.5 - driver now supports skipping tls verification +* v1.1.4 - all responses wrap an actual response +* v1.1.3 - made Reponse interfaceable +* v1.1.2 - now with actual responses wrapped +* v1.1.1 - now with extra 1s +* v1.1.0 - and now 1.1.0 +* v1.0.0 - let's try 1.0.0 +* v0.1.0 - blah diff --git a/vendor/github.com/ess/hype/driver.go b/vendor/github.com/ess/hype/driver.go new file mode 100644 index 0000000..a805b02 --- /dev/null +++ b/vendor/github.com/ess/hype/driver.go @@ -0,0 +1,112 @@ +package hype + +import ( + "bytes" + "crypto/tls" + "fmt" + "net/http" + "net/url" + "strings" + "time" + + "github.com/ess/debuggable" +) + +type Driver struct { + raw *http.Client + baseURL url.URL +} + +func New(baseURL string) (*Driver, error) { + url, err := url.Parse(baseURL) + if err != nil { + return nil, err + } + + d := &Driver{ + &http.Client{Timeout: 20 * time.Second}, + *url, + } + + return d, nil +} + +func (driver *Driver) WithoutTLSVerification() *Driver { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + client := &http.Client{Timeout: driver.raw.Timeout, Transport: tr} + + return &Driver{client, driver.baseURL} +} + +func (driver *Driver) Get(path string, params Params) *Request { + return driver.newRequest("GET", path, saneParams(params).ToValues(), nil) +} + +func (driver *Driver) Post(path string, params Params, data []byte) *Request { + return driver.newRequest("POST", path, saneParams(params).ToValues(), data) +} + +func (driver *Driver) Put(path string, params Params, data []byte) *Request { + return driver.newRequest("PUT", path, saneParams(params).ToValues(), data) +} + +func (driver *Driver) Patch(path string, params Params, data []byte) *Request { + return driver.newRequest("PATCH", path, saneParams(params).ToValues(), data) +} + +func (driver *Driver) Delete(path string, params Params) *Request { + return driver.newRequest("DELETE", path, saneParams(params).ToValues(), nil) +} + +func (driver *Driver) newRequest(verb string, path string, params url.Values, data []byte) *Request { + request, err := http.NewRequest( + verb, + driver.constructRequestURL(path, params), + bytes.NewReader(data), + ) + + if err != nil { + return &Request{nil, nil, err} + } + + return &Request{request, driver.raw, nil} +} + +func (driver *Driver) constructRequestURL(path string, params url.Values) string { + + pathParts := []string{driver.baseURL.Path, path} + + requestURL := url.URL{ + Scheme: driver.baseURL.Scheme, + Host: driver.baseURL.Host, + Path: strings.Join(pathParts, "/"), + RawQuery: params.Encode(), + } + + result := requestURL.String() + + if debuggable.Enabled() { + fmt.Println("[DEBUG] Request URL:", result) + } + + return result +} + +/* +Copyright 2021 Dennis Walters + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ diff --git a/vendor/github.com/ess/hype/go.mod b/vendor/github.com/ess/hype/go.mod new file mode 100644 index 0000000..cbc294e --- /dev/null +++ b/vendor/github.com/ess/hype/go.mod @@ -0,0 +1,5 @@ +module github.com/ess/hype + +go 1.15 + +require github.com/ess/debuggable v1.0.0 diff --git a/vendor/github.com/ess/hype/header.go b/vendor/github.com/ess/hype/header.go new file mode 100644 index 0000000..bd46ff9 --- /dev/null +++ b/vendor/github.com/ess/hype/header.go @@ -0,0 +1,10 @@ +package hype + +type Header struct { + Name string + Value string +} + +func NewHeader(name string, value string) *Header { + return &Header{Name: name, Value: value} +} diff --git a/vendor/github.com/ess/hype/params.go b/vendor/github.com/ess/hype/params.go new file mode 100644 index 0000000..d7d1ac6 --- /dev/null +++ b/vendor/github.com/ess/hype/params.go @@ -0,0 +1,23 @@ +package hype + +import ( + "net/url" +) + +type Params map[string][]string + +func (params Params) Set(key string, value string) { + params[key] = []string{value} +} + +func (params Params) ToValues() url.Values { + return url.Values(params) +} + +func saneParams(p Params) Params { + if p == nil { + return make(Params) + } + + return p +} diff --git a/vendor/github.com/ess/hype/request.go b/vendor/github.com/ess/hype/request.go new file mode 100644 index 0000000..9d8e614 --- /dev/null +++ b/vendor/github.com/ess/hype/request.go @@ -0,0 +1,45 @@ +package hype + +import ( + "fmt" + "io/ioutil" + "net/http" +) + +type Request struct { + actual *http.Request + raw *http.Client + err error +} + +func (request *Request) WithHeader(header *Header) *Request { + return request.WithHeaderSet(header) +} + +func (request *Request) WithHeaderSet(headers ...*Header) *Request { + for _, header := range headers { + request.actual.Header.Add(header.Name, header.Value) + } + + return request +} + +func (request *Request) Response() Response { + response, err := request.raw.Do(request.actual) + if err != nil { + return Response{response, nil, err} + } + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return Response{response, nil, err} + } + + defer response.Body.Close() + + if response.StatusCode < 200 || response.StatusCode > 299 { + return Response{response, nil, fmt.Errorf("response status: %d", response.StatusCode)} + } + + return Response{response, body, nil} +} diff --git a/vendor/github.com/ess/hype/response.go b/vendor/github.com/ess/hype/response.go new file mode 100644 index 0000000..27d1335 --- /dev/null +++ b/vendor/github.com/ess/hype/response.go @@ -0,0 +1,30 @@ +package hype + +import ( + "net/http" +) + +type Response struct { + actual *http.Response + data []byte + err error +} + +func (response Response) Okay() bool { + if response.err == nil { + return true + } + return false +} + +func (response Response) Data() []byte { + return response.data +} + +func (response Response) Error() error { + return response.err +} + +func (response Response) Header(name string) string { + return response.actual.Header.Get(name) +} diff --git a/vendor/github.com/gammazero/deque/.gitignore b/vendor/github.com/gammazero/deque/.gitignore new file mode 100644 index 0000000..b33406f --- /dev/null +++ b/vendor/github.com/gammazero/deque/.gitignore @@ -0,0 +1,26 @@ +*~ + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/gammazero/deque/LICENSE b/vendor/github.com/gammazero/deque/LICENSE new file mode 100644 index 0000000..0566f26 --- /dev/null +++ b/vendor/github.com/gammazero/deque/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Andrew J. Gillis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/gammazero/deque/README.md b/vendor/github.com/gammazero/deque/README.md new file mode 100644 index 0000000..45a0647 --- /dev/null +++ b/vendor/github.com/gammazero/deque/README.md @@ -0,0 +1,72 @@ +# deque + +[![GoDoc](https://pkg.go.dev/badge/github.com/gammazero/deque)](https://pkg.go.dev/github.com/gammazero/deque) +[![Build Status](https://github.com/gammazero/deque/actions/workflows/go.yml/badge.svg)](https://github.com/gammazero/deque/actions/workflows/go.yml) +[![Go Report Card](https://goreportcard.com/badge/github.com/gammazero/deque)](https://goreportcard.com/report/github.com/gammazero/deque) +[![codecov](https://codecov.io/gh/gammazero/deque/branch/master/graph/badge.svg)](https://codecov.io/gh/gammazero/deque) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) + +Fast ring-buffer deque ([double-ended queue](https://en.wikipedia.org/wiki/Double-ended_queue)) implementation. + +For a pictorial description, see the [Deque diagram](https://github.com/gammazero/deque/wiki) + +## Installation + +``` +$ go get github.com/gammazero/deque +``` + +## Deque data structure + +Deque generalizes a queue and a stack, to efficiently add and remove items at either end with O(1) performance. [Queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) (FIFO) operations are supported using `PushBack()` and `PopFront()`. [Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) (LIFO) operations are supported using `PushBack()` and `PopBack()`. + +## Ring-buffer Performance + +This deque implementation is optimized for CPU and GC performance. The circular buffer automatically re-sizes by powers of two, growing when additional capacity is needed and shrinking when only a quarter of the capacity is used, and uses bitwise arithmetic for all calculations. Since growth is by powers of two, adding elements will only cause O(log n) allocations. + +The ring-buffer implementation improves memory and time performance with fewer GC pauses, compared to implementations based on slices and linked lists. By wrapping around the buffer, previously used space is reused, making allocation unnecessary until all buffer capacity is used. This is particularly efficient when data going into the dequeue is relatively balanced against data coming out. However, if size changes are very large and only fill and then empty then deque, the ring structure offers little benefit for memory reuse. For that usage pattern a different implementation may be preferable. + +For maximum speed, this deque implementation leaves concurrency safety up to the application to provide, however the application chooses, if needed at all. + +## Reading Empty Deque + +Since it is OK for the deque to contain a nil value, it is necessary to either panic or return a second boolean value to indicate the deque is empty, when reading or removing an element. This deque panics when reading from an empty deque. This is a run-time check to help catch programming errors, which may be missed if a second return value is ignored. Simply check Deque.Len() before reading from the deque. + +## Example + +```go +package main + +import ( + "fmt" + "github.com/gammazero/deque" +) + +func main() { + var q deque.Deque + q.PushBack("foo") + q.PushBack("bar") + q.PushBack("baz") + + fmt.Println(q.Len()) // Prints: 3 + fmt.Println(q.Front()) // Prints: foo + fmt.Println(q.Back()) // Prints: baz + + q.PopFront() // remove "foo" + q.PopBack() // remove "baz" + + q.PushFront("hello") + q.PushBack("world") + + // Consume deque and print elements. + for q.Len() != 0 { + fmt.Println(q.PopFront()) + } +} +``` + +## Uses + +Deque can be used as both a: +- [Queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) using `PushBack` and `PopFront` +- [Stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) using `PushBack` and `PopBack` diff --git a/vendor/github.com/gammazero/deque/deque.go b/vendor/github.com/gammazero/deque/deque.go new file mode 100644 index 0000000..7baed9d --- /dev/null +++ b/vendor/github.com/gammazero/deque/deque.go @@ -0,0 +1,301 @@ +package deque + +// minCapacity is the smallest capacity that deque may have. +// Must be power of 2 for bitwise modulus: x % n == x & (n - 1). +const minCapacity = 16 + +// Deque represents a single instance of the deque data structure. +type Deque struct { + buf []interface{} + head int + tail int + count int + minCap int +} + +// New creates a new Deque, optionally setting the current and minimum capacity +// when non-zero values are given for these. +// +// To create a Deque with capacity to store 2048 items without resizing, and +// that will not resize below space for 32 items when removing itmes: +// d := deque.New(2048, 32) +// +// To create a Deque that has not yet allocated memory, but after it does will +// never resize to have space for less than 64 items: +// d := deque.New(0, 64) +// +// Note that any values supplied here are rounded up to the nearest power of 2. +func New(size ...int) *Deque { + var capacity, minimum int + if len(size) >= 1 { + capacity = size[0] + if len(size) >= 2 { + minimum = size[1] + } + } + + minCap := minCapacity + for minCap < minimum { + minCap <<= 1 + } + + var buf []interface{} + if capacity != 0 { + bufSize := minCap + for bufSize < capacity { + bufSize <<= 1 + } + buf = make([]interface{}, bufSize) + } + + return &Deque{ + buf: buf, + minCap: minCap, + } +} + +// Cap returns the current capacity of the Deque. +func (q *Deque) Cap() int { + return len(q.buf) +} + +// Len returns the number of elements currently stored in the queue. +func (q *Deque) Len() int { + return q.count +} + +// PushBack appends an element to the back of the queue. Implements FIFO when +// elements are removed with PopFront(), and LIFO when elements are removed +// with PopBack(). +func (q *Deque) PushBack(elem interface{}) { + q.growIfFull() + + q.buf[q.tail] = elem + // Calculate new tail position. + q.tail = q.next(q.tail) + q.count++ +} + +// PushFront prepends an element to the front of the queue. +func (q *Deque) PushFront(elem interface{}) { + q.growIfFull() + + // Calculate new head position. + q.head = q.prev(q.head) + q.buf[q.head] = elem + q.count++ +} + +// PopFront removes and returns the element from the front of the queue. +// Implements FIFO when used with PushBack(). If the queue is empty, the call +// panics. +func (q *Deque) PopFront() interface{} { + if q.count <= 0 { + panic("deque: PopFront() called on empty queue") + } + ret := q.buf[q.head] + q.buf[q.head] = nil + // Calculate new head position. + q.head = q.next(q.head) + q.count-- + + q.shrinkIfExcess() + return ret +} + +// PopBack removes and returns the element from the back of the queue. +// Implements LIFO when used with PushBack(). If the queue is empty, the call +// panics. +func (q *Deque) PopBack() interface{} { + if q.count <= 0 { + panic("deque: PopBack() called on empty queue") + } + + // Calculate new tail position + q.tail = q.prev(q.tail) + + // Remove value at tail. + ret := q.buf[q.tail] + q.buf[q.tail] = nil + q.count-- + + q.shrinkIfExcess() + return ret +} + +// Front returns the element at the front of the queue. This is the element +// that would be returned by PopFront(). This call panics if the queue is +// empty. +func (q *Deque) Front() interface{} { + if q.count <= 0 { + panic("deque: Front() called when empty") + } + return q.buf[q.head] +} + +// Back returns the element at the back of the queue. This is the element +// that would be returned by PopBack(). This call panics if the queue is +// empty. +func (q *Deque) Back() interface{} { + if q.count <= 0 { + panic("deque: Back() called when empty") + } + return q.buf[q.prev(q.tail)] +} + +// At returns the element at index i in the queue without removing the element +// from the queue. This method accepts only non-negative index values. At(0) +// refers to the first element and is the same as Front(). At(Len()-1) refers +// to the last element and is the same as Back(). If the index is invalid, the +// call panics. +// +// The purpose of At is to allow Deque to serve as a more general purpose +// circular buffer, where items are only added to and removed from the ends of +// the deque, but may be read from any place within the deque. Consider the +// case of a fixed-size circular log buffer: A new entry is pushed onto one end +// and when full the oldest is popped from the other end. All the log entries +// in the buffer must be readable without altering the buffer contents. +func (q *Deque) At(i int) interface{} { + if i < 0 || i >= q.count { + panic("deque: At() called with index out of range") + } + // bitwise modulus + return q.buf[(q.head+i)&(len(q.buf)-1)] +} + +// Set puts the element at index i in the queue. Set shares the same purpose +// than At() but perform the opposite operation. The index i is the same +// index defined by At(). If the index is invalid, the call panics. +func (q *Deque) Set(i int, elem interface{}) { + if i < 0 || i >= q.count { + panic("deque: Set() called with index out of range") + } + // bitwise modulus + q.buf[(q.head+i)&(len(q.buf)-1)] = elem +} + +// Clear removes all elements from the queue, but retains the current capacity. +// This is useful when repeatedly reusing the queue at high frequency to avoid +// GC during reuse. The queue will not be resized smaller as long as items are +// only added. Only when items are removed is the queue subject to getting +// resized smaller. +func (q *Deque) Clear() { + // bitwise modulus + modBits := len(q.buf) - 1 + for h := q.head; h != q.tail; h = (h + 1) & modBits { + q.buf[h] = nil + } + q.head = 0 + q.tail = 0 + q.count = 0 +} + +// Rotate rotates the deque n steps front-to-back. If n is negative, rotates +// back-to-front. Having Deque provide Rotate() avoids resizing that could +// happen if implementing rotation using only Pop and Push methods. +func (q *Deque) Rotate(n int) { + if q.count <= 1 { + return + } + // Rotating a multiple of q.count is same as no rotation. + n %= q.count + if n == 0 { + return + } + + modBits := len(q.buf) - 1 + // If no empty space in buffer, only move head and tail indexes. + if q.head == q.tail { + // Calculate new head and tail using bitwise modulus. + q.head = (q.head + n) & modBits + q.tail = (q.tail + n) & modBits + return + } + + if n < 0 { + // Rotate back to front. + for ; n < 0; n++ { + // Calculate new head and tail using bitwise modulus. + q.head = (q.head - 1) & modBits + q.tail = (q.tail - 1) & modBits + // Put tail value at head and remove value at tail. + q.buf[q.head] = q.buf[q.tail] + q.buf[q.tail] = nil + } + return + } + + // Rotate front to back. + for ; n > 0; n-- { + // Put head value at tail and remove value at head. + q.buf[q.tail] = q.buf[q.head] + q.buf[q.head] = nil + // Calculate new head and tail using bitwise modulus. + q.head = (q.head + 1) & modBits + q.tail = (q.tail + 1) & modBits + } +} + +// SetMinCapacity sets a minimum capacity of 2^minCapacityExp. If the value of +// the minimum capacity is less than or equal to the minimum allowed, then +// capacity is set to the minimum allowed. This may be called at anytime to +// set a new minimum capacity. +// +// Setting a larger minimum capacity may be used to prevent resizing when the +// number of stored items changes frequently across a wide range. +func (q *Deque) SetMinCapacity(minCapacityExp uint) { + if 1< minCapacity { + q.minCap = 1 << minCapacityExp + } else { + q.minCap = minCapacity + } +} + +// prev returns the previous buffer position wrapping around buffer. +func (q *Deque) prev(i int) int { + return (i - 1) & (len(q.buf) - 1) // bitwise modulus +} + +// next returns the next buffer position wrapping around buffer. +func (q *Deque) next(i int) int { + return (i + 1) & (len(q.buf) - 1) // bitwise modulus +} + +// growIfFull resizes up if the buffer is full. +func (q *Deque) growIfFull() { + if q.count != len(q.buf) { + return + } + if len(q.buf) == 0 { + if q.minCap == 0 { + q.minCap = minCapacity + } + q.buf = make([]interface{}, q.minCap) + return + } + q.resize() +} + +// shrinkIfExcess resize down if the buffer 1/4 full. +func (q *Deque) shrinkIfExcess() { + if len(q.buf) > q.minCap && (q.count<<2) == len(q.buf) { + q.resize() + } +} + +// resize resizes the deque to fit exactly twice its current contents. This is +// used to grow the queue when it is full, and also to shrink it when it is +// only a quarter full. +func (q *Deque) resize() { + newBuf := make([]interface{}, q.count<<1) + if q.tail > q.head { + copy(newBuf, q.buf[q.head:q.tail]) + } else { + n := copy(newBuf, q.buf[q.head:]) + copy(newBuf[n:], q.buf[:q.tail]) + } + + q.head = 0 + q.tail = q.count + q.buf = newBuf +} diff --git a/vendor/github.com/gammazero/deque/doc.go b/vendor/github.com/gammazero/deque/doc.go new file mode 100644 index 0000000..c9647f9 --- /dev/null +++ b/vendor/github.com/gammazero/deque/doc.go @@ -0,0 +1,34 @@ +/* +Package deque provides a fast ring-buffer deque (double-ended queue) +implementation. + +Deque generalizes a queue and a stack, to efficiently add and remove items at +either end with O(1) performance. Queue (FIFO) operations are supported using +PushBack() and PopFront(). Stack (LIFO) operations are supported using +PushBack() and PopBack(). + +Ring-buffer Performance + +The ring-buffer automatically resizes by +powers of two, growing when additional capacity is needed and shrinking when +only a quarter of the capacity is used, and uses bitwise arithmetic for all +calculations. + +The ring-buffer implementation significantly improves memory and time +performance with fewer GC pauses, compared to implementations based on slices +and linked lists. + +For maximum speed, this deque implementation leaves concurrency safety up to +the application to provide, however the application chooses, if needed at all. + +Reading Empty Deque + +Since it is OK for the deque to contain a nil value, it is necessary to either +panic or return a second boolean value to indicate the deque is empty, when +reading or removing an element. This deque panics when reading from an empty +deque. This is a run-time check to help catch programming errors, which may be +missed if a second return value is ignored. Simply check Deque.Len() before +reading from the deque. + +*/ +package deque diff --git a/vendor/github.com/gammazero/deque/go.mod b/vendor/github.com/gammazero/deque/go.mod new file mode 100644 index 0000000..b440602 --- /dev/null +++ b/vendor/github.com/gammazero/deque/go.mod @@ -0,0 +1,3 @@ +module github.com/gammazero/deque + +go 1.15 diff --git a/vendor/github.com/gammazero/workerpool/.gitignore b/vendor/github.com/gammazero/workerpool/.gitignore new file mode 100644 index 0000000..ff737f5 --- /dev/null +++ b/vendor/github.com/gammazero/workerpool/.gitignore @@ -0,0 +1,28 @@ +*~ + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +coverage.out diff --git a/vendor/github.com/gammazero/workerpool/LICENSE b/vendor/github.com/gammazero/workerpool/LICENSE new file mode 100644 index 0000000..f6ff6ce --- /dev/null +++ b/vendor/github.com/gammazero/workerpool/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Andrew J. Gillis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/gammazero/workerpool/README.md b/vendor/github.com/gammazero/workerpool/README.md new file mode 100644 index 0000000..75c0b9c --- /dev/null +++ b/vendor/github.com/gammazero/workerpool/README.md @@ -0,0 +1,48 @@ +# workerpool + +[![GoDoc](https://pkg.go.dev/badge/github.com/gammazero/workerpool)](https://pkg.go.dev/github.com/gammazero/workerpool) +[![Build Status](https://github.com/gammazero/workerpool/actions/workflows/go.yml/badge.svg)](https://github.com/gammazero/workerpool/actions/workflows/go.yml) +[![Go Report Card](https://goreportcard.com/badge/github.com/gammazero/workerpool)](https://goreportcard.com/report/github.com/gammazero/workerpool) +[![codecov](https://codecov.io/gh/gammazero/workerpool/branch/master/graph/badge.svg)](https://codecov.io/gh/gammazero/workerpool) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/gammazero/workerpool/blob/master/LICENSE) + +Concurrency limiting goroutine pool. Limits the concurrency of task execution, not the number of tasks queued. Never blocks submitting tasks, no matter how many tasks are queued. + +This implementation builds on ideas from the following: + +- http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang +- http://nesv.github.io/golang/2014/02/25/worker-queues-in-go.html + +## Installation +To install this package, you need to setup your Go workspace. The simplest way to install the library is to run: +``` +$ go get github.com/gammazero/workerpool +``` + +## Example +```go +package main + +import ( + "fmt" + "github.com/gammazero/workerpool" +) + +func main() { + wp := workerpool.New(2) + requests := []string{"alpha", "beta", "gamma", "delta", "epsilon"} + + for _, r := range requests { + r := r + wp.Submit(func() { + fmt.Println("Handling request:", r) + }) + } + + wp.StopWait() +} +``` + +## Usage Note + +There is no upper limit on the number of tasks queued, other than the limits of system resources. If the number of inbound tasks is too many to even queue for pending processing, then the solution is outside the scope of workerpool. If should be solved by distributing load over multiple systems, and/or storing input for pending processing in intermediate storage such as a file system, distributed message queue, etc. diff --git a/vendor/github.com/gammazero/workerpool/doc.go b/vendor/github.com/gammazero/workerpool/doc.go new file mode 100644 index 0000000..c189fde --- /dev/null +++ b/vendor/github.com/gammazero/workerpool/doc.go @@ -0,0 +1,66 @@ +/* +Package workerpool queues work to a limited number of goroutines. + +The purpose of the worker pool is to limit the concurrency of tasks +executed by the workers. This is useful when performing tasks that require +sufficient resources (CPU, memory, etc.), and running too many tasks at the +same time would exhaust resources. + +Non-blocking task submission + +A task is a function submitted to the worker pool for execution. Submitting +tasks to this worker pool will not block, regardless of the number of tasks. +Incoming tasks are immediately dispatched to an available +worker. If no worker is immediately available, or there are already tasks +waiting for an available worker, then the task is put on a waiting queue to +wait for an available worker. + +The intent of the worker pool is to limit the concurrency of task execution, +not limit the number of tasks queued to be executed. Therefore, this unbounded +input of tasks is acceptable as the tasks cannot be discarded. If the number +of inbound tasks is too many to even queue for pending processing, then the +solution is outside the scope of workerpool, and should be solved by +distributing load over multiple systems, and/or storing input for pending +processing in intermediate storage such as a database, file system, distributed +message queue, etc. + +Dispatcher + +This worker pool uses a single dispatcher goroutine to read tasks from the +input task queue and dispatch them to worker goroutines. This allows for a +small input channel, and lets the dispatcher queue as many tasks as are +submitted when there are no available workers. Additionally, the dispatcher +can adjust the number of workers as appropriate for the work load, without +having to utilize locked counters and checks incurred on task submission. + +When no tasks have been submitted for a period of time, a worker is removed by +the dispatcher. This is done until there are no more workers to remove. The +minimum number of workers is always zero, because the time to start new workers +is insignificant. + +Usage note + +It is advisable to use different worker pools for tasks that are bound by +different resources, or that have different resource use patterns. For +example, tasks that use X Mb of memory may need different concurrency limits +than tasks that use Y Mb of memory. + +Waiting queue vs goroutines + +When there are no available workers to handle incoming tasks, the tasks are put +on a waiting queue, in this implementation. In implementations mentioned in +the credits below, these tasks were passed to goroutines. Using a queue is +faster and has less memory overhead than creating a separate goroutine for each +waiting task, allowing a much higher number of waiting tasks. Also, using a +waiting queue ensures that tasks are given to workers in the order the tasks +were received. + +Credits + +This implementation builds on ideas from the following: + +http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang +http://nesv.github.io/golang/2014/02/25/worker-queues-in-go.html + +*/ +package workerpool diff --git a/vendor/github.com/gammazero/workerpool/go.mod b/vendor/github.com/gammazero/workerpool/go.mod new file mode 100644 index 0000000..25c741d --- /dev/null +++ b/vendor/github.com/gammazero/workerpool/go.mod @@ -0,0 +1,5 @@ +module github.com/gammazero/workerpool + +require github.com/gammazero/deque v0.1.0 + +go 1.15 diff --git a/vendor/github.com/gammazero/workerpool/go.sum b/vendor/github.com/gammazero/workerpool/go.sum new file mode 100644 index 0000000..e9d2426 --- /dev/null +++ b/vendor/github.com/gammazero/workerpool/go.sum @@ -0,0 +1,2 @@ +github.com/gammazero/deque v0.1.0 h1:f9LnNmq66VDeuAlSAapemq/U7hJ2jpIWa4c09q8Dlik= +github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= diff --git a/vendor/github.com/gammazero/workerpool/workerpool.go b/vendor/github.com/gammazero/workerpool/workerpool.go new file mode 100644 index 0000000..4ccca12 --- /dev/null +++ b/vendor/github.com/gammazero/workerpool/workerpool.go @@ -0,0 +1,305 @@ +package workerpool + +import ( + "context" + "sync" + "sync/atomic" + "time" + + "github.com/gammazero/deque" +) + +const ( + // If workes idle for at least this period of time, then stop a worker. + idleTimeout = 2 * time.Second +) + +// New creates and starts a pool of worker goroutines. +// +// The maxWorkers parameter specifies the maximum number of workers that can +// execute tasks concurrently. When there are no incoming tasks, workers are +// gradually stopped until there are no remaining workers. +func New(maxWorkers int) *WorkerPool { + // There must be at least one worker. + if maxWorkers < 1 { + maxWorkers = 1 + } + + pool := &WorkerPool{ + maxWorkers: maxWorkers, + taskQueue: make(chan func(), 1), + workerQueue: make(chan func()), + stopSignal: make(chan struct{}), + stoppedChan: make(chan struct{}), + } + + // Start the task dispatcher. + go pool.dispatch() + + return pool +} + +// WorkerPool is a collection of goroutines, where the number of concurrent +// goroutines processing requests does not exceed the specified maximum. +type WorkerPool struct { + maxWorkers int + taskQueue chan func() + workerQueue chan func() + stoppedChan chan struct{} + stopSignal chan struct{} + waitingQueue deque.Deque + stopLock sync.Mutex + stopOnce sync.Once + stopped bool + waiting int32 + wait bool +} + +// Size returns the maximum number of concurrent workers. +func (p *WorkerPool) Size() int { + return p.maxWorkers +} + +// Stop stops the worker pool and waits for only currently running tasks to +// complete. Pending tasks that are not currently running are abandoned. +// Tasks must not be submitted to the worker pool after calling stop. +// +// Since creating the worker pool starts at least one goroutine, for the +// dispatcher, Stop() or StopWait() should be called when the worker pool is no +// longer needed. +func (p *WorkerPool) Stop() { + p.stop(false) +} + +// StopWait stops the worker pool and waits for all queued tasks tasks to +// complete. No additional tasks may be submitted, but all pending tasks are +// executed by workers before this function returns. +func (p *WorkerPool) StopWait() { + p.stop(true) +} + +// Stopped returns true if this worker pool has been stopped. +func (p *WorkerPool) Stopped() bool { + p.stopLock.Lock() + defer p.stopLock.Unlock() + return p.stopped +} + +// Submit enqueues a function for a worker to execute. +// +// Any external values needed by the task function must be captured in a +// closure. Any return values should be returned over a channel that is +// captured in the task function closure. +// +// Submit will not block regardless of the number of tasks submitted. Each +// task is immediately given to an available worker or to a newly started +// worker. If there are no available workers, and the maximum number of +// workers are already created, then the task is put onto a waiting queue. +// +// When there are tasks on the waiting queue, any additional new tasks are put +// on the waiting queue. Tasks are removed from the waiting queue as workers +// become available. +// +// As long as no new tasks arrive, one available worker is shutdown each time +// period until there are no more idle workers. Since the time to start new +// goroutines is not significant, there is no need to retain idle workers +// indefinitely. +func (p *WorkerPool) Submit(task func()) { + if task != nil { + p.taskQueue <- task + } +} + +// SubmitWait enqueues the given function and waits for it to be executed. +func (p *WorkerPool) SubmitWait(task func()) { + if task == nil { + return + } + doneChan := make(chan struct{}) + p.taskQueue <- func() { + task() + close(doneChan) + } + <-doneChan +} + +// WaitingQueueSize returns the count of tasks in the waiting queue. +func (p *WorkerPool) WaitingQueueSize() int { + return int(atomic.LoadInt32(&p.waiting)) +} + +// Pause causes all workers to wait on the given Context, thereby making them +// unavailable to run tasks. Pause returns when all workers are waiting. +// Tasks can continue to be queued to the workerpool, but are not executed +// until the Context is canceled or times out. +// +// Calling Pause when the worker pool is already paused causes Pause to wait +// until all previous pauses are canceled. This allows a goroutine to take +// control of pausing and unpausing the pool as soon as other goroutines have +// unpaused it. +// +// When the workerpool is stopped, workers are unpaused and queued tasks are +// executed during StopWait. +func (p *WorkerPool) Pause(ctx context.Context) { + p.stopLock.Lock() + defer p.stopLock.Unlock() + if p.stopped { + return + } + ready := new(sync.WaitGroup) + ready.Add(p.maxWorkers) + for i := 0; i < p.maxWorkers; i++ { + p.Submit(func() { + ready.Done() + select { + case <-ctx.Done(): + case <-p.stopSignal: + } + }) + } + // Wait for workers to all be paused + ready.Wait() +} + +// dispatch sends the next queued task to an available worker. +func (p *WorkerPool) dispatch() { + defer close(p.stoppedChan) + timeout := time.NewTimer(idleTimeout) + var workerCount int + var idle bool + +Loop: + for { + // As long as tasks are in the waiting queue, incoming tasks are put + // into the waiting queue and tasks to run are taken from the waiting + // queue. Once the waiting queue is empty, then go back to submitting + // incoming tasks directly to available workers. + if p.waitingQueue.Len() != 0 { + if !p.processWaitingQueue() { + break Loop + } + continue + } + + select { + case task, ok := <-p.taskQueue: + if !ok { + break Loop + } + // Got a task to do. + select { + case p.workerQueue <- task: + default: + // Create a new worker, if not at max. + if workerCount < p.maxWorkers { + go startWorker(task, p.workerQueue) + workerCount++ + } else { + // Enqueue task to be executed by next available worker. + p.waitingQueue.PushBack(task) + atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len())) + } + } + idle = false + case <-timeout.C: + // Timed out waiting for work to arrive. Kill a ready worker if + // pool has been idle for a whole timeout. + if idle && workerCount > 0 { + if p.killIdleWorker() { + workerCount-- + } + } + idle = true + timeout.Reset(idleTimeout) + } + } + + // If instructed to wait, then run tasks that are already queued. + if p.wait { + p.runQueuedTasks() + } + + // Stop all remaining workers as they become ready. + for workerCount > 0 { + p.workerQueue <- nil + workerCount-- + } + + timeout.Stop() +} + +// startWorker runs initial task, then starts a worker waiting for more. +func startWorker(task func(), workerQueue chan func()) { + task() + go worker(workerQueue) +} + +// worker executes tasks and stops when it receives a nil task. +func worker(workerQueue chan func()) { + for task := range workerQueue { + if task == nil { + return + } + task() + } +} + +// stop tells the dispatcher to exit, and whether or not to complete queued +// tasks. +func (p *WorkerPool) stop(wait bool) { + p.stopOnce.Do(func() { + // Signal that workerpool is stopping, to unpause any paused workers. + close(p.stopSignal) + // Acquire stopLock to wait for any pause in progress to complete. All + // in-progress pauses will complete because the stopSignal unpauses the + // workers. + p.stopLock.Lock() + // The stopped flag prevents any additional paused workers. This makes + // it safe to close the taskQueue. + p.stopped = true + p.stopLock.Unlock() + p.wait = wait + // Close task queue and wait for currently running tasks to finish. + close(p.taskQueue) + }) + <-p.stoppedChan +} + +// processWaitingQueue puts new tasks onto the the waiting queue, and removes +// tasks from the waiting queue as workers become available. Returns false if +// worker pool is stopped. +func (p *WorkerPool) processWaitingQueue() bool { + select { + case task, ok := <-p.taskQueue: + if !ok { + return false + } + p.waitingQueue.PushBack(task) + case p.workerQueue <- p.waitingQueue.Front().(func()): + // A worker was ready, so gave task to worker. + p.waitingQueue.PopFront() + } + atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len())) + return true +} + +func (p *WorkerPool) killIdleWorker() bool { + select { + case p.workerQueue <- nil: + // Sent kill signal to worker. + return true + default: + // No ready workers. All, if any, workers are busy. + return false + } +} + +// runQueuedTasks removes each task from the waiting queue and gives it to +// workers until queue is empty. +func (p *WorkerPool) runQueuedTasks() { + for p.waitingQueue.Len() != 0 { + // A worker is ready, so give task to worker. + p.workerQueue <- p.waitingQueue.PopFront().(func()) + atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len())) + } +} diff --git a/vendor/github.com/google/uuid/go.mod b/vendor/github.com/google/uuid/go.mod new file mode 100644 index 0000000..fc84cd7 --- /dev/null +++ b/vendor/github.com/google/uuid/go.mod @@ -0,0 +1 @@ +module github.com/google/uuid diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go index 7f3643f..524404c 100644 --- a/vendor/github.com/google/uuid/uuid.go +++ b/vendor/github.com/google/uuid/uuid.go @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All rights reserved. +// Copyright 2018 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -35,20 +35,43 @@ const ( var rander = rand.Reader // random function -// Parse decodes s into a UUID or returns an error. Both the UUID form of -// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. +// Parse decodes s into a UUID or returns an error. Both the standard UUID +// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the +// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex +// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. func Parse(s string) (UUID, error) { var uuid UUID - if len(s) != 36 { - if len(s) != 36+9 { - return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) - } + switch len(s) { + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36: + + // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: if strings.ToLower(s[:9]) != "urn:uuid:" { return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) } s = s[9:] + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + case 36 + 2: + s = s[1:] + + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + case 32: + var ok bool + for i := range uuid { + uuid[i], ok = xtob(s[i*2], s[i*2+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return uuid, errors.New("invalid UUID format") } @@ -70,15 +93,29 @@ func Parse(s string) (UUID, error) { // ParseBytes is like Parse, except it parses a byte slice instead of a string. func ParseBytes(b []byte) (UUID, error) { var uuid UUID - if len(b) != 36 { - if len(b) != 36+9 { - return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) - } + switch len(b) { + case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) } b = b[9:] + case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + b = b[1:] + case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + var ok bool + for i := 0; i < 32; i += 2 { + uuid[i/2], ok = xtob(b[i], b[i+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { return uuid, errors.New("invalid UUID format") } @@ -97,6 +134,16 @@ func ParseBytes(b []byte) (UUID, error) { return uuid, nil } +// MustParse is like Parse but panics if the string cannot be parsed. +// It simplifies safe initialization of global variables holding compiled UUIDs. +func MustParse(s string) UUID { + uuid, err := Parse(s) + if err != nil { + panic(`uuid: Parse(` + s + `): ` + err.Error()) + } + return uuid +} + // FromBytes creates a new UUID from a byte slice. Returns an error if the slice // does not have a length of 16. The bytes are copied from the slice. func FromBytes(b []byte) (uuid UUID, err error) { @@ -130,7 +177,7 @@ func (uuid UUID) URN() string { } func encodeHex(dst []byte, uuid UUID) { - hex.Encode(dst[:], uuid[:4]) + hex.Encode(dst, uuid[:4]) dst[8] = '-' hex.Encode(dst[9:13], uuid[4:6]) dst[13] = '-' diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go index c9ba647..782a34b 100644 --- a/vendor/github.com/gorilla/mux/mux.go +++ b/vendor/github.com/gorilla/mux/mux.go @@ -435,8 +435,7 @@ func Vars(r *http.Request) map[string]string { // CurrentRoute returns the matched route for the current request, if any. // This only works when called inside the handler of the matched route // because the matched route is stored in the request context which is cleared -// after the handler returns, unless the KeepContext option is set on the -// Router. +// after the handler returns. func CurrentRoute(r *http.Request) *Route { if rv := r.Context().Value(routeKey); rv != nil { return rv.(*Route) diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go index 96dd94a..0144842 100644 --- a/vendor/github.com/gorilla/mux/regexp.go +++ b/vendor/github.com/gorilla/mux/regexp.go @@ -325,6 +325,12 @@ func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { // Store host variables. if v.host != nil { host := getHost(req) + if v.host.wildcardHostPort { + // Don't be strict on the port match + if i := strings.Index(host, ":"); i != -1 { + host = host[:i] + } + } matches := v.host.regexp.FindStringSubmatchIndex(host) if len(matches) > 0 { extractVars(host, matches, v.host.varsN, m.Vars) diff --git a/vendor/github.com/pivotal-cf/brokerapi/.gitignore b/vendor/github.com/pivotal-cf/brokerapi/.gitignore deleted file mode 100644 index b3b978b..0000000 --- a/vendor/github.com/pivotal-cf/brokerapi/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -.idea -.coverprofile \ No newline at end of file diff --git a/vendor/github.com/pivotal-cf/brokerapi/.travis.yml b/vendor/github.com/pivotal-cf/brokerapi/.travis.yml deleted file mode 100644 index 2c8a3a0..0000000 --- a/vendor/github.com/pivotal-cf/brokerapi/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: go - -go: - - 1.9.x - - 1.10.x - - 1.11.x - -install: - - go get -v -t ./... - - go get -v github.com/onsi/ginkgo/ginkgo - -script: ginkgo -r diff --git a/vendor/github.com/pivotal-cf/brokerapi/Gopkg.lock b/vendor/github.com/pivotal-cf/brokerapi/Gopkg.lock deleted file mode 100644 index 1886979..0000000 --- a/vendor/github.com/pivotal-cf/brokerapi/Gopkg.lock +++ /dev/null @@ -1,206 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:98a0a95c73a9b62d412530d393f5442bd2145c4bb9a100b6fcc02bbc55298e48" - name = "code.cloudfoundry.org/lager" - packages = [ - ".", - "internal/truncate", - "lagerctx", - "lagertest", - ] - pruneopts = "" - revision = "54c4f2530ddeb742ce9c9ee26f5fa758d6c1b3b6" - -[[projects]] - digest = "1:029f604c64cb13a3e2facee76fd1ae446d343df36fbb280b536bf36f9864ef1f" - name = "github.com/drewolson/testflight" - packages = ["."] - pruneopts = "" - revision = "ab2d9a74b97eda058004c8deef80ac624432f408" - version = "v1.0.0" - -[[projects]] - digest = "1:a25a2c5ae694b01713fb6cd03c3b1ac1ccc1902b9f0a922680a88ec254f968e1" - name = "github.com/google/uuid" - packages = ["."] - pruneopts = "" - revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8" - version = "v1.1.0" - -[[projects]] - digest = "1:883e2fdbdd0e577187bd8106fec775b1176059af267a7f40eba5308955c67d52" - name = "github.com/gorilla/mux" - packages = ["."] - pruneopts = "" - revision = "00bdffe0f3c77e27d2cf6f5c70232a2d3e4d9c15" - version = "v1.7.3" - -[[projects]] - digest = "1:b3c5b95e56c06f5aa72cb2500e6ee5f44fcd122872d4fec2023a488e561218bc" - name = "github.com/hpcloud/tail" - packages = [ - ".", - "ratelimiter", - "util", - "watch", - "winfile", - ] - pruneopts = "" - revision = "a30252cb686a21eb2d0b98132633053ec2f7f1e5" - version = "v1.0.0" - -[[projects]] - digest = "1:9e74f6fbb11a09c042218844e2bba95a47cc468c741b80d7c2ffb4067c32b4cc" - name = "github.com/onsi/ginkgo" - packages = [ - ".", - "config", - "extensions/table", - "internal/codelocation", - "internal/containernode", - "internal/failer", - "internal/leafnodes", - "internal/remote", - "internal/spec", - "internal/spec_iterator", - "internal/specrunner", - "internal/suite", - "internal/testingtproxy", - "internal/writer", - "reporters", - "reporters/stenographer", - "reporters/stenographer/support/go-colorable", - "reporters/stenographer/support/go-isatty", - "types", - ] - pruneopts = "" - revision = "72b6ab036f78c4bf1a9748c3941dd7c3de54917a" - version = "v1.10.2" - -[[projects]] - digest = "1:f201278781bbc4a1c20265278c28b80712ef90123563cc0a391d836fc49e0da5" - name = "github.com/onsi/gomega" - packages = [ - ".", - "format", - "gbytes", - "internal/assertion", - "internal/asyncassertion", - "internal/oraclematcher", - "internal/testingtsupport", - "matchers", - "matchers/support/goraph/bipartitegraph", - "matchers/support/goraph/edge", - "matchers/support/goraph/node", - "matchers/support/goraph/util", - "types", - ] - pruneopts = "" - revision = "bdebf9e0ece900259084cfa4121b97ce1a540939" - version = "v1.7.0" - -[[projects]] - digest = "1:a5484d4fa43127138ae6e7b2299a6a52ae006c7f803d98d717f60abf3e97192e" - name = "github.com/pborman/uuid" - packages = ["."] - pruneopts = "" - revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1" - version = "v1.2" - -[[projects]] - digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "" - revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" - version = "v0.8.1" - -[[projects]] - branch = "master" - digest = "1:b4ba046df563f56fe42b6270b20039107a37e1ab47c97aa47a16f848aa5b6d9a" - name = "golang.org/x/net" - packages = [ - "html", - "html/atom", - "html/charset", - ] - pruneopts = "" - revision = "cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb" - -[[projects]] - digest = "1:1ed067f6338b4c6496a27a889a53e5c1adcafdcc4442024a01e035e5dcf28698" - name = "golang.org/x/sys" - packages = ["unix"] - pruneopts = "" - revision = "a408501be4d17ee978c04a618e7a1b22af058c0e" - -[[projects]] - digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" - name = "golang.org/x/text" - packages = [ - "encoding", - "encoding/charmap", - "encoding/htmlindex", - "encoding/internal", - "encoding/internal/identifier", - "encoding/japanese", - "encoding/korean", - "encoding/simplifiedchinese", - "encoding/traditionalchinese", - "encoding/unicode", - "internal/gen", - "internal/tag", - "internal/utf8internal", - "language", - "runes", - "transform", - "unicode/cldr", - ] - pruneopts = "" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" - name = "gopkg.in/fsnotify.v1" - packages = ["."] - pruneopts = "" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - source = "gopkg.in/fsnotify/fsnotify.v1" - version = "v1.4.7" - -[[projects]] - branch = "v1" - digest = "1:a96d16bd088460f2e0685d46c39bcf1208ba46e0a977be2df49864ec7da447dd" - name = "gopkg.in/tomb.v1" - packages = ["."] - pruneopts = "" - revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" - -[[projects]] - digest = "1:59925e8b791ee90b60e4dc05099d34fd38d0922a2a8f9b4e700cb61fc421e6b2" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "" - revision = "e4d366fc3c7938e2958e662b4258c7a89e1f0e3e" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "code.cloudfoundry.org/lager", - "code.cloudfoundry.org/lager/lagertest", - "github.com/drewolson/testflight", - "github.com/gorilla/mux", - "github.com/onsi/ginkgo", - "github.com/onsi/ginkgo/extensions/table", - "github.com/onsi/gomega", - "github.com/onsi/gomega/gbytes", - "github.com/pborman/uuid", - "github.com/pkg/errors", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/pivotal-cf/brokerapi/Gopkg.toml b/vendor/github.com/pivotal-cf/brokerapi/Gopkg.toml deleted file mode 100644 index c70c34f..0000000 --- a/vendor/github.com/pivotal-cf/brokerapi/Gopkg.toml +++ /dev/null @@ -1,54 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - name = "code.cloudfoundry.org/lager" - branch = "master" - -[[constraint]] - name = "github.com/drewolson/testflight" - version = "1.0.0" - -[[constraint]] - name = "github.com/gorilla/mux" - version = "^1.6.1" - -[[constraint]] - name = "github.com/onsi/ginkgo" - version = "^1.10.2" - -[[constraint]] - name = "github.com/onsi/gomega" - version = "^1.6.0" - -[[constraint]] - version = "^1.2.0" - name = "github.com/pborman/uuid" - -[[constraint]] - name = "github.com/pkg/errors" - version = "^0.8.0" - -[[override]] - name = "gopkg.in/fsnotify.v1" - source = "gopkg.in/fsnotify/fsnotify.v1" diff --git a/vendor/github.com/pivotal-cf/brokerapi/v7/.gitignore b/vendor/github.com/pivotal-cf/brokerapi/v7/.gitignore new file mode 100644 index 0000000..7878e9b --- /dev/null +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/.gitignore @@ -0,0 +1,3 @@ +vendor/ +.idea +*.coverprofile diff --git a/vendor/github.com/pivotal-cf/brokerapi/v7/.travis.yml b/vendor/github.com/pivotal-cf/brokerapi/v7/.travis.yml new file mode 100644 index 0000000..f9d2f7a --- /dev/null +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/.travis.yml @@ -0,0 +1,13 @@ +language: go + +go: + - 1.11.x + - 1.12.x + - 1.13.x + +env: + - GO111MODULE=on + +script: +- go run honnef.co/go/tools/cmd/staticcheck ./... +- go run github.com/onsi/ginkgo/ginkgo -r diff --git a/vendor/github.com/pivotal-cf/brokerapi/LICENSE b/vendor/github.com/pivotal-cf/brokerapi/v7/LICENSE similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/LICENSE rename to vendor/github.com/pivotal-cf/brokerapi/v7/LICENSE diff --git a/vendor/github.com/pivotal-cf/brokerapi/NOTICE b/vendor/github.com/pivotal-cf/brokerapi/v7/NOTICE similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/NOTICE rename to vendor/github.com/pivotal-cf/brokerapi/v7/NOTICE diff --git a/vendor/github.com/pivotal-cf/brokerapi/README.md b/vendor/github.com/pivotal-cf/brokerapi/v7/README.md similarity index 94% rename from vendor/github.com/pivotal-cf/brokerapi/README.md rename to vendor/github.com/pivotal-cf/brokerapi/v7/README.md index 881b398..7b07a7b 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/README.md +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/README.md @@ -11,12 +11,9 @@ Brokers. ## Dependencies -- Go 1.7+ +- Go 1.11+ - [lager](https://github.com/cloudfoundry/lager) -- [gorilla/mux v1.6.1+](https://github.com/gorilla/mux) - -We use [dep](https://github.com/golang/dep) to manager our dependencies. Use -`dep ensure` in order to download the required packages. +- [gorilla/mux](https://github.com/gorilla/mux) ## Usage diff --git a/vendor/github.com/pivotal-cf/brokerapi/api.go b/vendor/github.com/pivotal-cf/brokerapi/v7/api.go similarity index 95% rename from vendor/github.com/pivotal-cf/brokerapi/api.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/api.go index 96301ee..f0573bc 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/api.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/api.go @@ -20,9 +20,9 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/auth" - "github.com/pivotal-cf/brokerapi/handlers" - "github.com/pivotal-cf/brokerapi/middlewares" + "github.com/pivotal-cf/brokerapi/v7/auth" + "github.com/pivotal-cf/brokerapi/v7/handlers" + "github.com/pivotal-cf/brokerapi/v7/middlewares" ) type BrokerCredentials struct { diff --git a/vendor/github.com/pivotal-cf/brokerapi/auth/auth.go b/vendor/github.com/pivotal-cf/brokerapi/v7/auth/auth.go similarity index 66% rename from vendor/github.com/pivotal-cf/brokerapi/auth/auth.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/auth/auth.go index 74e7799..25229ec 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/auth/auth.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/auth/auth.go @@ -22,14 +22,26 @@ import ( ) type Wrapper struct { + credentials []Credential +} + +type Credential struct { username []byte password []byte } +func NewWrapperMultiple(users map[string]string) *Wrapper { + var cs []Credential + for k, v := range users { + u := sha256.Sum256([]byte(k)) + p := sha256.Sum256([]byte(v)) + cs = append(cs, Credential{username: u[:], password: p[:]}) + } + return &Wrapper{credentials: cs} +} + func NewWrapper(username, password string) *Wrapper { - u := sha256.Sum256([]byte(username)) - p := sha256.Sum256([]byte(password)) - return &Wrapper{username: u[:], password: p[:]} + return NewWrapperMultiple(map[string]string{username: password}) } const notAuthorized = "Not Authorized" @@ -58,9 +70,19 @@ func (wrapper *Wrapper) WrapFunc(handlerFunc http.HandlerFunc) http.HandlerFunc func authorized(wrapper *Wrapper, r *http.Request) bool { username, password, isOk := r.BasicAuth() - u := sha256.Sum256([]byte(username)) - p := sha256.Sum256([]byte(password)) - return isOk && - subtle.ConstantTimeCompare(wrapper.username, u[:]) == 1 && - subtle.ConstantTimeCompare(wrapper.password, p[:]) == 1 + if isOk { + u := sha256.Sum256([]byte(username)) + p := sha256.Sum256([]byte(password)) + for _, c := range wrapper.credentials { + if c.isAuthorized(u, p) { + return true + } + } + } + return false +} + +func (c Credential) isAuthorized(uChecksum [32]byte, pChecksum [32]byte) bool { + return subtle.ConstantTimeCompare(c.username, uChecksum[:]) == 1 && + subtle.ConstantTimeCompare(c.password, pChecksum[:]) == 1 } diff --git a/vendor/github.com/pivotal-cf/brokerapi/catalog.go b/vendor/github.com/pivotal-cf/brokerapi/v7/catalog.go similarity index 98% rename from vendor/github.com/pivotal-cf/brokerapi/catalog.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/catalog.go index b95bce7..41cf543 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/catalog.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/catalog.go @@ -18,7 +18,7 @@ package brokerapi import ( "reflect" - "github.com/pivotal-cf/brokerapi/domain" + "github.com/pivotal-cf/brokerapi/v7/domain" ) //Deprecated: Use github.com/pivotal-cf/brokerapi/domain diff --git a/vendor/github.com/pivotal-cf/brokerapi/context_utils.go b/vendor/github.com/pivotal-cf/brokerapi/v7/context_utils.go similarity index 92% rename from vendor/github.com/pivotal-cf/brokerapi/context_utils.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/context_utils.go index ad4908f..1a4d1f6 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/context_utils.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/context_utils.go @@ -3,7 +3,7 @@ package brokerapi import ( "context" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/utils" ) func AddServiceToContext(ctx context.Context, service *Service) context.Context { diff --git a/vendor/github.com/pivotal-cf/brokerapi/v7/create_version_dir.sh b/vendor/github.com/pivotal-cf/brokerapi/v7/create_version_dir.sh new file mode 100644 index 0000000..0ed55ea --- /dev/null +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/create_version_dir.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -e + +version="${1:?"Usage: create_version_dir.sh "}" + +if [[ ! "$version" =~ ^v ]]; then + version="v$version" +fi + +go_files=$(find . ! -path "*/vendor/*" ! -path "*/fakes/*" ! -path "*/tools/*" ! -path "*/v[0-9]*/*" ! -name "*_test.go" -name "*.go") +for f in $go_files ; do + mkdir -p "$version/$(dirname $f)" + cp $f $version/$(dirname $f) +done diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/apiresponses/errors.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/apiresponses/errors.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/domain/apiresponses/errors.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/apiresponses/errors.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/apiresponses/failure_responses.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/apiresponses/failure_responses.go similarity index 97% rename from vendor/github.com/pivotal-cf/brokerapi/domain/apiresponses/failure_responses.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/apiresponses/failure_responses.go index f6bf8a3..d27d216 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/domain/apiresponses/failure_responses.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/apiresponses/failure_responses.go @@ -1,7 +1,6 @@ package apiresponses import ( - "errors" "fmt" "net/http" @@ -64,7 +63,7 @@ func (f *FailureResponse) LoggerAction() string { // AppendErrorMessage returns an error with the message updated. All other properties are preserved. func (f *FailureResponse) AppendErrorMessage(msg string) *FailureResponse { return &FailureResponse{ - error: errors.New(fmt.Sprintf("%s %s", f.Error(), msg)), + error: fmt.Errorf("%s %s", f.Error(), msg), statusCode: f.statusCode, loggerAction: f.loggerAction, emptyResponse: f.emptyResponse, diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/apiresponses/responses.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/apiresponses/responses.go similarity index 89% rename from vendor/github.com/pivotal-cf/brokerapi/domain/apiresponses/responses.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/apiresponses/responses.go index a1ff3f5..3b4655c 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/domain/apiresponses/responses.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/apiresponses/responses.go @@ -15,7 +15,7 @@ package apiresponses -import "github.com/pivotal-cf/brokerapi/domain" +import "github.com/pivotal-cf/brokerapi/v7/domain" type EmptyResponse struct{} @@ -59,10 +59,11 @@ type AsyncBindResponse struct { } type BindingResponse struct { - Credentials interface{} `json:"credentials"` + Credentials interface{} `json:"credentials,omitempty"` SyslogDrainURL string `json:"syslog_drain_url,omitempty"` RouteServiceURL string `json:"route_service_url,omitempty"` VolumeMounts []domain.VolumeMount `json:"volume_mounts,omitempty"` + BackupAgentURL string `json:"backup_agent_url,omitempty"` } type GetBindingResponse struct { @@ -75,8 +76,9 @@ type UnbindResponse struct { } type ExperimentalVolumeMountBindingResponse struct { - Credentials interface{} `json:"credentials"` + Credentials interface{} `json:"credentials,omitempty"` SyslogDrainURL string `json:"syslog_drain_url,omitempty"` RouteServiceURL string `json:"route_service_url,omitempty"` VolumeMounts []domain.ExperimentalVolumeMount `json:"volume_mounts,omitempty"` + BackupAgentURL string `json:"backup_agent_url,omitempty"` } diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/experimental_volume_mount.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/experimental_volume_mount.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/domain/experimental_volume_mount.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/experimental_volume_mount.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/maintenance_info.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/maintenance_info.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/domain/maintenance_info.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/maintenance_info.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/service_broker.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_broker.go similarity index 91% rename from vendor/github.com/pivotal-cf/brokerapi/domain/service_broker.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_broker.go index d8ea463..9aff08e 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/domain/service_broker.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_broker.go @@ -5,7 +5,7 @@ import ( "encoding/json" ) -//go:generate counterfeiter -o ../fakes/auto_fake_service_broker.go -fake-name AutoFakeServiceBroker . ServiceBroker +//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o ../fakes/auto_fake_service_broker.go -fake-name AutoFakeServiceBroker . ServiceBroker //Each method of the ServiceBroker interface maps to an individual endpoint of the Open Service Broker API. //The specification is available here: https://github.com/openservicebrokerapi/servicebroker/blob/v2.14/spec.md @@ -124,10 +124,11 @@ type UpdateDetails struct { } type PreviousValues struct { - PlanID string `json:"plan_id"` - ServiceID string `json:"service_id"` - OrgID string `json:"organization_id"` - SpaceID string `json:"space_id"` + PlanID string `json:"plan_id"` + ServiceID string `json:"service_id"` + OrgID string `json:"organization_id"` + SpaceID string `json:"space_id"` + MaintenanceInfo *MaintenanceInfo `json:"maintenance_info,omitempty"` } type UpdateServiceSpec struct { @@ -156,6 +157,7 @@ type BindResource struct { SpaceGuid string `json:"space_guid,omitempty"` Route string `json:"route,omitempty"` CredentialClientID string `json:"credential_client_id,omitempty"` + BackupAgent bool `json:"backup_agent,omitempty"` } type UnbindDetails struct { @@ -175,6 +177,7 @@ type Binding struct { Credentials interface{} `json:"credentials"` SyslogDrainURL string `json:"syslog_drain_url"` RouteServiceURL string `json:"route_service_url"` + BackupAgentURL string `json:"backup_agent_url,omitempty"` VolumeMounts []VolumeMount `json:"volume_mounts"` } @@ -202,6 +205,10 @@ func (d BindDetails) GetRawParameters() json.RawMessage { return d.RawParameters } +func (d UpdateDetails) GetRawContext() json.RawMessage { + return d.RawContext +} + func (d UpdateDetails) GetRawParameters() json.RawMessage { return d.RawParameters } diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/service_catalog.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_catalog.go similarity index 74% rename from vendor/github.com/pivotal-cf/brokerapi/domain/service_catalog.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_catalog.go index ad048f5..036d049 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/domain/service_catalog.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_catalog.go @@ -24,14 +24,15 @@ type Service struct { } type ServicePlan struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Free *bool `json:"free,omitempty"` - Bindable *bool `json:"bindable,omitempty"` - Metadata *ServicePlanMetadata `json:"metadata,omitempty"` - Schemas *ServiceSchemas `json:"schemas,omitempty"` - MaintenanceInfo *MaintenanceInfo `json:"maintenance_info,omitempty"` + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Free *bool `json:"free,omitempty"` + Bindable *bool `json:"bindable,omitempty"` + Metadata *ServicePlanMetadata `json:"metadata,omitempty"` + Schemas *ServiceSchemas `json:"schemas,omitempty"` + MaximumPollingDuration *int `json:"maximum_polling_duration,omitempty"` + MaintenanceInfo *MaintenanceInfo `json:"maintenance_info,omitempty"` } type ServiceSchemas struct { diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/service_metadata.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_metadata.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/domain/service_metadata.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_metadata.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/domain/service_plan_metadata.go b/vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_plan_metadata.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/domain/service_plan_metadata.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/domain/service_plan_metadata.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/failure_response.go b/vendor/github.com/pivotal-cf/brokerapi/v7/failure_response.go similarity index 97% rename from vendor/github.com/pivotal-cf/brokerapi/failure_response.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/failure_response.go index 7463c4b..ab7c85f 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/failure_response.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/failure_response.go @@ -7,7 +7,7 @@ package brokerapi import ( - "github.com/pivotal-cf/brokerapi/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" ) //Deprecated: Use github.com/pivotal-cf/brokerapi/domain/apiresponses diff --git a/vendor/github.com/pivotal-cf/brokerapi/v7/go.mod b/vendor/github.com/pivotal-cf/brokerapi/v7/go.mod new file mode 100644 index 0000000..2d9fb72 --- /dev/null +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/go.mod @@ -0,0 +1,14 @@ +module github.com/pivotal-cf/brokerapi/v7 + +require ( + code.cloudfoundry.org/lager v1.1.1-0.20191008172124-a9afc05ee5be + github.com/drewolson/testflight v1.0.0 + github.com/google/uuid v1.1.0 // indirect + github.com/gorilla/mux v1.8.0 + github.com/maxbrunsfeld/counterfeiter/v6 v6.2.3 + github.com/onsi/ginkgo v1.14.1 + github.com/onsi/gomega v1.10.2 + github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 + github.com/pkg/errors v0.9.1 + honnef.co/go/tools v0.0.1-2020.1.5 +) diff --git a/vendor/github.com/pivotal-cf/brokerapi/v7/go.sum b/vendor/github.com/pivotal-cf/brokerapi/v7/go.sum new file mode 100644 index 0000000..227865c --- /dev/null +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/go.sum @@ -0,0 +1,151 @@ +code.cloudfoundry.org/lager v1.1.1-0.20191008172124-a9afc05ee5be h1:rnGRgbKlOPKbI9N/PscJ78Ug5Iw+o1kE7aDW01V+0FM= +code.cloudfoundry.org/lager v1.1.1-0.20191008172124-a9afc05ee5be/go.mod h1:O2sS7gKP3HM2iemG+EnwvyNQK7pTSC6Foi4QiMp9sSk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40 h1:y4B3+GPxKlrigF1ha5FFErxK+sr6sWxQovRMzwMhejo= +github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= +github.com/drewolson/testflight v1.0.0 h1:jgA0pHcFIPnXoBmyFzrdoR2ka4UvReMDsjYc7Jcvl80= +github.com/drewolson/testflight v1.0.0/go.mod h1:t9oKuuEohRGLb80SWX+uxJHuhX98B7HnojqtW+Ryq30= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/joefitzgerald/rainbow-reporter v0.1.0 h1:AuMG652zjdzI0YCCnXAqATtRBpGXMcAnrajcaTrSeuo= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.0.0-20160823170715-cfb55aafdaf3 h1:dhwb1Ev84SKKVBfLuhR4bw/29yYHzwtTyTLUWWnvYxI= +github.com/kr/pretty v0.0.0-20160823170715-cfb55aafdaf3/go.mod h1:Bvhd+E3laJ0AVkG0c9rmtZcnhV0HQ3+c3YxxqTvc/gA= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.0.0-20160504234017-7cafcd837844 h1:kpzneEBeC0dMewP3gr/fADv1OlblH9r1goWVwpOt3TU= +github.com/kr/text v0.0.0-20160504234017-7cafcd837844/go.mod h1:sjUstKUATFIcff4qlB53Kml0wQPtJVc/3fWrmuUmcfA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2 h1:g+4J5sZg6osfvEfkRZxJ1em0VT95/UOZgi/l7zi1/oE= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.3 h1:z1lXirM9f9WTcdmzSZahKh/t+LCqPiiwK2/DB1kLlI4= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.3/go.mod h1:1ftk08SazyElaaNvmqAfZWGwJzshjCfBXDLoQtPAMNk= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= +github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE= +github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sclevine/spec v1.2.0 h1:1Jwdf9jSfDl9NVmt8ndHqbTZ7XCCPbh1jI3hkDBHVYA= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= +github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db h1:9hRk1xeL9LTT3yX/941DqeBz87XgHAQuj+TbimYJuiw= +golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200301222351-066e0c02454c h1:FD7jysxM+EJqg5UYYy3XYDsAiUickFsn4UiaanJkf8c= +golang.org/x/tools v0.0.0-20200301222351-066e0c02454c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k= +honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/api_handler.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/api_handler.go similarity index 97% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/api_handler.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/api_handler.go index ea7d093..91a011a 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/api_handler.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/api_handler.go @@ -7,7 +7,7 @@ import ( "net/http" "code.cloudfoundry.org/lager" - "github.com/pivotal-cf/brokerapi/domain" + "github.com/pivotal-cf/brokerapi/v7/domain" ) const ( diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/bind.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/bind.go similarity index 92% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/bind.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/bind.go index 4f7ff40..f6f4aea 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/bind.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/bind.go @@ -6,10 +6,10 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const ( @@ -88,6 +88,7 @@ func (h APIHandler) Bind(w http.ResponseWriter, req *http.Request) { SyslogDrainURL: binding.SyslogDrainURL, RouteServiceURL: binding.RouteServiceURL, VolumeMounts: binding.VolumeMounts, + BackupAgentURL: binding.BackupAgentURL, }) return } @@ -126,6 +127,7 @@ func (h APIHandler) Bind(w http.ResponseWriter, req *http.Request) { RouteServiceURL: binding.RouteServiceURL, SyslogDrainURL: binding.SyslogDrainURL, VolumeMounts: experimentalVols, + BackupAgentURL: binding.BackupAgentURL, } h.respond(w, http.StatusCreated, experimentalBinding) return @@ -136,5 +138,6 @@ func (h APIHandler) Bind(w http.ResponseWriter, req *http.Request) { SyslogDrainURL: binding.SyslogDrainURL, RouteServiceURL: binding.RouteServiceURL, VolumeMounts: binding.VolumeMounts, + BackupAgentURL: binding.BackupAgentURL, }) } diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/catalog.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/catalog.go similarity index 87% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/catalog.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/catalog.go index dacfd17..1383aaa 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/catalog.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/catalog.go @@ -3,7 +3,7 @@ package handlers import ( "net/http" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" ) func (h *APIHandler) Catalog(w http.ResponseWriter, req *http.Request) { diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/deprovision.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/deprovision.go similarity index 89% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/deprovision.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/deprovision.go index e8612a2..739bd51 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/deprovision.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/deprovision.go @@ -5,10 +5,10 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const deprovisionLogKey = "deprovision" diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/get_binding.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/get_binding.go similarity index 91% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/get_binding.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/get_binding.go index 966bf20..f53e0ff 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/get_binding.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/get_binding.go @@ -6,9 +6,9 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const getBindLogKey = "getBinding" diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/get_instance.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/get_instance.go similarity index 90% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/get_instance.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/get_instance.go index f9ea3a4..93a4680 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/get_instance.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/get_instance.go @@ -6,9 +6,9 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const getInstanceLogKey = "getInstance" diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/last_binding_operation.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/last_binding_operation.go similarity index 90% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/last_binding_operation.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/last_binding_operation.go index f9bbecb..8ac150c 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/last_binding_operation.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/last_binding_operation.go @@ -6,10 +6,10 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const lastBindingOperationLogKey = "lastBindingOperation" diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/last_operation.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/last_operation.go similarity index 87% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/last_operation.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/last_operation.go index d185d7c..9aecd7d 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/last_operation.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/last_operation.go @@ -5,10 +5,10 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const lastOperationLogKey = "lastOperation" diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/provision.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/provision.go similarity index 94% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/provision.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/provision.go index 859a641..1d9677d 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/provision.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/provision.go @@ -6,10 +6,10 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const ( diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/unbind.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/unbind.go similarity index 91% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/unbind.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/unbind.go index ba19faa..4126b2d 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/unbind.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/unbind.go @@ -6,10 +6,10 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const unbindLogKey = "unbind" diff --git a/vendor/github.com/pivotal-cf/brokerapi/handlers/update.go b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/update.go similarity index 89% rename from vendor/github.com/pivotal-cf/brokerapi/handlers/update.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/handlers/update.go index bebd212..d8a7a95 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/handlers/update.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/handlers/update.go @@ -7,10 +7,10 @@ import ( "code.cloudfoundry.org/lager" "github.com/gorilla/mux" - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" - "github.com/pivotal-cf/brokerapi/middlewares" - "github.com/pivotal-cf/brokerapi/utils" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/middlewares" + "github.com/pivotal-cf/brokerapi/v7/utils" ) const updateLogKey = "update" diff --git a/vendor/github.com/pivotal-cf/brokerapi/maintenance_info.go b/vendor/github.com/pivotal-cf/brokerapi/v7/maintenance_info.go similarity index 74% rename from vendor/github.com/pivotal-cf/brokerapi/maintenance_info.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/maintenance_info.go index 5cb1466..3abe4d4 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/maintenance_info.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/maintenance_info.go @@ -1,7 +1,7 @@ package brokerapi import ( - "github.com/pivotal-cf/brokerapi/domain" + "github.com/pivotal-cf/brokerapi/v7/domain" ) //Deprecated: Use github.com/pivotal-cf/brokerapi/domain diff --git a/vendor/github.com/pivotal-cf/brokerapi/middlewares/api_version_header.go b/vendor/github.com/pivotal-cf/brokerapi/v7/middlewares/api_version_header.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/middlewares/api_version_header.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/middlewares/api_version_header.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/middlewares/correlation_id_header.go b/vendor/github.com/pivotal-cf/brokerapi/v7/middlewares/correlation_id_header.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/middlewares/correlation_id_header.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/middlewares/correlation_id_header.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/middlewares/info_location_header.go b/vendor/github.com/pivotal-cf/brokerapi/v7/middlewares/info_location_header.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/middlewares/info_location_header.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/middlewares/info_location_header.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/middlewares/originating_identity_header.go b/vendor/github.com/pivotal-cf/brokerapi/v7/middlewares/originating_identity_header.go similarity index 100% rename from vendor/github.com/pivotal-cf/brokerapi/middlewares/originating_identity_header.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/middlewares/originating_identity_header.go diff --git a/vendor/github.com/pivotal-cf/brokerapi/response.go b/vendor/github.com/pivotal-cf/brokerapi/v7/response.go similarity index 96% rename from vendor/github.com/pivotal-cf/brokerapi/response.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/response.go index cc1a67c..4313dae 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/response.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/response.go @@ -16,8 +16,8 @@ package brokerapi import ( - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" ) //Deprecated: Use github.com/pivotal-cf/brokerapi/domain/apiresponses diff --git a/vendor/github.com/pivotal-cf/brokerapi/service_broker.go b/vendor/github.com/pivotal-cf/brokerapi/v7/service_broker.go similarity index 94% rename from vendor/github.com/pivotal-cf/brokerapi/service_broker.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/service_broker.go index 230bf49..34de1e5 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/service_broker.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/service_broker.go @@ -16,11 +16,11 @@ package brokerapi import ( - "github.com/pivotal-cf/brokerapi/domain" - "github.com/pivotal-cf/brokerapi/domain/apiresponses" + "github.com/pivotal-cf/brokerapi/v7/domain" + "github.com/pivotal-cf/brokerapi/v7/domain/apiresponses" ) -//go:generate counterfeiter -o fakes/auto_fake_service_broker.go -fake-name AutoFakeServiceBroker . ServiceBroker +//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o fakes/auto_fake_service_broker.go -fake-name AutoFakeServiceBroker . ServiceBroker //Deprecated: Use github.com/pivotal-cf/brokerapi/domain //Each method of the ServiceBroker interface maps to an individual endpoint of the Open Service Broker API. diff --git a/vendor/github.com/pivotal-cf/brokerapi/v7/staticcheck.conf b/vendor/github.com/pivotal-cf/brokerapi/v7/staticcheck.conf new file mode 100644 index 0000000..7feb2a2 --- /dev/null +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/staticcheck.conf @@ -0,0 +1,4 @@ +# When adding staticcheck, we thought it was better to get it working with some checks disabled +# rather than fixing all the problems in one go. Some problems cannot be fixed without making +# breaking changes. +checks = ["all", "-SA1019", "-ST1000", "-ST1003", "-ST1005", "-ST1012", "-ST1021", "-SA1029", "-ST1020"] diff --git a/vendor/github.com/pivotal-cf/brokerapi/utils/context.go b/vendor/github.com/pivotal-cf/brokerapi/v7/utils/context.go similarity index 96% rename from vendor/github.com/pivotal-cf/brokerapi/utils/context.go rename to vendor/github.com/pivotal-cf/brokerapi/v7/utils/context.go index 78fabb7..34e4ce8 100644 --- a/vendor/github.com/pivotal-cf/brokerapi/utils/context.go +++ b/vendor/github.com/pivotal-cf/brokerapi/v7/utils/context.go @@ -4,7 +4,7 @@ import ( "context" "code.cloudfoundry.org/lager" - "github.com/pivotal-cf/brokerapi/domain" + "github.com/pivotal-cf/brokerapi/v7/domain" ) type contextKey string diff --git a/vendor/modules.txt b/vendor/modules.txt index f7ff81a..793b4e6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -44,6 +44,8 @@ code.cloudfoundry.org/cli/version code.cloudfoundry.org/lager # code.cloudfoundry.org/tlsconfig v0.0.0-20200131000646-bbe0f8da39b3 code.cloudfoundry.org/tlsconfig +# github.com/Masterminds/semver v1.4.2 +github.com/Masterminds/semver # github.com/SermoDigital/jose v0.9.1 => github.com/SermoDigital/jose v0.9.2-0.20161205224733-f6df55f235c2 github.com/SermoDigital/jose github.com/SermoDigital/jose/crypto @@ -60,6 +62,9 @@ github.com/charlievieth/fs # github.com/cloudfoundry-community/go-cf-clients-helper v1.0.1 ## explicit github.com/cloudfoundry-community/go-cf-clients-helper +# github.com/cloudfoundry-community/go-cfclient v0.0.0-20220930021109-9c4e6c59ccf1 +## explicit +github.com/cloudfoundry-community/go-cfclient # github.com/cloudfoundry-community/go-uaa v0.3.1 ## explicit github.com/cloudfoundry-community/go-uaa @@ -79,21 +84,26 @@ github.com/cloudfoundry/noaa/errors github.com/cloudfoundry/sonde-go/events # github.com/cppforlife/go-patch v0.2.0 github.com/cppforlife/go-patch/patch -# github.com/drewolson/testflight v1.0.0 +# github.com/ess/debuggable v1.0.0 +github.com/ess/debuggable +# github.com/ess/hype v1.1.5 +## explicit +github.com/ess/hype +# github.com/gammazero/deque v0.1.0 +github.com/gammazero/deque +# github.com/gammazero/workerpool v1.1.2 ## explicit +github.com/gammazero/workerpool # github.com/gogo/protobuf v1.3.1 github.com/gogo/protobuf/gogoproto github.com/gogo/protobuf/proto github.com/gogo/protobuf/protoc-gen-gogo/descriptor # github.com/golang/protobuf v1.5.2 -## explicit github.com/golang/protobuf/proto -# github.com/google/go-cmp v0.5.8 +# github.com/google/uuid v1.1.0 ## explicit -# github.com/google/uuid v1.0.0 github.com/google/uuid -# github.com/gorilla/mux v1.7.4 -## explicit +# github.com/gorilla/mux v1.8.0 github.com/gorilla/mux # github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket @@ -111,15 +121,15 @@ github.com/mattn/go-runewidth # github.com/pborman/uuid v1.2.0 ## explicit github.com/pborman/uuid -# github.com/pivotal-cf/brokerapi v6.4.2+incompatible -## explicit -github.com/pivotal-cf/brokerapi -github.com/pivotal-cf/brokerapi/auth -github.com/pivotal-cf/brokerapi/domain -github.com/pivotal-cf/brokerapi/domain/apiresponses -github.com/pivotal-cf/brokerapi/handlers -github.com/pivotal-cf/brokerapi/middlewares -github.com/pivotal-cf/brokerapi/utils +# github.com/pivotal-cf/brokerapi/v7 v7.4.0 +## explicit +github.com/pivotal-cf/brokerapi/v7 +github.com/pivotal-cf/brokerapi/v7/auth +github.com/pivotal-cf/brokerapi/v7/domain +github.com/pivotal-cf/brokerapi/v7/domain/apiresponses +github.com/pivotal-cf/brokerapi/v7/handlers +github.com/pivotal-cf/brokerapi/v7/middlewares +github.com/pivotal-cf/brokerapi/v7/utils # github.com/pkg/errors v0.9.1 github.com/pkg/errors # github.com/sirupsen/logrus v1.6.0 @@ -135,7 +145,6 @@ github.com/tedsuo/rata ## explicit golang.org/x/crypto/ssh/terminal # golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 -## explicit golang.org/x/net/context golang.org/x/net/context/ctxhttp # golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 @@ -158,7 +167,6 @@ google.golang.org/appengine/internal/remote_api google.golang.org/appengine/internal/urlfetch google.golang.org/appengine/urlfetch # google.golang.org/protobuf v1.28.0 -## explicit google.golang.org/protobuf/encoding/prototext google.golang.org/protobuf/encoding/protowire google.golang.org/protobuf/internal/descfmt