From 8904e44f785a204fbc73550aa448d27141bc7bd5 Mon Sep 17 00:00:00 2001 From: Silvestre Zabala Date: Wed, 30 Oct 2019 15:18:09 +0100 Subject: [PATCH] Split tests of current and legacy components * Run the split tests in separate travis jobs * Cache maven artifacts * Update Linux and Go versions to current --- .travis.yml | 89 +- src/autoscaler/db/sqldb/binding_sqldb_test.go | 19 +- src/integration/helpers.go | 18 + src/integration_legacy/components.go | 889 +++++++++++++++ .../fakeInvalidDataPolicy.json | 0 src/integration_legacy/fakeInvalidPolicy.json | 4 + .../fakeMinimalScalingRulePolicy.json | 70 ++ .../fakePolicyWithSchedule.json | 91 ++ .../fakePolicyWithScheduleAnother.json | 73 ++ .../fakePolicyWithoutSchedule.json | 22 + src/integration_legacy/helpers.go | 185 +++ ...ation_api_broker_graceful_shutdown_test.go | 4 +- .../integration_api_eventgenerator_test.go | 4 +- .../integration_api_metricscollector_test.go | 4 +- .../integration_api_scalingengine_test.go | 4 +- .../integration_api_scheduler_test.go | 4 +- .../integration_broker_api_test.go | 4 +- ...gration_golangapi_metricscollector_test.go | 4 +- .../integration_suite_test.go | 1004 +++++++++++++++++ 19 files changed, 2433 insertions(+), 59 deletions(-) create mode 100644 src/integration_legacy/components.go rename src/{integration => integration_legacy}/fakeInvalidDataPolicy.json (100%) create mode 100644 src/integration_legacy/fakeInvalidPolicy.json create mode 100644 src/integration_legacy/fakeMinimalScalingRulePolicy.json create mode 100644 src/integration_legacy/fakePolicyWithSchedule.json create mode 100644 src/integration_legacy/fakePolicyWithScheduleAnother.json create mode 100644 src/integration_legacy/fakePolicyWithoutSchedule.json create mode 100644 src/integration_legacy/helpers.go rename src/{integration => integration_legacy}/integration_api_broker_graceful_shutdown_test.go (98%) rename src/{integration => integration_legacy}/integration_api_eventgenerator_test.go (99%) rename src/{integration => integration_legacy}/integration_api_metricscollector_test.go (99%) rename src/{integration => integration_legacy}/integration_api_scalingengine_test.go (99%) rename src/{integration => integration_legacy}/integration_api_scheduler_test.go (99%) rename src/{integration => integration_legacy}/integration_broker_api_test.go (99%) rename src/{integration => integration_legacy}/integration_golangapi_metricscollector_test.go (99%) create mode 100644 src/integration_legacy/integration_suite_test.go diff --git a/.travis.yml b/.travis.yml index 81b9f4ae0..1798552cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,18 @@ - - -dist: trusty -sudo: required +os: linux +dist: xenial env: global: - DBURL=postgres://postgres@localhost/autoscaler?sslmode=disable - NODE_VERSION=6.2 - - GO_VERSION=1.11 + - GO_VERSION=1.13.3 - LOGLEVEL=info language: java +cache: + directories: + - $HOME/.m2 jdk: -- openjdk8 + - openjdk8 services: - postgresql addons: @@ -37,36 +38,50 @@ before_script: - java -cp 'db/target/lib/*' liquibase.integration.commandline.Main --url jdbc:postgresql://127.0.0.1/autoscaler --driver=org.postgresql.Driver --changeLogFile=src/autoscaler/scalingengine/db/scalingengine.db.changelog.yml update - java -cp 'db/target/lib/*' liquibase.integration.commandline.Main --url jdbc:postgresql://127.0.0.1/autoscaler --driver=org.postgresql.Driver --changeLogFile=src/autoscaler/operator/db/operator.db.changelog.yml update -matrix: +jobs: include: - - name: unit test - script: - # Unit test - - pushd api - - npm install - - npm test - - popd - - pushd servicebroker - - npm install - - npm test - - popd - - pushd src/autoscaler - - ginkgo -r -race -randomizeAllSpecs - - popd - - pushd scheduler - - mvn test - - popd + - name: unit test + script: + # Unit test + - pushd src/autoscaler + - ginkgo -r -race -randomizeAllSpecs + - popd + - pushd scheduler + - mvn test + - popd + + - name: integration test + script: + # Integration test + - pushd scheduler + - mvn package -DskipTests + - popd + - ginkgo -r -race -randomizeAllSpecs src/integration + + # Tests for legacy components (node apiserver, broker and metricscollector) + - name: legacy unit test + script: + - pushd api + - npm install + - npm test + - popd + - pushd servicebroker + - npm install + - npm test + - popd + + - name: legacy integration test + script: + - pushd api + - npm install + - npm test + - popd + - pushd servicebroker + - npm install + - npm test + - popd + - pushd scheduler + - mvn package -DskipTests + - popd + - ginkgo -r -race -randomizeAllSpecs src/integration_legacy - - name: integration test - script: - # Integration test - - pushd api - - npm install - - popd - - pushd servicebroker - - npm install - - popd - - pushd scheduler - - mvn package -DskipTests - - popd - - ginkgo -r -race -randomizeAllSpecs src/integration diff --git a/src/autoscaler/db/sqldb/binding_sqldb_test.go b/src/autoscaler/db/sqldb/binding_sqldb_test.go index cc7916564..35b7593f2 100644 --- a/src/autoscaler/db/sqldb/binding_sqldb_test.go +++ b/src/autoscaler/db/sqldb/binding_sqldb_test.go @@ -272,16 +272,19 @@ var _ = Describe("BindingSqldb", func() { It("should return what was created", func() { Expect(retrievedServiceInstance).To(Equal(&models.ServiceInstance{testInstanceId, testOrgGuid, testSpaceGuid, policyJsonStr, policyGuid})) }) - Context("When the service instance doesn't have a default policy", func() { - BeforeEach(func() { - policyJsonStr = "" - }) - It("should return an empty default policy", func() { - Expect(err).NotTo(HaveOccurred()) - Expect(retrievedServiceInstance.DefaultPolicy).To(BeEmpty()) - }) + }) + Context("when the service instance doesn't have a default policy", func() { + BeforeEach(func() { + err = bdb.CreateServiceInstance(models.ServiceInstance{testInstanceId, testOrgGuid, testSpaceGuid, "", ""}) + Expect(err).NotTo(HaveOccurred()) + }) + It("should return an empty default policy", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(retrievedServiceInstance.DefaultPolicy).To(BeEmpty()) + Expect(retrievedServiceInstance.DefaultPolicyGuid).To(BeEmpty()) }) }) + }) Describe("CreateServiceBinding", func() { diff --git a/src/integration/helpers.go b/src/integration/helpers.go index f7c75f006..ad911ee06 100644 --- a/src/integration/helpers.go +++ b/src/integration/helpers.go @@ -22,6 +22,24 @@ type AppInstanceMetricResult struct { Resources []models.AppInstanceMetric `json:"resources"` } +type AppAggregatedMetricResult struct { + TotalResults int `json:"total_results"` + TotalPages int `json:"total_pages"` + Page int `json:"page"` + PrevUrl string `json:"prev_url"` + NextUrl string `json:"next_url"` + Resources []models.AppMetric `json:"resources"` +} + +type ScalingHistoryResult struct { + TotalResults int `json:"total_results"` + TotalPages int `json:"total_pages"` + Page int `json:"page"` + PrevUrl string `json:"prev_url"` + NextUrl string `json:"next_url"` + Resources []models.AppScalingHistory `json:"resources"` +} + func getAppAggregatedMetricUrl(appId string, metricType string, parameteters map[string]string, pageNo int) string { return fmt.Sprintf("/v1/apps/%s/aggregated_metric_histories/%s?any=any&start-time=%s&end-time=%s&order-direction=%s&page=%d&results-per-page=%s", appId, metricType, parameteters["start-time"], parameteters["end-time"], parameteters["order-direction"], pageNo, parameteters["results-per-page"]) } diff --git a/src/integration_legacy/components.go b/src/integration_legacy/components.go new file mode 100644 index 000000000..660c68e67 --- /dev/null +++ b/src/integration_legacy/components.go @@ -0,0 +1,889 @@ +package integration_legacy + +import ( + "autoscaler/cf" + "autoscaler/db" + "autoscaler/helpers" + "autoscaler/models" + + apiConfig "autoscaler/api/config" + egConfig "autoscaler/eventgenerator/config" + mcConfig "autoscaler/metricscollector/config" + mgConfig "autoscaler/metricsgateway/config" + msConfig "autoscaler/metricsserver/config" + opConfig "autoscaler/operator/config" + seConfig "autoscaler/scalingengine/config" + + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + "os" + "os/exec" + "path/filepath" + "time" + + . "github.com/onsi/gomega" + "github.com/tedsuo/ifrit/ginkgomon" + yaml "gopkg.in/yaml.v2" +) + +const ( + APIServer = "apiServer" + APIPublicServer = "APIPublicServer" + GolangAPIServer = "golangApiServer" + ServiceBroker = "serviceBroker" + GolangServiceBroker = "golangServiceBroker" + ServiceBrokerInternal = "serviceBrokerInternal" + Scheduler = "scheduler" + MetricsCollector = "metricsCollector" + EventGenerator = "eventGenerator" + ScalingEngine = "scalingEngine" + Operator = "operator" + ConsulCluster = "consulCluster" + MetricsGateway = "metricsGateway" + MetricsServerHTTP = "metricsServerHTTP" + MetricsServerWS = "metricsServerWS" +) + +var serviceCatalogPath string = "../../servicebroker/config/catalog.json" +var schemaValidationPath string = "../../servicebroker/config/catalog.schema.json" +var apiServerInfoFilePath string = "../../api/config/info.json" + +var golangAPIInfoFilePath string = "../autoscaler/api/exampleconfig/catalog-example.json" +var golangSchemaValidationPath string = "../autoscaler/api/schemas/catalog.schema.json" +var golangApiServerPolicySchemaPath string = "../autoscaler/api/policyvalidator/policy_json.schema.json" +var golangServiceCatalogPath string = "../../servicebroker/config/catalog.json" + +type Executables map[string]string +type Ports map[string]int + +type Components struct { + Executables Executables + Ports Ports +} + +type DBConfig struct { + URI string `json:"uri"` + MinConnections int `json:"minConnections"` + MaxConnections int `json:"maxConnections"` + IdleTimeout int `json:"idleTimeout"` +} +type APIServerClient struct { + Uri string `json:"uri"` + TLS models.TLSCerts `json:"tls"` +} + +type ServiceBrokerConfig struct { + Port int `json:"port"` + PublicPort int `json:"publicPort"` + HealthPort int `json:"healthPort"` + EnableCustomMetrics bool `json:"enableCustomMetrics"` + + Username string `json:"username"` + Password string `json:"password"` + + DB DBConfig `json:"db"` + + APIServerClient APIServerClient `json:"apiserver"` + HttpRequestTimeout int `json:"httpRequestTimeout"` + TLS models.TLSCerts `json:"tls"` + PublicTLS models.TLSCerts `json:"publicTls"` + ServiceCatalogPath string `json:"serviceCatalogPath"` + SchemaValidationPath string `json:"schemaValidationPath"` +} +type SchedulerClient struct { + Uri string `json:"uri"` + TLS models.TLSCerts `json:"tls"` +} +type ScalingEngineClient struct { + Uri string `json:"uri"` + TLS models.TLSCerts `json:"tls"` +} +type MetricsCollectorClient struct { + Uri string `json:"uri"` + TLS models.TLSCerts `json:"tls"` +} +type EventGeneratorClient struct { + Uri string `json:"uri"` + TLS models.TLSCerts `json:"tls"` +} +type ServiceOffering struct { + Enabled bool `json:"enabled"` + ServiceBrokerClient ServiceBrokerClient `json:"serviceBroker"` +} +type ServiceBrokerClient struct { + Uri string `json:"uri"` + TLS models.TLSCerts `json:"tls"` +} +type APIServerConfig struct { + Port int `json:"port"` + PublicPort int `json:"publicPort"` + HealthPort int `json:"healthPort"` + InfoFilePath string `json:"infoFilePath"` + CFAPI string `json:"cfApi"` + CFClientId string `json:"cfClientId"` + CFClientSecret string `json:"cfClientSecret"` + SkipSSLValidation bool `json:"skipSSLValidation"` + CacheTTL int `json:"cacheTTL"` + DB DBConfig `json:"db"` + SchedulerClient SchedulerClient `json:"scheduler"` + ScalingEngineClient ScalingEngineClient `json:"scalingEngine"` + MetricsCollectorClient MetricsCollectorClient `json:"metricsCollector"` + EventGeneratorClient EventGeneratorClient `json:"eventGenerator"` + ServiceOffering ServiceOffering `json:"serviceOffering"` + + TLS models.TLSCerts `json:"tls"` + PublicTLS models.TLSCerts `json:"publicTls"` + HttpClientTimeout int `json:"httpClientTimeout"` + MinBreachDurationSecs int `json:"minBreachDurationSecs"` + MinCoolDownSecs int `json:"minCoolDownSecs"` +} + +func (components *Components) ServiceBroker(confPath string, argv ...string) *ginkgomon.Runner { + return ginkgomon.New(ginkgomon.Config{ + Name: ServiceBroker, + AnsiColorCode: "32m", + StartCheck: "Service broker server is running", + StartCheckTimeout: 20 * time.Second, + Command: exec.Command( + "node", append([]string{components.Executables[ServiceBroker], "-c", confPath}, argv...)..., + ), + Cleanup: func() { + }, + }) +} + +func (components *Components) ApiServer(confPath string, argv ...string) *ginkgomon.Runner { + return ginkgomon.New(ginkgomon.Config{ + Name: APIServer, + AnsiColorCode: "33m", + StartCheck: "Autoscaler API server started", + StartCheckTimeout: 20 * time.Second, + Command: exec.Command( + "node", append([]string{components.Executables[APIServer], "-c", confPath}, argv...)..., + ), + Cleanup: func() { + }, + }) +} +func (components *Components) GolangAPIServer(confPath string, argv ...string) *ginkgomon.Runner { + + return ginkgomon.New(ginkgomon.Config{ + Name: GolangAPIServer, + AnsiColorCode: "33m", + StartCheck: `"api.started"`, + StartCheckTimeout: 20 * time.Second, + Command: exec.Command( + components.Executables[GolangAPIServer], + append([]string{ + "-c", confPath, + }, argv...)..., + ), + }) +} +func (components *Components) Scheduler(confPath string, argv ...string) *ginkgomon.Runner { + return ginkgomon.New(ginkgomon.Config{ + Name: Scheduler, + AnsiColorCode: "34m", + StartCheck: "Scheduler is ready to start", + StartCheckTimeout: 120 * time.Second, + Command: exec.Command( + "java", append([]string{"-jar", "-Dspring.config.location=" + confPath, components.Executables[Scheduler]}, argv...)..., + ), + Cleanup: func() { + }, + }) +} + +func (components *Components) MetricsCollector(confPath string, argv ...string) *ginkgomon.Runner { + + return ginkgomon.New(ginkgomon.Config{ + Name: MetricsCollector, + AnsiColorCode: "35m", + StartCheck: `"metricscollector.started"`, + StartCheckTimeout: 20 * time.Second, + Command: exec.Command( + components.Executables[MetricsCollector], + append([]string{ + "-c", confPath, + }, argv...)..., + ), + }) +} + +func (components *Components) EventGenerator(confPath string, argv ...string) *ginkgomon.Runner { + + return ginkgomon.New(ginkgomon.Config{ + Name: EventGenerator, + AnsiColorCode: "36m", + StartCheck: `"eventgenerator.started"`, + StartCheckTimeout: 20 * time.Second, + Command: exec.Command( + components.Executables[EventGenerator], + append([]string{ + "-c", confPath, + }, argv...)..., + ), + }) +} + +func (components *Components) ScalingEngine(confPath string, argv ...string) *ginkgomon.Runner { + + return ginkgomon.New(ginkgomon.Config{ + Name: ScalingEngine, + AnsiColorCode: "31m", + StartCheck: `"scalingengine.started"`, + StartCheckTimeout: 20 * time.Second, + Command: exec.Command( + components.Executables[ScalingEngine], + append([]string{ + "-c", confPath, + }, argv...)..., + ), + }) +} + +func (components *Components) Operator(confPath string, argv ...string) *ginkgomon.Runner { + + return ginkgomon.New(ginkgomon.Config{ + Name: Operator, + AnsiColorCode: "38m", + StartCheck: `"operator.started"`, + StartCheckTimeout: 40 * time.Second, + Command: exec.Command( + components.Executables[Operator], + append([]string{ + "-c", confPath, + }, argv...)..., + ), + }) +} + +func (components *Components) MetricsGateway(confPath string, argv ...string) *ginkgomon.Runner { + + return ginkgomon.New(ginkgomon.Config{ + Name: MetricsGateway, + AnsiColorCode: "32m", + StartCheck: `"metricsgateway.started"`, + StartCheckTimeout: 20 * time.Second, + Command: exec.Command( + components.Executables[MetricsGateway], + append([]string{ + "-c", confPath, + }, argv...)..., + ), + }) +} + +func (components *Components) MetricsServer(confPath string, argv ...string) *ginkgomon.Runner { + + return ginkgomon.New(ginkgomon.Config{ + Name: MetricsServerHTTP, + AnsiColorCode: "33m", + StartCheck: `"metricsserver.started"`, + StartCheckTimeout: 20 * time.Second, + Command: exec.Command( + components.Executables[MetricsServerHTTP], + append([]string{ + "-c", confPath, + }, argv...)..., + ), + }) +} + +func (components *Components) PrepareServiceBrokerConfig(publicPort int, internalPort int, username string, password string, enableCustomMetrics bool, dbUri string, apiServerUri string, brokerApiHttpRequestTimeout time.Duration, tmpDir string) string { + brokerConfig := ServiceBrokerConfig{ + Port: internalPort, + PublicPort: publicPort, + HealthPort: 0, + Username: username, + Password: password, + EnableCustomMetrics: enableCustomMetrics, + DB: DBConfig{ + URI: dbUri, + MinConnections: 1, + MaxConnections: 10, + IdleTimeout: 1000, + }, + APIServerClient: APIServerClient{ + Uri: apiServerUri, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "api.key"), + CertFile: filepath.Join(testCertDir, "api.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + HttpRequestTimeout: int(brokerApiHttpRequestTimeout / time.Millisecond), + PublicTLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "servicebroker.key"), + CertFile: filepath.Join(testCertDir, "servicebroker.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "servicebroker_internal.key"), + CertFile: filepath.Join(testCertDir, "servicebroker_internal.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + ServiceCatalogPath: serviceCatalogPath, + SchemaValidationPath: schemaValidationPath, + } + + cfgFile, err := ioutil.TempFile(tmpDir, ServiceBroker) + w := json.NewEncoder(cfgFile) + err = w.Encode(brokerConfig) + Expect(err).NotTo(HaveOccurred()) + cfgFile.Close() + return cfgFile.Name() +} + +func (components *Components) PrepareApiServerConfig(port int, publicPort int, skipSSLValidation bool, cacheTTL int, cfApi string, dbUri string, schedulerUri string, scalingEngineUri string, metricsCollectorUri string, eventGeneratorUri string, serviceBrokerUri string, serviceOfferingEnabled bool, httpClientTimeout time.Duration, minBreachDurationSecs int, minCoolDownSecs int, tmpDir string) string { + + apiConfig := APIServerConfig{ + Port: port, + PublicPort: publicPort, + HealthPort: 0, + InfoFilePath: apiServerInfoFilePath, + CFAPI: cfApi, + CFClientId: "admin", + CFClientSecret: "admin-secret", + SkipSSLValidation: skipSSLValidation, + CacheTTL: cacheTTL, + DB: DBConfig{ + URI: dbUri, + MinConnections: 1, + MaxConnections: 10, + IdleTimeout: 1000, + }, + + SchedulerClient: SchedulerClient{ + Uri: schedulerUri, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "scheduler.key"), + CertFile: filepath.Join(testCertDir, "scheduler.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + ScalingEngineClient: ScalingEngineClient{ + Uri: scalingEngineUri, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "scalingengine.key"), + CertFile: filepath.Join(testCertDir, "scalingengine.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + MetricsCollectorClient: MetricsCollectorClient{ + Uri: metricsCollectorUri, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "metricscollector.key"), + CertFile: filepath.Join(testCertDir, "metricscollector.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + EventGeneratorClient: EventGeneratorClient{ + Uri: eventGeneratorUri, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "eventgenerator.key"), + CertFile: filepath.Join(testCertDir, "eventgenerator.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + ServiceOffering: ServiceOffering{ + Enabled: serviceOfferingEnabled, + ServiceBrokerClient: ServiceBrokerClient{ + Uri: serviceBrokerUri, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "servicebroker_internal.key"), + CertFile: filepath.Join(testCertDir, "servicebroker_internal.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + }, + + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "api.key"), + CertFile: filepath.Join(testCertDir, "api.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + + PublicTLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "api_public.key"), + CertFile: filepath.Join(testCertDir, "api_public.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + HttpClientTimeout: int(httpClientTimeout / time.Millisecond), + MinBreachDurationSecs: minBreachDurationSecs, + MinCoolDownSecs: minCoolDownSecs, + } + + cfgFile, err := ioutil.TempFile(tmpDir, APIServer) + w := json.NewEncoder(cfgFile) + err = w.Encode(apiConfig) + Expect(err).NotTo(HaveOccurred()) + cfgFile.Close() + return cfgFile.Name() +} + +func (components *Components) PrepareGolangApiServerConfig(dbURI string, publicApiPort int, brokerPort int, cfApi string, skipSSLValidation bool, cacheTTL int, schedulerUri string, scalingEngineUri string, metricsCollectorUri string, eventGeneratorUri string, metricsForwarderUri string, useBuildInMode bool, httpClientTimeout time.Duration, tmpDir string) string { + + cfg := apiConfig.Config{ + Logging: helpers.LoggingConfig{ + Level: LOGLEVEL, + }, + PublicApiServer: apiConfig.ServerConfig{ + Port: publicApiPort, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "api.key"), + CertFile: filepath.Join(testCertDir, "api.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + BrokerServer: apiConfig.ServerConfig{ + Port: brokerPort, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "servicebroker.key"), + CertFile: filepath.Join(testCertDir, "servicebroker.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + DB: apiConfig.DBConfig{ + PolicyDB: db.DatabaseConfig{ + URL: dbURI, + }, + BindingDB: db.DatabaseConfig{ + URL: dbURI, + }, + }, + BrokerUsername: brokerUserName, + BrokerPassword: brokerPassword, + CatalogPath: golangServiceCatalogPath, + CatalogSchemaPath: golangSchemaValidationPath, + DashboardRedirectURI: "", + PolicySchemaPath: golangApiServerPolicySchemaPath, + Scheduler: apiConfig.SchedulerConfig{ + SchedulerURL: schedulerUri, + TLSClientCerts: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "scheduler.key"), + CertFile: filepath.Join(testCertDir, "scheduler.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + ScalingEngine: apiConfig.ScalingEngineConfig{ + ScalingEngineUrl: scalingEngineUri, + TLSClientCerts: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "scalingengine.key"), + CertFile: filepath.Join(testCertDir, "scalingengine.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + MetricsCollector: apiConfig.MetricsCollectorConfig{ + MetricsCollectorUrl: metricsCollectorUri, + TLSClientCerts: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "metricscollector.key"), + CertFile: filepath.Join(testCertDir, "metricscollector.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + EventGenerator: apiConfig.EventGeneratorConfig{ + EventGeneratorUrl: eventGeneratorUri, + TLSClientCerts: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "eventgenerator.key"), + CertFile: filepath.Join(testCertDir, "eventgenerator.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + CF: cf.CFConfig{ + API: cfApi, + ClientID: "admin", + Secret: "admin", + }, + UseBuildInMode: useBuildInMode, + InfoFilePath: golangAPIInfoFilePath, + MetricsForwarder: apiConfig.MetricsForwarderConfig{ + MetricsForwarderUrl: metricsForwarderUri, + }, + RateLimit: models.RateLimitConfig{ + MaxAmount: 10, + ValidDuration: 1 * time.Second, + }, + } + + return writeYmlConfig(tmpDir, GolangAPIServer, &cfg) +} + +func (components *Components) PrepareSchedulerConfig(dbUri string, scalingEngineUri string, tmpDir string, httpClientTimeout time.Duration) string { + dbUrl, _ := url.Parse(dbUri) + scheme := dbUrl.Scheme + host := dbUrl.Host + path := dbUrl.Path + userInfo := dbUrl.User + userName := userInfo.Username() + password, _ := userInfo.Password() + if scheme == "postgres" { + scheme = "postgresql" + } + jdbcDBUri := fmt.Sprintf("jdbc:%s://%s%s", scheme, host, path) + settingStrTemplate := ` +#datasource for application and quartz +spring.datasource.driverClassName=org.postgresql.Driver +spring.datasource.url=%s +spring.datasource.username=%s +spring.datasource.password=%s +#policy db +spring.policyDbDataSource.driverClassName=org.postgresql.Driver +spring.policyDbDataSource.url=%s +spring.policyDbDataSource.username=%s +spring.policyDbDataSource.password=%s +#quartz job +scalingenginejob.reschedule.interval.millisecond=10000 +scalingenginejob.reschedule.maxcount=3 +scalingengine.notification.reschedule.maxcount=3 +# scaling engine url +autoscaler.scalingengine.url=%s +#ssl +server.ssl.key-store=%s/scheduler.p12 +server.ssl.key-alias=scheduler +server.ssl.key-store-password=123456 +server.ssl.key-store-type=PKCS12 +server.ssl.trust-store=%s/autoscaler.truststore +server.ssl.trust-store-password=123456 +client.ssl.key-store=%s/scheduler.p12 +client.ssl.key-store-password=123456 +client.ssl.key-store-type=PKCS12 +client.ssl.trust-store=%s/autoscaler.truststore +client.ssl.trust-store-password=123456 +client.ssl.protocol=TLSv1.2 +server.ssl.enabled-protocols[3]=TLSv1,TLSv1.1,TLSv1.2 +server.ssl.ciphers[23]=TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_RC4_128_SHA,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,SSL_RSA_WITH_RC4_128_SHA + +server.port=%d +scheduler.healthserver.port=0 +client.httpClientTimeout=%d +#Quartz +org.quartz.scheduler.instanceName=app-autoscaler +org.quartz.scheduler.instanceId=0 + +spring.application.name=scheduler +spring.mvc.servlet.load-on-startup=1 +spring.aop.auto=false +endpoints.enabled=false +spring.data.jpa.repositories.enabled=false +` + settingJsonStr := fmt.Sprintf(settingStrTemplate, jdbcDBUri, userName, password, jdbcDBUri, userName, password, scalingEngineUri, testCertDir, testCertDir, testCertDir, testCertDir, components.Ports[Scheduler], components.Ports[Scheduler], int(httpClientTimeout/time.Second)) + cfgFile, err := os.Create(filepath.Join(tmpDir, "application.properties")) + Expect(err).NotTo(HaveOccurred()) + ioutil.WriteFile(cfgFile.Name(), []byte(settingJsonStr), 0777) + cfgFile.Close() + return cfgFile.Name() +} + +func (components *Components) PrepareMetricsCollectorConfig(dbURI string, port int, ccNOAAUAAURL string, collectInterval time.Duration, + refreshInterval time.Duration, saveInterval time.Duration, collectMethod string, httpClientTimeout time.Duration, tmpDir string) string { + cfg := mcConfig.Config{ + CF: cf.CFConfig{ + API: ccNOAAUAAURL, + ClientID: "admin", + Secret: "admin", + }, + Server: mcConfig.ServerConfig{ + Port: port, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "metricscollector.key"), + CertFile: filepath.Join(testCertDir, "metricscollector.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + NodeAddrs: []string{"localhost"}, + NodeIndex: 0, + }, + Logging: helpers.LoggingConfig{ + Level: LOGLEVEL, + }, + DB: mcConfig.DBConfig{ + InstanceMetricsDB: db.DatabaseConfig{ + URL: dbURI, + }, + PolicyDB: db.DatabaseConfig{ + URL: dbURI, + }, + }, + Collector: mcConfig.CollectorConfig{ + CollectInterval: collectInterval, + RefreshInterval: refreshInterval, + CollectMethod: collectMethod, + SaveInterval: saveInterval, + MetricCacheSizePerApp: 500, + PersistMetrics: true, + }, + HttpClientTimeout: httpClientTimeout, + } + return writeYmlConfig(tmpDir, MetricsCollector, &cfg) +} + +func (components *Components) PrepareEventGeneratorConfig(dbUri string, port int, metricsCollectorURL string, scalingEngineURL string, aggregatorExecuteInterval time.Duration, + policyPollerInterval time.Duration, saveInterval time.Duration, evaluationManagerInterval time.Duration, httpClientTimeout time.Duration, tmpDir string) string { + conf := &egConfig.Config{ + Logging: helpers.LoggingConfig{ + Level: LOGLEVEL, + }, + Server: egConfig.ServerConfig{ + Port: port, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "eventgenerator.key"), + CertFile: filepath.Join(testCertDir, "eventgenerator.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + NodeAddrs: []string{"localhost"}, + NodeIndex: 0, + }, + Aggregator: egConfig.AggregatorConfig{ + AggregatorExecuteInterval: aggregatorExecuteInterval, + PolicyPollerInterval: policyPollerInterval, + SaveInterval: saveInterval, + MetricPollerCount: 1, + AppMonitorChannelSize: 1, + AppMetricChannelSize: 1, + MetricCacheSizePerApp: 50, + }, + Evaluator: egConfig.EvaluatorConfig{ + EvaluationManagerInterval: evaluationManagerInterval, + EvaluatorCount: 1, + TriggerArrayChannelSize: 1, + }, + DB: egConfig.DBConfig{ + PolicyDB: db.DatabaseConfig{ + URL: dbUri, + }, + AppMetricDB: db.DatabaseConfig{ + URL: dbUri, + }, + }, + ScalingEngine: egConfig.ScalingEngineConfig{ + ScalingEngineURL: scalingEngineURL, + TLSClientCerts: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "eventgenerator.key"), + CertFile: filepath.Join(testCertDir, "eventgenerator.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + MetricCollector: egConfig.MetricCollectorConfig{ + MetricCollectorURL: metricsCollectorURL, + TLSClientCerts: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "eventgenerator.key"), + CertFile: filepath.Join(testCertDir, "eventgenerator.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + DefaultBreachDurationSecs: 600, + DefaultStatWindowSecs: 60, + HttpClientTimeout: httpClientTimeout, + } + return writeYmlConfig(tmpDir, EventGenerator, &conf) +} + +func (components *Components) PrepareScalingEngineConfig(dbURI string, port int, ccUAAURL string, httpClientTimeout time.Duration, tmpDir string) string { + conf := seConfig.Config{ + CF: cf.CFConfig{ + API: ccUAAURL, + ClientID: "admin", + Secret: "admin", + }, + Server: seConfig.ServerConfig{ + Port: port, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "scalingengine.key"), + CertFile: filepath.Join(testCertDir, "scalingengine.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + Logging: helpers.LoggingConfig{ + Level: LOGLEVEL, + }, + DB: seConfig.DBConfig{ + PolicyDB: db.DatabaseConfig{ + URL: dbURI, + }, + ScalingEngineDB: db.DatabaseConfig{ + URL: dbURI, + }, + SchedulerDB: db.DatabaseConfig{ + URL: dbURI, + }, + }, + DefaultCoolDownSecs: 300, + LockSize: 32, + HttpClientTimeout: httpClientTimeout, + } + + return writeYmlConfig(tmpDir, ScalingEngine, &conf) +} + +func (components *Components) PrepareOperatorConfig(dbURI string, ccUAAURL string, scalingEngineURL string, schedulerURL string, syncInterval time.Duration, cutoffDuration time.Duration, httpClientTimeout time.Duration, tmpDir string) string { + conf := &opConfig.Config{ + Logging: helpers.LoggingConfig{ + Level: LOGLEVEL, + }, + CF: cf.CFConfig{ + API: ccUAAURL, + ClientID: "admin", + Secret: "admin", + }, + InstanceMetricsDB: opConfig.InstanceMetricsDbPrunerConfig{ + RefreshInterval: 2 * time.Minute, + CutoffDuration: cutoffDuration, + DB: db.DatabaseConfig{ + URL: dbURI, + }, + }, + AppMetricsDB: opConfig.AppMetricsDBPrunerConfig{ + RefreshInterval: 2 * time.Minute, + CutoffDuration: cutoffDuration, + DB: db.DatabaseConfig{ + URL: dbURI, + }, + }, + ScalingEngineDB: opConfig.ScalingEngineDBPrunerConfig{ + RefreshInterval: 2 * time.Minute, + CutoffDuration: cutoffDuration, + DB: db.DatabaseConfig{ + URL: dbURI, + }, + }, + ScalingEngine: opConfig.ScalingEngineConfig{ + URL: scalingEngineURL, + SyncInterval: syncInterval, + TLSClientCerts: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "scalingengine.key"), + CertFile: filepath.Join(testCertDir, "scalingengine.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + Scheduler: opConfig.SchedulerConfig{ + URL: schedulerURL, + SyncInterval: syncInterval, + TLSClientCerts: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "scheduler.key"), + CertFile: filepath.Join(testCertDir, "scheduler.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + DBLock: opConfig.DBLockConfig{ + LockTTL: 30 * time.Second, + DB: db.DatabaseConfig{ + URL: dbURI, + }, + LockRetryInterval: 15 * time.Second, + }, + AppSyncer: opConfig.AppSyncerConfig{ + SyncInterval: 60 * time.Second, + DB: db.DatabaseConfig{ + URL: dbURI, + }, + }, + HttpClientTimeout: httpClientTimeout, + } + return writeYmlConfig(tmpDir, Operator, &conf) +} + +func (components *Components) PrepareMetricsGatewayConfig(dbURI string, metricServerAddresses []string, rlpAddr string, tmpDir string) string { + cfg := mgConfig.Config{ + Logging: helpers.LoggingConfig{ + Level: LOGLEVEL, + }, + EnvelopChanSize: 500, + NozzleCount: 1, + MetricServerAddrs: metricServerAddresses, + AppManager: mgConfig.AppManagerConfig{ + AppRefreshInterval: 10 * time.Second, + PolicyDB: db.DatabaseConfig{ + URL: dbURI, + MaxOpenConnections: 10, + MaxIdleConnections: 5, + ConnectionMaxLifetime: 60 * time.Second, + }, + }, + Emitter: mgConfig.EmitterConfig{ + BufferSize: 500, + KeepAliveInterval: 1 * time.Second, + HandshakeTimeout: 1 * time.Second, + MaxSetupRetryCount: 3, + MaxCloseRetryCount: 3, + RetryDelay: 500 * time.Millisecond, + MetricsServerClientTLS: &models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "metricserver_client.key"), + CertFile: filepath.Join(testCertDir, "metricserver_client.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + Nozzle: mgConfig.NozzleConfig{ + RLPAddr: rlpAddr, + ShardID: "autoscaler", + RLPClientTLS: &models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "reverselogproxy_client.key"), + CertFile: filepath.Join(testCertDir, "reverselogproxy_client.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + } + return writeYmlConfig(tmpDir, MetricsGateway, &cfg) +} + +func (components *Components) PrepareMetricsServerConfig(dbURI string, httpClientTimeout time.Duration, httpServerPort int, wsServerPort int, tmpDir string) string { + cfg := msConfig.Config{ + Logging: helpers.LoggingConfig{ + Level: LOGLEVEL, + }, + HttpClientTimeout: httpClientTimeout, + NodeAddrs: []string{"localhost"}, + NodeIndex: 0, + DB: msConfig.DBConfig{ + PolicyDB: db.DatabaseConfig{ + URL: dbURI, + MaxOpenConnections: 10, + MaxIdleConnections: 5, + ConnectionMaxLifetime: 60 * time.Second, + }, + InstanceMetricsDB: db.DatabaseConfig{ + URL: dbURI, + MaxOpenConnections: 10, + MaxIdleConnections: 5, + ConnectionMaxLifetime: 60 * time.Second, + }, + }, + Collector: msConfig.CollectorConfig{ + WSPort: wsServerPort, + WSKeepAliveTime: 5 * time.Second, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "metricserver.key"), + CertFile: filepath.Join(testCertDir, "metricserver.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + RefreshInterval: 5 * time.Second, + CollectInterval: 1 * time.Second, + SaveInterval: 2 * time.Second, + MetricCacheSizePerApp: 100, + PersistMetrics: true, + EnvelopeProcessorCount: 2, + EnvelopeChannelSize: 100, + MetricChannelSize: 100, + }, + Server: msConfig.ServerConfig{ + Port: httpServerPort, + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "metricserver.key"), + CertFile: filepath.Join(testCertDir, "metricserver.crt"), + CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), + }, + }, + } + return writeYmlConfig(tmpDir, MetricsServerHTTP, &cfg) +} + +func writeYmlConfig(dir string, componentName string, c interface{}) string { + cfgFile, err := ioutil.TempFile(dir, componentName) + Expect(err).NotTo(HaveOccurred()) + defer cfgFile.Close() + configBytes, err := yaml.Marshal(c) + ioutil.WriteFile(cfgFile.Name(), configBytes, 0777) + return cfgFile.Name() + +} diff --git a/src/integration/fakeInvalidDataPolicy.json b/src/integration_legacy/fakeInvalidDataPolicy.json similarity index 100% rename from src/integration/fakeInvalidDataPolicy.json rename to src/integration_legacy/fakeInvalidDataPolicy.json diff --git a/src/integration_legacy/fakeInvalidPolicy.json b/src/integration_legacy/fakeInvalidPolicy.json new file mode 100644 index 000000000..a1473d603 --- /dev/null +++ b/src/integration_legacy/fakeInvalidPolicy.json @@ -0,0 +1,4 @@ +{ + "instance_min_count": 10, + "instance_max_count": 4 +} diff --git a/src/integration_legacy/fakeMinimalScalingRulePolicy.json b/src/integration_legacy/fakeMinimalScalingRulePolicy.json new file mode 100644 index 000000000..3eecade2f --- /dev/null +++ b/src/integration_legacy/fakeMinimalScalingRulePolicy.json @@ -0,0 +1,70 @@ +{ + "instance_min_count": 1, + "instance_max_count": 4, + "scaling_rules": [ + { + "metric_type": "memoryused", + "threshold": 30, + "operator": "<", + "adjustment": "-1" + }, + { + "metric_type": "memoryutil", + "threshold": 90, + "operator": ">=", + "adjustment": "+1" + }, + { + "metric_type": "responsetime", + "threshold": 90, + "operator": ">=", + "adjustment": "+1" + }, + { + "metric_type": "throughput", + "threshold": 90, + "operator": ">=", + "adjustment": "+1" + } + ], + "schedules": { + "timezone": "Asia/Shanghai", + "recurring_schedule": [ + { + "start_time": "10:00", + "end_time": "18:00", + "days_of_week": [ + 1, + 2, + 3 + ], + "instance_min_count": 1, + "instance_max_count": 10, + "initial_min_instance_count": 5 + }, + { + "start_date": "2099-06-27", + "end_date": "2099-07-23", + "start_time": "11:00", + "end_time": "19:30", + "days_of_month": [ + 5, + 15, + 25 + ], + "instance_min_count": 3, + "instance_max_count": 10, + "initial_min_instance_count": 5 + } + ], + "specific_date": [ + { + "start_date_time": "2099-06-02T10:00", + "end_date_time": "2099-06-15T13:59", + "instance_min_count": 1, + "instance_max_count": 4, + "initial_min_instance_count": 2 + } + ] + } +} diff --git a/src/integration_legacy/fakePolicyWithSchedule.json b/src/integration_legacy/fakePolicyWithSchedule.json new file mode 100644 index 000000000..ea4a75c63 --- /dev/null +++ b/src/integration_legacy/fakePolicyWithSchedule.json @@ -0,0 +1,91 @@ +{ + "instance_min_count": 1, + "instance_max_count": 4, + "scaling_rules": [ + { + "metric_type": "memoryutil", + "breach_duration_secs": 600, + "threshold": 40, + "operator": "<", + "cool_down_secs": 300, + "adjustment": "-1" + }, + { + "metric_type": "memoryutil", + "breach_duration_secs": 600, + "threshold": 90, + "operator": ">=", + "cool_down_secs": 300, + "adjustment": "+1" + } + ], + "schedules": { + "timezone": "Asia/Shanghai", + "recurring_schedule": [ + { + "start_time": "10:00", + "end_time": "18:00", + "days_of_week": [ + 1, + 2, + 3 + ], + "instance_min_count": 1, + "instance_max_count": 10, + "initial_min_instance_count": 5 + }, + { + "start_date": "2099-06-27", + "end_date": "2099-07-23", + "start_time": "11:00", + "end_time": "19:30", + "days_of_month": [ + 5, + 15, + 25 + ], + "instance_min_count": 3, + "instance_max_count": 10, + "initial_min_instance_count": 5 + }, + { + "start_time": "10:00", + "end_time": "18:00", + "days_of_week": [ + 4, + 5, + 6 + ], + "instance_min_count": 1, + "instance_max_count": 10 + }, + { + "start_time": "11:00", + "end_time": "19:30", + "days_of_month": [ + 10, + 20, + 30 + ], + "instance_min_count": 1, + "instance_max_count": 10 + } + ], + "specific_date": [ + { + "start_date_time": "2099-06-02T10:00", + "end_date_time": "2099-06-15T13:59", + "instance_min_count": 1, + "instance_max_count": 4, + "initial_min_instance_count": 2 + }, + { + "start_date_time": "2099-01-04T20:00", + "end_date_time": "2099-02-19T23:15", + "instance_min_count": 2, + "instance_max_count": 5, + "initial_min_instance_count": 3 + } + ] + } +} diff --git a/src/integration_legacy/fakePolicyWithScheduleAnother.json b/src/integration_legacy/fakePolicyWithScheduleAnother.json new file mode 100644 index 000000000..9b3d57a0c --- /dev/null +++ b/src/integration_legacy/fakePolicyWithScheduleAnother.json @@ -0,0 +1,73 @@ +{ + "instance_min_count": 2, + "instance_max_count": 5, + "scaling_rules": [ + { + "metric_type": "memoryutil", + "breach_duration_secs": 600, + "threshold": 30, + "operator": "<", + "cool_down_secs": 300, + "adjustment": "-1" + }, + { + "metric_type": "memoryutil", + "breach_duration_secs": 600, + "threshold": 90, + "operator": ">=", + "cool_down_secs": 300, + "adjustment": "+1" + } + ], + "schedules": { + "timezone": "Asia/Shanghai", + "recurring_schedule": [ + { + "start_time": "10:00", + "end_time": "18:00", + "days_of_week": [ + 1, + 2, + 3 + ], + "instance_min_count": 2, + "instance_max_count": 5, + "initial_min_instance_count": 5 + }, + { + "start_date": "2099-06-27", + "end_date": "2099-07-23", + "start_time": "11:00", + "end_time": "19:30", + "days_of_month": [ + 5, + 15, + 25 + ], + "instance_min_count": 2, + "instance_max_count": 5, + "initial_min_instance_count": 5 + }, + { + "start_time": "10:00", + "end_time": "18:00", + "days_of_week": [ + 4, + 5, + 6 + ], + "instance_min_count": 2, + "instance_max_count": 5 + } + ], + "specific_date": [ + { + "start_date_time": "2099-06-02T10:00", + "end_date_time": "2099-06-15T13:59", + "instance_min_count": 1, + "instance_max_count": 4, + "initial_min_instance_count": 2 + } + ] + } +} diff --git a/src/integration_legacy/fakePolicyWithoutSchedule.json b/src/integration_legacy/fakePolicyWithoutSchedule.json new file mode 100644 index 000000000..33959ddaa --- /dev/null +++ b/src/integration_legacy/fakePolicyWithoutSchedule.json @@ -0,0 +1,22 @@ +{ + "instance_min_count": 1, + "instance_max_count": 4, + "scaling_rules": [ + { + "metric_type": "memoryutil", + "breach_duration_secs": 600, + "threshold": 30, + "operator": "<", + "cool_down_secs": 300, + "adjustment": "-1" + }, + { + "metric_type": "memoryutil", + "breach_duration_secs": 600, + "threshold": 90, + "operator": ">=", + "cool_down_secs": 300, + "adjustment": "+1" + } + ] +} diff --git a/src/integration_legacy/helpers.go b/src/integration_legacy/helpers.go new file mode 100644 index 000000000..f9dc86925 --- /dev/null +++ b/src/integration_legacy/helpers.go @@ -0,0 +1,185 @@ +package integration_legacy + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "autoscaler/models" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +type AppInstanceMetricResult struct { + TotalResults int `json:"total_results"` + TotalPages int `json:"total_pages"` + Page int `json:"page"` + PrevUrl string `json:"prev_url"` + NextUrl string `json:"next_url"` + Resources []models.AppInstanceMetric `json:"resources"` +} + +func getAppAggregatedMetricUrl(appId string, metricType string, parameteters map[string]string, pageNo int) string { + return fmt.Sprintf("/v1/apps/%s/aggregated_metric_histories/%s?any=any&start-time=%s&end-time=%s&order-direction=%s&page=%d&results-per-page=%s", appId, metricType, parameteters["start-time"], parameteters["end-time"], parameteters["order-direction"], pageNo, parameteters["results-per-page"]) +} + +func compareAppAggregatedMetricResult(o1, o2 AppAggregatedMetricResult) { + Expect(o1.Page).To(Equal(o2.Page)) + Expect(o1.TotalPages).To(Equal(o2.TotalPages)) + Expect(o1.TotalResults).To(Equal(o2.TotalResults)) + Expect(o1.Resources).To(Equal(o2.Resources)) + + prevUrl1, err1 := url.Parse(o1.PrevUrl) + Expect(err1).NotTo(HaveOccurred()) + prevUrl2, err2 := url.Parse(o2.PrevUrl) + Expect(err2).NotTo(HaveOccurred()) + queries1 := prevUrl1.Query() + queries2 := prevUrl2.Query() + Expect(queries1).To(Equal(queries2)) + + nextUrl1, err1 := url.Parse(o1.NextUrl) + Expect(err1).NotTo(HaveOccurred()) + nextUrl2, err2 := url.Parse(o2.NextUrl) + Expect(err2).NotTo(HaveOccurred()) + queries1 = nextUrl1.Query() + queries2 = nextUrl2.Query() + Expect(queries1).To(Equal(queries2)) + +} +func checkAggregatedMetricResult(apiServerPort int, pathVariables []string, parameters map[string]string, result AppAggregatedMetricResult) { + var actual AppAggregatedMetricResult + resp, err := getAppAggregatedMetrics(apiServerPort, pathVariables, parameters) + defer resp.Body.Close() + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + err = json.NewDecoder(resp.Body).Decode(&actual) + Expect(err).NotTo(HaveOccurred()) + compareAppAggregatedMetricResult(actual, result) + +} + +func getInstanceMetricsUrl(appId string, metricType string, parameteters map[string]string, pageNo int) string { + return fmt.Sprintf("/v1/apps/%s/metric_histories/%s?any=any&start-time=%s&end-time=%s&order-direction=%s&page=%d&results-per-page=%s", appId, metricType, parameteters["start-time"], parameteters["end-time"], parameteters["order-direction"], pageNo, parameteters["results-per-page"]) +} + +func getInstanceMetricsUrlWithInstanceIndex(appId string, metricType string, parameteters map[string]string, pageNo int) string { + return fmt.Sprintf("/v1/apps/%s/metric_histories/%s?any=any&instance-index=%s&start-time=%s&end-time=%s&order-direction=%s&page=%d&results-per-page=%s", appId, metricType, parameteters["instance-index"], parameteters["start-time"], parameteters["end-time"], parameteters["order-direction"], pageNo, parameteters["results-per-page"]) +} + +func compareAppInstanceMetricResult(o1, o2 AppInstanceMetricResult) { + Expect(o1.Page).To(Equal(o2.Page)) + Expect(o1.TotalPages).To(Equal(o2.TotalPages)) + Expect(o1.TotalResults).To(Equal(o2.TotalResults)) + Expect(o1.Resources).To(Equal(o2.Resources)) + + prevUrl1, err1 := url.Parse(o1.PrevUrl) + Expect(err1).NotTo(HaveOccurred()) + prevUrl2, err2 := url.Parse(o2.PrevUrl) + Expect(err2).NotTo(HaveOccurred()) + queries1 := prevUrl1.Query() + queries2 := prevUrl2.Query() + Expect(queries1).To(Equal(queries2)) + + nextUrl1, err1 := url.Parse(o1.NextUrl) + Expect(err1).NotTo(HaveOccurred()) + nextUrl2, err2 := url.Parse(o2.NextUrl) + Expect(err2).NotTo(HaveOccurred()) + queries1 = nextUrl1.Query() + queries2 = nextUrl2.Query() + Expect(queries1).To(Equal(queries2)) + +} +func checkAppInstanceMetricResult(apiServerPort int, pathVariables []string, parameters map[string]string, result AppInstanceMetricResult) { + var actual AppInstanceMetricResult + resp, err := getAppInstanceMetrics(apiServerPort, pathVariables, parameters) + Expect(err).NotTo(HaveOccurred()) + defer resp.Body.Close() + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + err = json.NewDecoder(resp.Body).Decode(&actual) + Expect(err).NotTo(HaveOccurred()) + compareAppInstanceMetricResult(actual, result) + +} + +func getScalingHistoriesUrl(appId string, parameteters map[string]string, pageNo int) string { + return fmt.Sprintf("/v1/apps/%s/scaling_histories?any=any&start-time=%s&end-time=%s&order-direction=%s&page=%d&results-per-page=%s", appId, parameteters["start-time"], parameteters["end-time"], parameteters["order-direction"], pageNo, parameteters["results-per-page"]) +} + +func compareScalingHistoryResult(o1, o2 ScalingHistoryResult) { + Expect(o1.Page).To(Equal(o2.Page)) + Expect(o1.TotalPages).To(Equal(o2.TotalPages)) + Expect(o1.TotalResults).To(Equal(o2.TotalResults)) + Expect(o1.Resources).To(Equal(o2.Resources)) + + prevUrl1, err1 := url.Parse(o1.PrevUrl) + Expect(err1).NotTo(HaveOccurred()) + prevUrl2, err2 := url.Parse(o2.PrevUrl) + Expect(err2).NotTo(HaveOccurred()) + queries1 := prevUrl1.Query() + queries2 := prevUrl2.Query() + Expect(queries1).To(Equal(queries2)) + + nextUrl1, err1 := url.Parse(o1.NextUrl) + Expect(err1).NotTo(HaveOccurred()) + nextUrl2, err2 := url.Parse(o2.NextUrl) + Expect(err2).NotTo(HaveOccurred()) + queries1 = nextUrl1.Query() + queries2 = nextUrl2.Query() + Expect(queries1).To(Equal(queries2)) + +} +func checkScalingHistoryResult(apiServerPort int, pathVariables []string, parameters map[string]string, result ScalingHistoryResult) { + var actual ScalingHistoryResult + resp, err := getScalingHistories(apiServerPort, pathVariables, parameters) + defer resp.Body.Close() + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + err = json.NewDecoder(resp.Body).Decode(&actual) + Expect(err).NotTo(HaveOccurred()) + compareScalingHistoryResult(actual, result) + +} + +func doAttachPolicy(appId string, policyStr []byte, statusCode int, apiServerPort int, httpClient *http.Client) { + resp, err := attachPolicy(appId, policyStr, apiServerPort, httpClient) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + ExpectWithOffset(1, resp.StatusCode).To(Equal(statusCode)) + resp.Body.Close() + +} +func doDetachPolicy(appId string, statusCode int, msg string, apiServerPort int, httpClient *http.Client) { + resp, err := detachPolicy(appId, apiServerPort, httpClient) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(statusCode)) + if msg != "" { + respBody, err := ioutil.ReadAll(resp.Body) + Expect(err).NotTo(HaveOccurred()) + Expect(string(respBody)).To(Equal(msg)) + } + resp.Body.Close() +} +func checkApiServerStatus(appId string, statusCode int, apiServerPort int, httpClient *http.Client) { + By("checking the API Server") + resp, err := getPolicy(appId, apiServerPort, httpClient) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(statusCode)) + resp.Body.Close() +} +func checkApiServerContent(appId string, policyStr []byte, statusCode int, port int, httpClient *http.Client) { + By("checking the API Server") + var expected map[string]interface{} + err := json.Unmarshal(policyStr, &expected) + Expect(err).NotTo(HaveOccurred()) + checkResponseContent(getPolicy, appId, statusCode, expected, port, httpClient) +} +func checkSchedulerStatus(appId string, statusCode int) { + By("checking the Scheduler") + resp, err := getSchedules(appId) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(statusCode)) + resp.Body.Close() +} diff --git a/src/integration/integration_api_broker_graceful_shutdown_test.go b/src/integration_legacy/integration_api_broker_graceful_shutdown_test.go similarity index 98% rename from src/integration/integration_api_broker_graceful_shutdown_test.go rename to src/integration_legacy/integration_api_broker_graceful_shutdown_test.go index 350b6001f..f026b6921 100644 --- a/src/integration/integration_api_broker_graceful_shutdown_test.go +++ b/src/integration_legacy/integration_api_broker_graceful_shutdown_test.go @@ -1,4 +1,4 @@ -package integration +package integration_legacy import ( "encoding/base64" @@ -17,7 +17,7 @@ import ( "github.com/tedsuo/ifrit/ginkgomon" ) -var _ = Describe("Integration_Api_Broker_Graceful_Shutdown", func() { +var _ = Describe("integration_legacy_Api_Broker_Graceful_Shutdown", func() { var ( runner *ginkgomon.Runner diff --git a/src/integration/integration_api_eventgenerator_test.go b/src/integration_legacy/integration_api_eventgenerator_test.go similarity index 99% rename from src/integration/integration_api_eventgenerator_test.go rename to src/integration_legacy/integration_api_eventgenerator_test.go index 9b6e8867d..d53d48655 100644 --- a/src/integration/integration_api_eventgenerator_test.go +++ b/src/integration_legacy/integration_api_eventgenerator_test.go @@ -1,4 +1,4 @@ -package integration +package integration_legacy import ( "autoscaler/cf" @@ -20,7 +20,7 @@ type AppAggregatedMetricResult struct { Resources []models.AppMetric `json:"resources"` } -var _ = Describe("Integration_Api_EventGenerator", func() { +var _ = Describe("Integration_legacy_Api_EventGenerator", func() { var ( appId string pathVariables []string diff --git a/src/integration/integration_api_metricscollector_test.go b/src/integration_legacy/integration_api_metricscollector_test.go similarity index 99% rename from src/integration/integration_api_metricscollector_test.go rename to src/integration_legacy/integration_api_metricscollector_test.go index 9fa46f81b..339b93b24 100644 --- a/src/integration/integration_api_metricscollector_test.go +++ b/src/integration_legacy/integration_api_metricscollector_test.go @@ -1,4 +1,4 @@ -package integration +package integration_legacy import ( "autoscaler/cf" @@ -12,7 +12,7 @@ import ( "github.com/onsi/gomega/ghttp" ) -var _ = Describe("Integration_Api_MetricsCollector", func() { +var _ = Describe("Integration_legacy_Api_MetricsCollector", func() { var ( appId string pathVariables []string diff --git a/src/integration/integration_api_scalingengine_test.go b/src/integration_legacy/integration_api_scalingengine_test.go similarity index 99% rename from src/integration/integration_api_scalingengine_test.go rename to src/integration_legacy/integration_api_scalingengine_test.go index a156ff977..bbba18fcd 100644 --- a/src/integration/integration_api_scalingengine_test.go +++ b/src/integration_legacy/integration_api_scalingengine_test.go @@ -1,4 +1,4 @@ -package integration +package integration_legacy import ( "autoscaler/cf" @@ -20,7 +20,7 @@ type ScalingHistoryResult struct { Resources []models.AppScalingHistory `json:"resources"` } -var _ = Describe("Integration_Api_ScalingEngine", func() { +var _ = Describe("Integration_legacy_Api_ScalingEngine", func() { var ( initInstanceCount int = 2 appId string diff --git a/src/integration/integration_api_scheduler_test.go b/src/integration_legacy/integration_api_scheduler_test.go similarity index 99% rename from src/integration/integration_api_scheduler_test.go rename to src/integration_legacy/integration_api_scheduler_test.go index a43c911e8..2568a8504 100644 --- a/src/integration/integration_api_scheduler_test.go +++ b/src/integration_legacy/integration_api_scheduler_test.go @@ -1,4 +1,4 @@ -package integration +package integration_legacy import ( "autoscaler/cf" @@ -12,7 +12,7 @@ import ( "github.com/onsi/gomega/ghttp" ) -var _ = Describe("Integration_Api_Scheduler", func() { +var _ = Describe("Integration_legacy_Api_Scheduler", func() { var ( appId string policyStr []byte diff --git a/src/integration/integration_broker_api_test.go b/src/integration_legacy/integration_broker_api_test.go similarity index 99% rename from src/integration/integration_broker_api_test.go rename to src/integration_legacy/integration_broker_api_test.go index dba4c918e..6173f5b4d 100644 --- a/src/integration/integration_broker_api_test.go +++ b/src/integration_legacy/integration_broker_api_test.go @@ -1,4 +1,4 @@ -package integration +package integration_legacy import ( "encoding/base64" @@ -13,7 +13,7 @@ import ( "github.com/onsi/gomega/ghttp" ) -var _ = Describe("Integration_Broker_Api", func() { +var _ = Describe("Integration_legacy_Broker_Api", func() { var ( regPath = regexp.MustCompile(`^/v1/apps/.*/schedules`) diff --git a/src/integration/integration_golangapi_metricscollector_test.go b/src/integration_legacy/integration_golangapi_metricscollector_test.go similarity index 99% rename from src/integration/integration_golangapi_metricscollector_test.go rename to src/integration_legacy/integration_golangapi_metricscollector_test.go index 26549816a..c17181660 100644 --- a/src/integration/integration_golangapi_metricscollector_test.go +++ b/src/integration_legacy/integration_golangapi_metricscollector_test.go @@ -1,4 +1,4 @@ -package integration +package integration_legacy import ( "autoscaler/cf" @@ -12,7 +12,7 @@ import ( "github.com/onsi/gomega/ghttp" ) -var _ = Describe("Integration_GolangApi_MetricsCollector", func() { +var _ = Describe("Integration_legacy_GolangApi_MetricsCollector", func() { var ( appId string pathVariables []string diff --git a/src/integration_legacy/integration_suite_test.go b/src/integration_legacy/integration_suite_test.go new file mode 100644 index 000000000..f518cb5c6 --- /dev/null +++ b/src/integration_legacy/integration_suite_test.go @@ -0,0 +1,1004 @@ +package integration_legacy + +import ( + "autoscaler/cf" + "autoscaler/db" + "autoscaler/metricscollector/testhelpers" + "autoscaler/models" + as_testhelpers "autoscaler/testhelpers" + "bytes" + + "database/sql" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + "os" + "path" + "path/filepath" + "regexp" + "strconv" + "strings" + "syscall" + "testing" + "time" + + "code.cloudfoundry.org/cfhttp" + "code.cloudfoundry.org/go-loggregator/rpc/loggregator_v2" + "code.cloudfoundry.org/lager" + "github.com/cloudfoundry/sonde-go/events" + "github.com/gogo/protobuf/proto" + _ "github.com/lib/pq" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" + "github.com/onsi/gomega/ghttp" + "github.com/tedsuo/ifrit" + "github.com/tedsuo/ifrit/ginkgomon" + "github.com/tedsuo/ifrit/grouper" +) + +type APIType uint8 + +const ( + INTERNAL APIType = iota + PUBLIC +) + +var ( + components Components + tmpDir string + serviceBrokerConfPath string + apiServerConfPath string + golangApiServerConfPath string + schedulerConfPath string + metricsCollectorConfPath string + eventGeneratorConfPath string + scalingEngineConfPath string + operatorConfPath string + metricsGatewayConfPath string + metricsServerConfPath string + brokerUserName string = "username" + brokerPassword string = "password" + brokerAuth string + dbUrl string + LOGLEVEL string + noaaPollingRegPath = regexp.MustCompile(`^/apps/.*/containermetrics$`) + noaaStreamingRegPath = regexp.MustCompile(`^/apps/.*/stream$`) + appSummaryRegPath = regexp.MustCompile(`^/v2/apps/.*/summary$`) + appInstanceRegPath = regexp.MustCompile(`^/v2/apps/.*$`) + checkUserSpaceRegPath = regexp.MustCompile(`^/v2/users/.+/spaces.*$`) + dbHelper *sql.DB + fakeScheduler *ghttp.Server + fakeCCNOAAUAA *ghttp.Server + messagesToSend chan []byte + streamingDoneChan chan bool + emptyMessageChannel chan []byte + testUserId string = "testUserId" + testUserScope []string = []string{"cloud_controller.read", "cloud_controller.write", "password.write", "openid", "network.admin", "network.write", "uaa.user"} + + processMap map[string]ifrit.Process = map[string]ifrit.Process{} + + defaultHttpClientTimeout time.Duration = 10 * time.Second + + brokerApiHttpRequestTimeout time.Duration = 10 * time.Second + apiSchedulerHttpRequestTimeout time.Duration = 10 * time.Second + apiScalingEngineHttpRequestTimeout time.Duration = 10 * time.Second + apiMetricsCollectorHttpRequestTimeout time.Duration = 10 * time.Second + apiMetricsServerHttpRequestTimeout time.Duration = 10 * time.Second + apiEventGeneratorHttpRequestTimeout time.Duration = 10 * time.Second + schedulerScalingEngineHttpRequestTimeout time.Duration = 10 * time.Second + + collectInterval time.Duration = 1 * time.Second + refreshInterval time.Duration = 1 * time.Second + saveInterval time.Duration = 1 * time.Second + aggregatorExecuteInterval time.Duration = 1 * time.Second + policyPollerInterval time.Duration = 1 * time.Second + evaluationManagerInterval time.Duration = 1 * time.Second + breachDurationSecs int = 5 + + httpClient *http.Client + httpClientForPublicApi *http.Client + logger lager.Logger + + testCertDir string = "../../test-certs" +) + +func TestIntegration(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Integration Legacy Suite") +} + +var _ = SynchronizedBeforeSuite(func() []byte { + components = Components{ + Ports: PreparePorts(), + Executables: CompileTestedExecutables(), + } + payload, err := json.Marshal(&components) + Expect(err).NotTo(HaveOccurred()) + + dbUrl = os.Getenv("DBURL") + if dbUrl == "" { + Fail("environment variable $DBURL is not set") + } + + dbHelper, err = sql.Open(db.PostgresDriverName, dbUrl) + Expect(err).NotTo(HaveOccurred()) + + clearDatabase() + + return payload +}, func(encodedBuiltArtifacts []byte) { + err := json.Unmarshal(encodedBuiltArtifacts, &components) + Expect(err).NotTo(HaveOccurred()) + components.Ports = PreparePorts() + + tmpDir, err = ioutil.TempDir("", "autoscaler") + Expect(err).NotTo(HaveOccurred()) + + dbUrl = os.Getenv("DBURL") + dbHelper, err = sql.Open(db.PostgresDriverName, dbUrl) + Expect(err).NotTo(HaveOccurred()) + + LOGLEVEL = os.Getenv("LOGLEVEL") + if LOGLEVEL == "" { + LOGLEVEL = "info" + } +}) + +var _ = SynchronizedAfterSuite(func() { + if len(tmpDir) > 0 { + os.RemoveAll(tmpDir) + } +}, func() { + +}) + +var _ = BeforeEach(func() { + httpClient = cfhttp.NewClient() + httpClientForPublicApi = cfhttp.NewClient() + logger = lager.NewLogger("test") + logger.RegisterSink(lager.NewWriterSink(GinkgoWriter, lager.DEBUG)) +}) + +func CompileTestedExecutables() Executables { + builtExecutables := Executables{} + rootDir := os.Getenv("GOPATH") + var err error + builtExecutables[APIServer] = path.Join(rootDir, "api/index.js") + builtExecutables[ServiceBroker] = path.Join(rootDir, "servicebroker/lib/index.js") + builtExecutables[Scheduler] = path.Join(rootDir, "scheduler/target/scheduler-1.0-SNAPSHOT.war") + + builtExecutables[EventGenerator], err = gexec.BuildIn(rootDir, "autoscaler/eventgenerator/cmd/eventgenerator", "-race") + Expect(err).NotTo(HaveOccurred()) + + builtExecutables[MetricsCollector], err = gexec.BuildIn(rootDir, "autoscaler/metricscollector/cmd/metricscollector", "-race") + Expect(err).NotTo(HaveOccurred()) + + builtExecutables[ScalingEngine], err = gexec.BuildIn(rootDir, "autoscaler/scalingengine/cmd/scalingengine", "-race") + Expect(err).NotTo(HaveOccurred()) + + builtExecutables[Operator], err = gexec.BuildIn(rootDir, "autoscaler/operator/cmd/operator", "-race") + Expect(err).NotTo(HaveOccurred()) + + builtExecutables[MetricsGateway], err = gexec.BuildIn(rootDir, "autoscaler/metricsgateway/cmd/metricsgateway", "-race") + Expect(err).NotTo(HaveOccurred()) + + builtExecutables[MetricsServerHTTP], err = gexec.BuildIn(rootDir, "autoscaler/metricsserver/cmd/metricsserver", "-race") + Expect(err).NotTo(HaveOccurred()) + + builtExecutables[GolangAPIServer], err = gexec.BuildIn(rootDir, "autoscaler/api/cmd/api", "-race") + Expect(err).NotTo(HaveOccurred()) + + return builtExecutables +} + +func PreparePorts() Ports { + return Ports{ + APIServer: 10000 + GinkgoParallelNode(), + GolangAPIServer: 22000 + GinkgoParallelNode(), + APIPublicServer: 12000 + GinkgoParallelNode(), + ServiceBroker: 13000 + GinkgoParallelNode(), + GolangServiceBroker: 23000 + GinkgoParallelNode(), + ServiceBrokerInternal: 14000 + GinkgoParallelNode(), + Scheduler: 15000 + GinkgoParallelNode(), + MetricsCollector: 16000 + GinkgoParallelNode(), + MetricsServerHTTP: 20000 + GinkgoParallelNode(), + MetricsServerWS: 21000 + GinkgoParallelNode(), + EventGenerator: 17000 + GinkgoParallelNode(), + ScalingEngine: 18000 + GinkgoParallelNode(), + } +} + +func startApiServer() *ginkgomon.Runner { + runner := components.ApiServer(apiServerConfPath) + processMap[APIServer] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {APIServer, runner}, + })) + return runner +} + +func startGolangApiServer() { + processMap[GolangAPIServer] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {GolangAPIServer, components.GolangAPIServer(golangApiServerConfPath)}, + })) +} + +func startServiceBroker() *ginkgomon.Runner { + runner := components.ServiceBroker(serviceBrokerConfPath) + processMap[ServiceBroker] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {ServiceBroker, runner}, + })) + return runner +} + +func startScheduler() { + processMap[Scheduler] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {Scheduler, components.Scheduler(schedulerConfPath)}, + })) +} + +func startMetricsCollector() { + processMap[MetricsCollector] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {MetricsCollector, components.MetricsCollector(metricsCollectorConfPath)}, + })) +} + +func startEventGenerator() { + processMap[EventGenerator] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {EventGenerator, components.EventGenerator(eventGeneratorConfPath)}, + })) +} + +func startScalingEngine() { + processMap[ScalingEngine] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {ScalingEngine, components.ScalingEngine(scalingEngineConfPath)}, + })) +} + +func startOperator() { + processMap[Operator] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {Operator, components.Operator(operatorConfPath)}, + })) +} + +func startMetricsGateway() { + processMap[MetricsGateway] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {MetricsGateway, components.MetricsGateway(metricsGatewayConfPath)}, + })) +} + +func startMetricsServer() { + processMap[MetricsServerHTTP] = ginkgomon.Invoke(grouper.NewOrdered(os.Interrupt, grouper.Members{ + {MetricsServerHTTP, components.MetricsServer(metricsServerConfPath)}, + })) +} + +func stopApiServer() { + ginkgomon.Kill(processMap[APIServer], 5*time.Second) +} +func stopGolangApiServer() { + ginkgomon.Kill(processMap[GolangAPIServer], 5*time.Second) +} +func stopScheduler() { + ginkgomon.Kill(processMap[Scheduler], 5*time.Second) +} +func stopScalingEngine() { + ginkgomon.Kill(processMap[ScalingEngine], 5*time.Second) +} +func stopMetricsCollector() { + ginkgomon.Kill(processMap[MetricsCollector], 5*time.Second) +} +func stopEventGenerator() { + ginkgomon.Kill(processMap[EventGenerator], 5*time.Second) +} +func stopServiceBroker() { + ginkgomon.Kill(processMap[ServiceBroker], 5*time.Second) +} +func stopOperator() { + ginkgomon.Kill(processMap[Operator], 5*time.Second) +} +func stopMetricsGateway() { + ginkgomon.Kill(processMap[MetricsGateway], 5*time.Second) +} +func stopMetricsServer() { + ginkgomon.Kill(processMap[MetricsServerHTTP], 5*time.Second) +} + +func sendSigusr2Signal(component string) { + process := processMap[component] + if process != nil { + process.Signal(syscall.SIGUSR2) + } +} + +func sendKillSignal(component string) { + ginkgomon.Kill(processMap[component], 5*time.Second) +} + +func stopAll() { + for _, process := range processMap { + if process == nil { + continue + } + ginkgomon.Interrupt(process, 15*time.Second) + } +} + +func getRandomId() string { + return strconv.FormatInt(time.Now().UnixNano(), 10) +} + +func initializeHttpClient(certFileName string, keyFileName string, caCertFileName string, httpRequestTimeout time.Duration) { + TLSConfig, err := cfhttp.NewTLSConfig( + filepath.Join(testCertDir, certFileName), + filepath.Join(testCertDir, keyFileName), + filepath.Join(testCertDir, caCertFileName), + ) + Expect(err).NotTo(HaveOccurred()) + httpClient.Transport.(*http.Transport).TLSClientConfig = TLSConfig + httpClient.Timeout = httpRequestTimeout +} +func initializeHttpClientForPublicApi(certFileName string, keyFileName string, caCertFileName string, httpRequestTimeout time.Duration) { + TLSConfig, err := cfhttp.NewTLSConfig( + filepath.Join(testCertDir, certFileName), + filepath.Join(testCertDir, keyFileName), + filepath.Join(testCertDir, caCertFileName), + ) + Expect(err).NotTo(HaveOccurred()) + httpClientForPublicApi.Transport.(*http.Transport).TLSClientConfig = TLSConfig + httpClientForPublicApi.Timeout = httpRequestTimeout +} + +func provisionServiceInstance(serviceInstanceId string, orgId string, spaceId string, defaultPolicy []byte, brokerPort int, httpClient *http.Client) (*http.Response, error) { + var bindBody map[string]interface{} + if defaultPolicy != nil { + defaultPolicy := json.RawMessage(defaultPolicy) + parameters := map[string]interface{}{ + "default_policy": &defaultPolicy, + } + bindBody = map[string]interface{}{ + "organization_guid": orgId, + "space_guid": spaceId, + "service_id": "app-autoscaler", + "plan_id": "free", + "parameters": parameters, + } + } else { + bindBody = map[string]interface{}{ + "organization_guid": orgId, + "space_guid": spaceId, + "service_id": "app-autoscaler", + "plan_id": "free", + } + } + + body, err := json.Marshal(bindBody) + + req, err := http.NewRequest("PUT", fmt.Sprintf("https://127.0.0.1:%d/v2/service_instances/%s", brokerPort, serviceInstanceId), bytes.NewReader(body)) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+brokerAuth) + return httpClient.Do(req) +} + +func updateServiceInstance(serviceInstanceId string, defaultPolicy []byte, brokerPort int, httpClient *http.Client) (*http.Response, error) { + var updateBody map[string]interface{} + if defaultPolicy != nil { + defaultPolicy := json.RawMessage(defaultPolicy) + parameters := map[string]interface{}{ + "default_policy": &defaultPolicy, + } + updateBody = map[string]interface{}{ + "service_id": "app-autoscaler", + "parameters": parameters, + } + } + + body, err := json.Marshal(updateBody) + + req, err := http.NewRequest("PATCH", fmt.Sprintf("https://127.0.0.1:%d/v2/service_instances/%s", brokerPort, serviceInstanceId), bytes.NewReader(body)) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+brokerAuth) + return httpClient.Do(req) +} + +func deprovisionServiceInstance(serviceInstanceId string, brokerPort int, httpClient *http.Client) (*http.Response, error) { + req, err := http.NewRequest("DELETE", fmt.Sprintf("https://127.0.0.1:%d/v2/service_instances/%s", brokerPort, serviceInstanceId), strings.NewReader(`{"service_id":"app-autoscaler","plan_id":"free"}`)) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+brokerAuth) + return httpClient.Do(req) +} + +func bindService(bindingId string, appId string, serviceInstanceId string, policy []byte, brokerPort int, httpClient *http.Client) (*http.Response, error) { + var bindBody map[string]interface{} + if policy != nil { + rawParameters := json.RawMessage(policy) + bindBody = map[string]interface{}{ + "app_guid": appId, + "service_id": "app-autoscaler", + "plan_id": "free", + "parameters": &rawParameters, + } + } else { + bindBody = map[string]interface{}{ + "app_guid": appId, + "service_id": "app-autoscaler", + "plan_id": "free", + } + } + + body, err := json.Marshal(bindBody) + req, err := http.NewRequest("PUT", fmt.Sprintf("https://127.0.0.1:%d/v2/service_instances/%s/service_bindings/%s", brokerPort, serviceInstanceId, bindingId), bytes.NewReader(body)) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+brokerAuth) + return httpClient.Do(req) +} + +func unbindService(bindingId string, appId string, serviceInstanceId string, brokerPort int, httpClient *http.Client) (*http.Response, error) { + req, err := http.NewRequest("DELETE", fmt.Sprintf("https://127.0.0.1:%d/v2/service_instances/%s/service_bindings/%s", brokerPort, serviceInstanceId, bindingId), strings.NewReader(fmt.Sprintf(`{"app_guid":"%s","service_id":"app-autoscaler","plan_id":"free"}`, appId))) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+brokerAuth) + return httpClient.Do(req) +} + +func provisionAndBind(serviceInstanceId string, orgId string, spaceId string, defaultPolicy []byte, bindingId string, appId string, policy []byte, brokerPort int, httpClient *http.Client) { + resp, err := provisionServiceInstance(serviceInstanceId, orgId, spaceId, defaultPolicy, brokerPort, httpClient) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + resp.Body.Close() + + resp, err = bindService(bindingId, appId, serviceInstanceId, policy, brokerPort, httpClient) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + resp.Body.Close() +} +func unbindAndDeprovision(bindingId string, appId string, serviceInstanceId string, brokerPort int, httpClient *http.Client) { + resp, err := unbindService(bindingId, appId, serviceInstanceId, brokerPort, httpClient) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + + resp, err = deprovisionServiceInstance(serviceInstanceId, brokerPort, httpClient) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + +} +func getPolicy(appId string, apiServerPort int, httpClient *http.Client) (*http.Response, error) { + + req, err := http.NewRequest("GET", fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/policy", apiServerPort, appId), nil) + req.Header.Set("Authorization", "bearer fake-token") + Expect(err).NotTo(HaveOccurred()) + return httpClient.Do(req) +} + +func detachPolicy(appId string, apiServerPort int, httpClient *http.Client) (*http.Response, error) { + req, err := http.NewRequest("DELETE", fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/policy", apiServerPort, appId), strings.NewReader("")) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "bearer fake-token") + return httpClient.Do(req) +} + +func attachPolicy(appId string, policy []byte, apiServerPort int, httpClient *http.Client) (*http.Response, error) { + req, err := http.NewRequest("PUT", fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/policy", apiServerPort, appId), bytes.NewReader(policy)) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "bearer fake-token") + return httpClient.Do(req) +} + +func getSchedules(appId string) (*http.Response, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/schedules", components.Ports[Scheduler], appId), strings.NewReader("")) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + return httpClient.Do(req) +} + +func createSchedule(appId string, guid string, schedule string) (*http.Response, error) { + req, err := http.NewRequest("PUT", fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/schedules?guid=%s", components.Ports[Scheduler], appId, guid), bytes.NewReader([]byte(schedule))) + if err != nil { + panic(err) + } + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + return httpClient.Do(req) +} + +func deleteSchedule(appId string) (*http.Response, error) { + req, err := http.NewRequest("DELETE", fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/schedules", components.Ports[Scheduler], appId), strings.NewReader("")) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + return httpClient.Do(req) +} + +func getActiveSchedule(appId string) (*http.Response, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/active_schedules", components.Ports[ScalingEngine], appId), strings.NewReader("")) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + return httpClient.Do(req) +} + +func activeScheduleExists(appId string) bool { + resp, err := getActiveSchedule(appId) + Expect(err).NotTo(HaveOccurred()) + + return resp.StatusCode == http.StatusOK +} + +func setPolicyRecurringDate(policyByte []byte) []byte { + + var policy models.ScalingPolicy + err := json.Unmarshal(policyByte, &policy) + Expect(err).NotTo(HaveOccurred()) + + if policy.Schedules != nil { + location, err := time.LoadLocation(policy.Schedules.Timezone) + Expect(err).NotTo(HaveOccurred()) + now := time.Now().In(location) + starttime := now.Add(time.Minute * 10) + endtime := now.Add(time.Minute * 20) + for _, entry := range policy.Schedules.RecurringSchedules { + if endtime.Day() != starttime.Day() { + entry.StartTime = "00:01" + entry.EndTime = "23:59" + entry.StartDate = endtime.Format("2006-01-02") + } else { + entry.StartTime = starttime.Format("15:04") + entry.EndTime = endtime.Format("15:04") + } + } + } + + content, err := json.Marshal(policy) + Expect(err).NotTo(HaveOccurred()) + return content + +} + +func setPolicySpecificDateTime(policyByte []byte, start time.Duration, end time.Duration) string { + timeZone := "GMT" + location, _ := time.LoadLocation(timeZone) + timeNowInTimeZone := time.Now().In(location) + dateTimeFormat := "2006-01-02T15:04" + startTime := timeNowInTimeZone.Add(start).Format(dateTimeFormat) + endTime := timeNowInTimeZone.Add(end).Format(dateTimeFormat) + + return fmt.Sprintf(string(policyByte), timeZone, startTime, endTime) +} +func getScalingHistories(apiServerPort int, pathVariables []string, parameters map[string]string) (*http.Response, error) { + var httpClientTmp *http.Client + httpClientTmp = httpClientForPublicApi + + url := "https://127.0.0.1:%d/v1/apps/%s/scaling_histories" + if parameters != nil && len(parameters) > 0 { + url += "?any=any" + for paramName, paramValue := range parameters { + url += "&" + paramName + "=" + paramValue + } + } + req, err := http.NewRequest("GET", fmt.Sprintf(url, apiServerPort, pathVariables[0]), strings.NewReader("")) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "bearer fake-token") + return httpClientTmp.Do(req) +} +func getAppInstanceMetrics(apiServerPort int, pathVariables []string, parameters map[string]string) (*http.Response, error) { + var httpClientTmp *http.Client + httpClientTmp = httpClientForPublicApi + url := "https://127.0.0.1:%d/v1/apps/%s/metric_histories/%s" + if parameters != nil && len(parameters) > 0 { + url += "?any=any" + for paramName, paramValue := range parameters { + url += "&" + paramName + "=" + paramValue + } + } + req, err := http.NewRequest("GET", fmt.Sprintf(url, apiServerPort, pathVariables[0], pathVariables[1]), strings.NewReader("")) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "bearer fake-token") + return httpClientTmp.Do(req) +} + +func getAppAggregatedMetrics(apiServerPort int, pathVariables []string, parameters map[string]string) (*http.Response, error) { + var httpClientTmp *http.Client + httpClientTmp = httpClientForPublicApi + url := "https://127.0.0.1:%d/v1/apps/%s/aggregated_metric_histories/%s" + if parameters != nil && len(parameters) > 0 { + url += "?any=any" + for paramName, paramValue := range parameters { + url += "&" + paramName + "=" + paramValue + } + } + req, err := http.NewRequest("GET", fmt.Sprintf(url, apiServerPort, pathVariables[0], pathVariables[1]), strings.NewReader("")) + Expect(err).NotTo(HaveOccurred()) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "bearer fake-token") + return httpClientTmp.Do(req) +} + +func readPolicyFromFile(filename string) []byte { + content, err := ioutil.ReadFile(filename) + Expect(err).NotTo(HaveOccurred()) + return content +} + +func clearDatabase() { + _, err := dbHelper.Exec("DELETE FROM policy_json") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM binding") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM service_instance") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM app_scaling_recurring_schedule") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM app_scaling_specific_date_schedule") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM app_scaling_active_schedule") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM activeschedule") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM scalinghistory") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM app_metric") + Expect(err).NotTo(HaveOccurred()) + + _, err = dbHelper.Exec("DELETE FROM appinstancemetrics") + Expect(err).NotTo(HaveOccurred()) +} + +func insertPolicy(appId string, policyStr string, guid string) { + query := "INSERT INTO policy_json(app_id, policy_json, guid) VALUES($1, $2, $3)" + _, err := dbHelper.Exec(query, appId, policyStr, guid) + Expect(err).NotTo(HaveOccurred()) + +} + +func deletePolicy(appId string) { + query := "DELETE FROM policy_json WHERE app_id=$1" + _, err := dbHelper.Exec(query, appId) + Expect(err).NotTo(HaveOccurred()) +} + +func insertScalingHistory(history *models.AppScalingHistory) { + query := "INSERT INTO scalinghistory" + + "(appid, timestamp, scalingtype, status, oldinstances, newinstances, reason, message, error) " + + " VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9)" + _, err := dbHelper.Exec(query, history.AppId, history.Timestamp, history.ScalingType, history.Status, + history.OldInstances, history.NewInstances, history.Reason, history.Message, history.Error) + + Expect(err).NotTo(HaveOccurred()) +} +func getScalingHistoryCount(appId string, oldInstanceCount int, newInstanceCount int) int { + var count int + query := "SELECT COUNT(*) FROM scalinghistory WHERE appid=$1 AND oldinstances=$2 AND newinstances=$3" + err := dbHelper.QueryRow(query, appId, oldInstanceCount, newInstanceCount).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + return count +} +func getScalingHistoryTotalCount(appId string) int { + var count int + query := "SELECT COUNT(*) FROM scalinghistory WHERE appid=$1" + err := dbHelper.QueryRow(query, appId).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + return count +} +func insertAppInstanceMetric(appInstanceMetric *models.AppInstanceMetric) { + query := "INSERT INTO appinstancemetrics" + + "(appid, instanceindex, collectedat, name, unit, value, timestamp) " + + "VALUES($1, $2, $3, $4, $5, $6, $7)" + _, err := dbHelper.Exec(query, appInstanceMetric.AppId, appInstanceMetric.InstanceIndex, appInstanceMetric.CollectedAt, appInstanceMetric.Name, appInstanceMetric.Unit, appInstanceMetric.Value, appInstanceMetric.Timestamp) + Expect(err).NotTo(HaveOccurred()) +} +func insertAppMetric(appMetrics *models.AppMetric) { + query := "INSERT INTO app_metric" + + "(app_id, metric_type, unit, value, timestamp) " + + "VALUES($1, $2, $3, $4, $5)" + _, err := dbHelper.Exec(query, appMetrics.AppId, appMetrics.MetricType, appMetrics.Unit, appMetrics.Value, appMetrics.Timestamp) + Expect(err).NotTo(HaveOccurred()) +} + +func getAppInstanceMetricTotalCount(appId string) int { + var count int + query := "SELECT COUNT(*) FROM appinstancemetrics WHERE appid=$1" + err := dbHelper.QueryRow(query, appId).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + return count +} + +func getAppMetricTotalCount(appId string) int { + var count int + query := "SELECT COUNT(*) FROM app_metric WHERE app_id=$1" + err := dbHelper.QueryRow(query, appId).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + return count +} + +func getCredentialsCount(appId string) int { + var count int + query := "SELECT COUNT(*) FROM credentials WHERE id=$1" + err := dbHelper.QueryRow(query, appId).Scan(&count) + Expect(err).NotTo(HaveOccurred()) + return count +} + +type GetResponse func(id string, port int, httpClient *http.Client) (*http.Response, error) +type GetResponseWithParameters func(apiServerPort int, pathVariables []string, parameters map[string]string) (*http.Response, error) + +func checkResponseContent(getResponse GetResponse, id string, expectHttpStatus int, expectResponseMap map[string]interface{}, port int, httpClient *http.Client) { + resp, err := getResponse(id, port, httpClient) + checkResponse(resp, err, expectHttpStatus, expectResponseMap) + +} +func checkPublicAPIResponseContentWithParameters(getResponseWithParameters GetResponseWithParameters, apiServerPort int, pathVariables []string, parameters map[string]string, expectHttpStatus int, expectResponseMap map[string]interface{}) { + resp, err := getResponseWithParameters(apiServerPort, pathVariables, parameters) + checkResponse(resp, err, expectHttpStatus, expectResponseMap) +} +func checkResponse(resp *http.Response, err error, expectHttpStatus int, expectResponseMap map[string]interface{}) { + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(expectHttpStatus)) + var actual map[string]interface{} + err = json.NewDecoder(resp.Body).Decode(&actual) + Expect(err).NotTo(HaveOccurred()) + Expect(actual).To(Equal(expectResponseMap)) + resp.Body.Close() +} + +func checkResponseEmptyAndStatusCode(resp *http.Response, err error, expectedStatus int) { + Expect(err).NotTo(HaveOccurred()) + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + Expect(err).NotTo(HaveOccurred()) + Expect(body).To(HaveLen(0)) + Expect(resp.StatusCode).To(Equal(expectedStatus)) +} + +func assertScheduleContents(appId string, expectHttpStatus int, expectResponseMap map[string]int) { + By("checking the schedule contents") + resp, err := getSchedules(appId) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + ExpectWithOffset(1, resp.StatusCode).To(Equal(expectHttpStatus)) + defer resp.Body.Close() + var actual map[string]interface{} + err = json.NewDecoder(resp.Body).Decode(&actual) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + var schedules map[string]interface{} = actual["schedules"].(map[string]interface{}) + var recurring []interface{} = schedules["recurring_schedule"].([]interface{}) + var specificDate []interface{} = schedules["specific_date"].([]interface{}) + ExpectWithOffset(1, len(specificDate)).To(Equal(expectResponseMap["specific_date"])) + ExpectWithOffset(1, len(recurring)).To(Equal(expectResponseMap["recurring_schedule"])) +} + +func checkScheduleContents(appId string, expectHttpStatus int, expectResponseMap map[string]int) bool { + resp, err := getSchedules(appId) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + ExpectWithOffset(1, resp.StatusCode).To(Equal(expectHttpStatus)) + defer resp.Body.Close() + var actual map[string]interface{} + err = json.NewDecoder(resp.Body).Decode(&actual) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + var schedules map[string]interface{} = actual["schedules"].(map[string]interface{}) + var recurring []interface{} = schedules["recurring_schedule"].([]interface{}) + var specificDate []interface{} = schedules["specific_date"].([]interface{}) + return len(specificDate) == expectResponseMap["specific_date"] && len(recurring) == expectResponseMap["recurring_schedule"] +} + +func startFakeCCNOAAUAA(instanceCount int) { + fakeCCNOAAUAA = ghttp.NewServer() + fakeCCNOAAUAA.RouteToHandler("GET", "/v2/info", ghttp.RespondWithJSONEncoded(http.StatusOK, + cf.Endpoints{ + AuthEndpoint: fakeCCNOAAUAA.URL(), + TokenEndpoint: fakeCCNOAAUAA.URL(), + DopplerEndpoint: strings.Replace(fakeCCNOAAUAA.URL(), "http", "ws", 1), + })) + fakeCCNOAAUAA.RouteToHandler("POST", "/oauth/token", ghttp.RespondWithJSONEncoded(http.StatusOK, cf.Tokens{})) + appState := models.AppStatusStarted + fakeCCNOAAUAA.RouteToHandler("GET", appSummaryRegPath, ghttp.RespondWithJSONEncoded(http.StatusOK, + models.AppEntity{Instances: instanceCount, State: &appState})) + fakeCCNOAAUAA.RouteToHandler("PUT", appInstanceRegPath, ghttp.RespondWith(http.StatusCreated, "")) + fakeCCNOAAUAA.RouteToHandler("POST", "/check_token", ghttp.RespondWithJSONEncoded(http.StatusOK, + struct { + Scope []string `json:"scope"` + }{ + testUserScope, + })) + fakeCCNOAAUAA.RouteToHandler("GET", "/userinfo", ghttp.RespondWithJSONEncoded(http.StatusOK, + struct { + UserId string `json:"user_id"` + }{ + testUserId, + })) + fakeCCNOAAUAA.RouteToHandler("GET", checkUserSpaceRegPath, ghttp.RespondWithJSONEncoded(http.StatusOK, + struct { + TotalResults int `json:"total_results"` + }{ + 1, + })) +} +func fakeMetricsPolling(appId string, memoryValue uint64, memQuota uint64) { + fakeCCNOAAUAA.RouteToHandler("GET", noaaPollingRegPath, + func(rw http.ResponseWriter, r *http.Request) { + mp := multipart.NewWriter(rw) + defer mp.Close() + + rw.Header().Set("Content-Type", `multipart/x-protobuf; boundary=`+mp.Boundary()) + timestamp := time.Now().UnixNano() + message1 := marshalMessage(createContainerMetric(appId, 0, 3.0, memoryValue, 2048000000, memQuota, 4096000000, timestamp)) + message2 := marshalMessage(createContainerMetric(appId, 1, 4.0, memoryValue, 2048000000, memQuota, 4096000000, timestamp)) + message3 := marshalMessage(createContainerMetric(appId, 2, 5.0, memoryValue, 2048000000, memQuota, 4096000000, timestamp)) + + messages := [][]byte{message1, message2, message3} + for _, msg := range messages { + partWriter, _ := mp.CreatePart(nil) + partWriter.Write(msg) + } + }, + ) + +} + +func fakeMetricsStreaming(appId string, memoryValue uint64, memQuota uint64) { + messagesToSend = make(chan []byte, 256) + wsHandler := testhelpers.NewWebsocketHandler(messagesToSend, 100*time.Millisecond) + fakeCCNOAAUAA.RouteToHandler("GET", "/apps/"+appId+"/stream", wsHandler.ServeWebsocket) + + streamingDoneChan = make(chan bool) + ticker := time.NewTicker(500 * time.Millisecond) + go func() { + select { + case <-streamingDoneChan: + ticker.Stop() + return + case <-ticker.C: + timestamp := time.Now().UnixNano() + message1 := marshalMessage(createContainerMetric(appId, 0, 3.0, memoryValue, 2048000000, memQuota, 4096000000, timestamp-int64(time.Duration(breachDurationSecs)*time.Second))) + messagesToSend <- message1 + message2 := marshalMessage(createContainerMetric(appId, 1, 4.0, memoryValue, 2048000000, memQuota, 4096000000, timestamp)) + messagesToSend <- message2 + message3 := marshalMessage(createContainerMetric(appId, 2, 5.0, memoryValue, 2048000000, memQuota, 4096000000, timestamp)) + messagesToSend <- message3 + message4 := marshalMessage(createContainerMetric(appId, 2, 5.0, memoryValue, 2048000000, memQuota, 4096000000, timestamp)) + messagesToSend <- message4 + message5 := marshalMessage(createContainerMetric(appId, 2, 5.0, memoryValue, 2048000000, memQuota, 4096000000, timestamp+int64(time.Duration(breachDurationSecs)*time.Second))) + messagesToSend <- message5 + } + }() + + emptyMessageChannel = make(chan []byte, 256) + emptyWsHandler := testhelpers.NewWebsocketHandler(emptyMessageChannel, 200*time.Millisecond) + fakeCCNOAAUAA.RouteToHandler("GET", noaaStreamingRegPath, emptyWsHandler.ServeWebsocket) + +} + +func closeFakeMetricsStreaming() { + close(streamingDoneChan) + close(messagesToSend) + close(emptyMessageChannel) +} + +func startFakeRLPServer(appId string, envelopes []*loggregator_v2.Envelope, emitInterval time.Duration) *as_testhelpers.FakeEventProducer { + fakeRLPServer, err := as_testhelpers.NewFakeEventProducer(filepath.Join(testCertDir, "reverselogproxy.crt"), filepath.Join(testCertDir, "reverselogproxy.key"), filepath.Join(testCertDir, "autoscaler-ca.crt"), emitInterval) + Expect(err).NotTo(HaveOccurred()) + fakeRLPServer.SetEnvelops(envelopes) + fakeRLPServer.Start() + return fakeRLPServer +} +func stopFakeRLPServer(fakeRLPServer *as_testhelpers.FakeEventProducer) { + stopped := fakeRLPServer.Stop() + Expect(stopped).To(Equal(true)) +} + +func createContainerMetric(appId string, instanceIndex int32, cpuPercentage float64, memoryBytes uint64, diskByte uint64, memQuota uint64, diskQuota uint64, timestamp int64) *events.Envelope { + if timestamp == 0 { + timestamp = time.Now().UnixNano() + } + cm := &events.ContainerMetric{ + ApplicationId: proto.String(appId), + InstanceIndex: proto.Int32(instanceIndex), + CpuPercentage: proto.Float64(cpuPercentage), + MemoryBytes: proto.Uint64(memoryBytes), + DiskBytes: proto.Uint64(diskByte), + MemoryBytesQuota: proto.Uint64(memQuota), + DiskBytesQuota: proto.Uint64(diskQuota), + } + + return &events.Envelope{ + ContainerMetric: cm, + EventType: events.Envelope_ContainerMetric.Enum(), + Origin: proto.String("fake-origin-1"), + Timestamp: proto.Int64(timestamp), + } +} +func createContainerEnvelope(appId string, instanceIndex int32, cpuPercentage float64, memoryBytes float64, diskByte float64, memQuota float64) []*loggregator_v2.Envelope { + return []*loggregator_v2.Envelope{ + &loggregator_v2.Envelope{ + SourceId: appId, + Message: &loggregator_v2.Envelope_Gauge{ + Gauge: &loggregator_v2.Gauge{ + Metrics: map[string]*loggregator_v2.GaugeValue{ + "cpu": &loggregator_v2.GaugeValue{ + Unit: "percentage", + Value: cpuPercentage, + }, + "disk": &loggregator_v2.GaugeValue{ + Unit: "bytes", + Value: diskByte, + }, + "memory": &loggregator_v2.GaugeValue{ + Unit: "bytes", + Value: memoryBytes, + }, + "memory_quota": &loggregator_v2.GaugeValue{ + Unit: "bytes", + Value: memQuota, + }, + }, + }, + }, + }, + } +} +func createHTTPTimerEnvelope(appId string, start int64, end int64) []*loggregator_v2.Envelope { + return []*loggregator_v2.Envelope{ + &loggregator_v2.Envelope{ + SourceId: appId, + Message: &loggregator_v2.Envelope_Timer{ + Timer: &loggregator_v2.Timer{ + Name: "http", + Start: start, + Stop: end, + }, + }, + }, + } + +} +func createCustomEnvelope(appId string, name string, unit string, value float64) []*loggregator_v2.Envelope { + return []*loggregator_v2.Envelope{ + &loggregator_v2.Envelope{ + SourceId: appId, + DeprecatedTags: map[string]*loggregator_v2.Value{ + "origin": &loggregator_v2.Value{ + Data: &loggregator_v2.Value_Text{ + Text: "autoscaler_metrics_forwarder", + }, + }, + }, + Message: &loggregator_v2.Envelope_Gauge{ + Gauge: &loggregator_v2.Gauge{ + Metrics: map[string]*loggregator_v2.GaugeValue{ + name: &loggregator_v2.GaugeValue{ + Unit: unit, + Value: value, + }, + }, + }, + }, + }, + } + +} + +func marshalMessage(message *events.Envelope) []byte { + data, err := proto.Marshal(message) + if err != nil { + log.Println(err.Error()) + } + + return data +}