From 3852b4b1e6e7b65da7612ad0c76a4b1d61d1ce77 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:36:19 +0200 Subject: [PATCH 01/39] Updated .gitignore and refactored health check config in metricsforwarder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Removed metricsforwarder and metricsforwarder.yml from .gitignore. • Changed health check configuration keys to nest under BasicAuth and ServerConfig. • Updated tests to reflect configuration changes and removed unused code. • Fixed request creation in tests to use dynamic server URL and proper basic auth setup. --- src/autoscaler/metricsforwarder/.gitignore | 2 - .../metricsforwarder_suite_test.go | 6 +- .../metricsforwarder/config/config_test.go | 18 ++-- .../server/custom_metrics_handlers_test.go | 22 +++-- .../server/server_suite_test.go | 10 +- .../metricsforwarder/server/server_test.go | 93 ++++++++++--------- 6 files changed, 78 insertions(+), 73 deletions(-) diff --git a/src/autoscaler/metricsforwarder/.gitignore b/src/autoscaler/metricsforwarder/.gitignore index 7995a170f2..7e2f179b52 100644 --- a/src/autoscaler/metricsforwarder/.gitignore +++ b/src/autoscaler/metricsforwarder/.gitignore @@ -1,3 +1 @@ assets -metricsforwarder -metricsforwarder.yml diff --git a/src/autoscaler/metricsforwarder/cmd/metricsforwarder/metricsforwarder_suite_test.go b/src/autoscaler/metricsforwarder/cmd/metricsforwarder/metricsforwarder_suite_test.go index 8b0844078d..1050ee294f 100644 --- a/src/autoscaler/metricsforwarder/cmd/metricsforwarder/metricsforwarder_suite_test.go +++ b/src/autoscaler/metricsforwarder/cmd/metricsforwarder/metricsforwarder_suite_test.go @@ -136,13 +136,13 @@ var _ = SynchronizedBeforeSuite(func() []byte { cfg.RateLimit.ValidDuration = 1 * time.Second cfg.Logging.Level = "debug" - cfg.Health.HealthCheckUsername = "metricsforwarderhealthcheckuser" - cfg.Health.HealthCheckPassword = "metricsforwarderhealthcheckpassword" + cfg.Health.BasicAuth.Username = "metricsforwarderhealthcheckuser" + cfg.Health.BasicAuth.Password = "metricsforwarderhealthcheckpassword" cfg.Health.ReadinessCheckEnabled = true cfg.Server.Port = 10000 + GinkgoParallelProcess() healthport = 8000 + GinkgoParallelProcess() - cfg.Health.Port = healthport + cfg.Health.ServerConfig.Port = healthport cfg.CacheCleanupInterval = 10 * time.Minute cfg.PolicyPollerInterval = 40 * time.Second cfg.Db = make(map[string]db.DatabaseConfig) diff --git a/src/autoscaler/metricsforwarder/config/config_test.go b/src/autoscaler/metricsforwarder/config/config_test.go index fe02004a8b..b37fcfefcd 100644 --- a/src/autoscaler/metricsforwarder/config/config_test.go +++ b/src/autoscaler/metricsforwarder/config/config_test.go @@ -68,7 +68,8 @@ db: max_idle_connections: 5 connection_max_lifetime: 60s health: - port: 9999 + server_config: + port: 9999 cred_helper_impl: default `) }) @@ -76,7 +77,7 @@ cred_helper_impl: default It("returns the config", func() { Expect(conf.Server.Port).To(Equal(8081)) Expect(conf.Logging.Level).To(Equal("debug")) - Expect(conf.Health.Port).To(Equal(9999)) + Expect(conf.Health.ServerConfig.Port).To(Equal(9999)) Expect(conf.LoggregatorConfig.MetronAddress).To(Equal("127.0.0.1:3457")) Expect(conf.Db[db.PolicyDb]).To(Equal( db.DatabaseConfig{ @@ -104,7 +105,8 @@ db: max_idle_connections: 5 connection_max_lifetime: 60s health: - port: 8081 + server_config: + port: 8081 `) }) @@ -115,7 +117,7 @@ health: Expect(conf.LoggregatorConfig.MetronAddress).To(Equal(DefaultMetronAddress)) Expect(conf.CacheTTL).To(Equal(DefaultCacheTTL)) Expect(conf.CacheCleanupInterval).To(Equal(DefaultCacheCleanupInterval)) - Expect(conf.Health.Port).To(Equal(8081)) + Expect(conf.Health.ServerConfig.Port).To(Equal(8081)) }) When("PORT env variable is set", func() { @@ -164,7 +166,8 @@ server: BeforeEach(func() { configBytes = []byte(` health: - port: port + server_config: + port: port `) }) @@ -190,7 +193,8 @@ db: max_idle_connections: 5 connection_max_lifetime: 60s health: - port: 8081 + server_config: + port: 8081 `) }) @@ -316,7 +320,7 @@ rate_limit: conf = &Config{} conf.Server.Port = 8081 conf.Logging.Level = "debug" - conf.Health.Port = 8081 + conf.Health.ServerConfig.Port = 8081 conf.LoggregatorConfig.MetronAddress = "127.0.0.1:3458" conf.LoggregatorConfig.TLS.CACertFile = "../testcerts/ca.crt" conf.LoggregatorConfig.TLS.CertFile = "../testcerts/client.crt" diff --git a/src/autoscaler/metricsforwarder/server/custom_metrics_handlers_test.go b/src/autoscaler/metricsforwarder/server/custom_metrics_handlers_test.go index 9eea0d12ef..7454721ff9 100644 --- a/src/autoscaler/metricsforwarder/server/custom_metrics_handlers_test.go +++ b/src/autoscaler/metricsforwarder/server/custom_metrics_handlers_test.go @@ -3,6 +3,7 @@ package server_test import ( "bytes" "encoding/json" + "fmt" "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" @@ -15,6 +16,7 @@ import ( "net/http" "net/http/httptest" + "net/url" "github.com/patrickmn/go-cache" ) @@ -32,7 +34,6 @@ var _ = Describe("MetricHandler", func() { metricsforwarder *fakes.FakeMetricForwarder resp *httptest.ResponseRecorder - req *http.Request err error body []byte @@ -41,6 +42,8 @@ var _ = Describe("MetricHandler", func() { found bool scalingPolicy *models.ScalingPolicy + + serverURL *url.URL ) BeforeEach(func() { @@ -53,11 +56,17 @@ var _ = Describe("MetricHandler", func() { resp = httptest.NewRecorder() handler = NewCustomMetricsHandler(logger, metricsforwarder, policyDB, allowedMetricCache) allowedMetricCache.Flush() + + serverURL, err = url.Parse(fmt.Sprintf("http://127.0.0.1:%d", conf.Server.Port)) + Expect(err).NotTo(HaveOccurred()) }) Describe("PublishMetrics", func() { JustBeforeEach(func() { - req = CreateRequest(body) + serverURL.Path = "/v1/apps/an-app-id/metrics" + req, err := http.NewRequest(http.MethodPost, serverURL.String(), bytes.NewReader(body)) + Expect(err).ToNot(HaveOccurred()) + req.Header.Add("Content-Type", "application/json") Expect(err).ToNot(HaveOccurred()) vars["appid"] = "an-app-id" handler.VerifyCredentialsAndPublishMetrics(resp, req, vars) @@ -71,7 +80,7 @@ var _ = Describe("MetricHandler", func() { }, nil) body = []byte(`{ "instance_index":0, - "test" : + "test" : "metrics":[ { "name":"custom_metric1", @@ -268,10 +277,3 @@ var _ = Describe("MetricHandler", func() { }) }) - -func CreateRequest(body []byte) *http.Request { - req, err := http.NewRequest(http.MethodPost, serverUrl+"/v1/apps/an-app-id/metrics", bytes.NewReader(body)) - Expect(err).ToNot(HaveOccurred()) - req.Header.Add("Content-Type", "application/json") - return req -} diff --git a/src/autoscaler/metricsforwarder/server/server_suite_test.go b/src/autoscaler/metricsforwarder/server/server_suite_test.go index 23dbdbcf31..7272358256 100644 --- a/src/autoscaler/metricsforwarder/server/server_suite_test.go +++ b/src/autoscaler/metricsforwarder/server/server_suite_test.go @@ -1,7 +1,6 @@ package server_test import ( - "fmt" "os" "path/filepath" "time" @@ -26,7 +25,6 @@ import ( var ( conf *config.Config serverProcess ifrit.Process - serverUrl string policyDB *fakes.FakePolicyDB rateLimiter *fakes.FakeLimiter fakeCredentials *fakes.FakeCredentials @@ -72,8 +70,10 @@ var _ = SynchronizedBeforeSuite(func() []byte { healthConfig := helpers.HealthConfig{ ReadinessCheckEnabled: true, - HealthCheckUsername: "metricsforwarderhealthcheckuser", - HealthCheckPassword: "metricsforwarderhealthcheckpassword", + BasicAuth: models.BasicAuth{ + Username: "metricsforwarderhealthcheckuser", + Password: "metricsforwarderhealthcheckpassword", + }, } conf = &config.Config{ Server: serverConfig, @@ -94,7 +94,7 @@ var _ = SynchronizedBeforeSuite(func() []byte { httpServer, err := NewServer(logger, conf, policyDB, fakeCredentials, allowedMetricCache, httpStatusCollector, rateLimiter) Expect(err).NotTo(HaveOccurred()) - serverUrl = fmt.Sprintf("http://127.0.0.1:%d", conf.Server.Port) + serverProcess = ginkgomon_v2.Invoke(httpServer) }) diff --git a/src/autoscaler/metricsforwarder/server/server_test.go b/src/autoscaler/metricsforwarder/server/server_test.go index 8c3d121249..09feffaca8 100644 --- a/src/autoscaler/metricsforwarder/server/server_test.go +++ b/src/autoscaler/metricsforwarder/server/server_test.go @@ -2,11 +2,12 @@ package server_test import ( "bytes" - "encoding/base64" "encoding/json" "errors" + "fmt" "io" "net/http" + "net/url" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" @@ -15,35 +16,17 @@ import ( . "github.com/onsi/gomega" ) -// Helper function to create a basic auth string -func basicAuth(username, password string) string { - auth := username + ":" + password - return base64.StdEncoding.EncodeToString([]byte(auth)) -} - -// Helper function to create a new request -func newRequest(method, url string, body []byte) (*http.Request, error) { - req, err := http.NewRequest(method, url, bytes.NewReader(body)) +// Helper function to set up a new client and request +func setupRequest(method string, url *url.URL, body []byte) (*http.Request, error) { + req, err := http.NewRequest(method, url.String(), bytes.NewReader(body)) if err != nil { return nil, err } + req.Header.Add("Content-Type", "application/json") return req, nil } -// Helper function to set up a new client and request -func setupRequest(method, url, authHeader string, body []byte) (*http.Client, *http.Request, error) { - client := &http.Client{} - req, err := newRequest(method, url, body) - if err != nil { - return nil, nil, err - } - if authHeader != "" { - req.Header.Add("Authorization", authHeader) - } - return client, req, nil -} - var _ = Describe("CustomMetrics Server", func() { var ( resp *http.Response @@ -52,12 +35,21 @@ var _ = Describe("CustomMetrics Server", func() { err error scalingPolicy *models.ScalingPolicy client *http.Client - authHeader string + + serverURL *url.URL + healthURL *url.URL ) BeforeEach(func() { client = &http.Client{} fakeCredentials.ValidateReturns(true, nil) + + serverURL, err = url.Parse(fmt.Sprintf("http://127.0.0.1:%d", conf.Server.Port)) + Expect(err).NotTo(HaveOccurred()) + + // health url runs on the same port as metricsforwarder, maybe we need to roll back to use original port + healthURL, err = url.Parse(fmt.Sprintf("http://127.0.0.1:%d", conf.Server.Port)) + Expect(err).NotTo(HaveOccurred()) }) When("POST /v1/apps/some-app-id/metrics", func() { @@ -79,8 +71,9 @@ var _ = Describe("CustomMetrics Server", func() { body, err = json.Marshal(models.MetricsConsumer{InstanceIndex: 0, CustomMetrics: customMetrics}) Expect(err).NotTo(HaveOccurred()) - authHeader = "Basic " + basicAuth("username", "Password") - client, req, err = setupRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", authHeader, body) + serverURL.Path = "/v1/apps/an-app-id/metrics" + req, err = setupRequest("POST", serverURL, body) + req.SetBasicAuth("username", "password") Expect(err).NotTo(HaveOccurred()) resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) @@ -98,7 +91,8 @@ var _ = Describe("CustomMetrics Server", func() { body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) Expect(err).NotTo(HaveOccurred()) - client, req, err = setupRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", "", body) + serverURL.Path = "/v1/apps/an-app-id/metrics" + req, err = setupRequest("POST", serverURL, body) Expect(err).NotTo(HaveOccurred()) resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) @@ -115,8 +109,8 @@ var _ = Describe("CustomMetrics Server", func() { body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) Expect(err).NotTo(HaveOccurred()) - authHeader = basicAuth("username", "password") - client, req, err = setupRequest("POST", serverUrl+"/v1/apps/san-app-id/metrics", authHeader, body) + serverURL.Path = "/v1/apps/an-app-id/metrics" + req, err = setupRequest("POST", serverURL, body) Expect(err).NotTo(HaveOccurred()) resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) @@ -135,8 +129,9 @@ var _ = Describe("CustomMetrics Server", func() { fakeCredentials.ValidateReturns(false, errors.New("wrong credentials")) - authHeader = "Basic " + basicAuth("invalidUsername", "invalidPassword") - client, req, err = setupRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", authHeader, body) + serverURL.Path = "/v1/apps/an-app-id/metrics" + req, err = setupRequest("POST", serverURL, body) + req.SetBasicAuth("invalidUsername", "invalidPassword") Expect(err).NotTo(HaveOccurred()) resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) @@ -153,8 +148,9 @@ var _ = Describe("CustomMetrics Server", func() { body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) Expect(err).NotTo(HaveOccurred()) - authHeader = "Basic " + basicAuth("username", "password") - client, req, err = setupRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", authHeader, body) + serverURL.Path = "/v1/apps/an-app-id/metrics" + req, err = setupRequest("POST", serverURL, body) + req.SetBasicAuth("username", "password") Expect(err).NotTo(HaveOccurred()) resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) @@ -186,8 +182,9 @@ var _ = Describe("CustomMetrics Server", func() { body, err = json.Marshal(models.MetricsConsumer{InstanceIndex: 0, CustomMetrics: customMetrics}) Expect(err).NotTo(HaveOccurred()) - authHeader = "Basic " + basicAuth("username", "password") - client, req, err = setupRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", authHeader, body) + serverURL.Path = "/v1/apps/an-app-id/metrics" + req, err = setupRequest("POST", serverURL, body) + req.SetBasicAuth("username", "password") Expect(err).NotTo(HaveOccurred()) resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) @@ -204,10 +201,16 @@ var _ = Describe("CustomMetrics Server", func() { }) When("the Health server is ready to serve RESTful API with basic Auth", func() { + var client *http.Client + + BeforeEach(func() { + healthURL.Path = "/health" + client = &http.Client{} + }) + When("username and password are incorrect for basic authentication during health check", func() { It("should return 401", func() { - client := &http.Client{} - req, err = http.NewRequest("GET", serverUrl+"/health", nil) + req, err = http.NewRequest("GET", healthURL.String(), nil) Expect(err).NotTo(HaveOccurred()) req.SetBasicAuth("wrongusername", "wrongpassword") rsp, err := client.Do(req) @@ -217,15 +220,11 @@ var _ = Describe("CustomMetrics Server", func() { }) When("username and password are correct for basic authentication during health check", func() { - BeforeEach(func() { - client = &http.Client{} - }) - When("a request to query health comes", func() { It("returns with a 200", func() { - req, err = http.NewRequest("GET", serverUrl, nil) + req, err = http.NewRequest("GET", healthURL.String(), nil) Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth(conf.Health.HealthCheckUsername, conf.Health.HealthCheckPassword) + req.SetBasicAuth(conf.Health.BasicAuth.Username, conf.Health.BasicAuth.Password) rsp, err := client.Do(req) Expect(err).NotTo(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) @@ -240,16 +239,18 @@ var _ = Describe("CustomMetrics Server", func() { }) It("should return 200 for /health", func() { - req, err = http.NewRequest("GET", serverUrl+"/health", nil) + healthURL.Path = "/health" + req, err = http.NewRequest("GET", healthURL.String(), nil) Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth(conf.Health.HealthCheckUsername, conf.Health.HealthCheckPassword) + req.SetBasicAuth(conf.Health.BasicAuth.Username, conf.Health.BasicAuth.Password) rsp, err := client.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) }) It("should return 200 for /health/readiness", func() { - req, err = http.NewRequest("GET", serverUrl+"/health/readiness", nil) + healthURL.Path = "/health/readiness" + req, err = http.NewRequest("GET", healthURL.String(), nil) Expect(err).NotTo(HaveOccurred()) rsp, err := client.Do(req) Expect(err).ToNot(HaveOccurred()) From a4ff5eca03819e3c1a28da1d65e61203bf88b68b Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:36:46 +0200 Subject: [PATCH 02/39] Add new files for autoscaler API and security, refactor scaling engine server and tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Add generate.go for autoscaler API generation • Create ogen-config.yaml to allow remote parser configuration • Introduce security.go with SecuritySource struct • Refactor main.go to remove Prometheus and health endpoint dependencies • Update scalingengine_suite_test.go and scalingengine_test.go to reflect new health server configuration and client creation • Modify config_test.go and valid.yml to adjust health server port configuration • Implement scaling_history_handler.go without bearer auth for internal mTLS use • Adjust scaling_history_handler_test.go to use new package path • Overhaul server.go to separate health and mTLS server creation, and to streamline router setup • Amend server_test.go to align with server refactoring and use dynamic URL construction --- src/autoscaler/scalingengine/apis/generate.go | 3 + .../scalingengine/apis/ogen-config.yaml | 2 + .../scalingengine/client/security.go | 4 + .../scalingengine/cmd/scalingengine/main.go | 17 +- .../scalingengine/scalingengine_suite_test.go | 8 +- .../cmd/scalingengine/scalingengine_test.go | 69 ++++--- .../scalingengine/config/config_test.go | 7 +- .../scalingengine/config/testdata/valid.yml | 5 +- .../server/scaling_history_handler.go | 13 +- .../server/scaling_history_handler_test.go | 2 +- src/autoscaler/scalingengine/server/server.go | 104 +++++++++- .../scalingengine/server/server_test.go | 180 +++++++----------- 12 files changed, 233 insertions(+), 181 deletions(-) create mode 100644 src/autoscaler/scalingengine/apis/generate.go create mode 100644 src/autoscaler/scalingengine/apis/ogen-config.yaml create mode 100644 src/autoscaler/scalingengine/client/security.go diff --git a/src/autoscaler/scalingengine/apis/generate.go b/src/autoscaler/scalingengine/apis/generate.go new file mode 100644 index 0000000000..532c7b1a6d --- /dev/null +++ b/src/autoscaler/scalingengine/apis/generate.go @@ -0,0 +1,3 @@ +package apis + +//go:generate go run github.com/ogen-go/ogen/cmd/ogen --config ogen-config.yaml --package scalinghistory --target scalinghistory --clean ../../../../api/internal-scaling-history-api.openapi.yaml diff --git a/src/autoscaler/scalingengine/apis/ogen-config.yaml b/src/autoscaler/scalingengine/apis/ogen-config.yaml new file mode 100644 index 0000000000..5b85450eaa --- /dev/null +++ b/src/autoscaler/scalingengine/apis/ogen-config.yaml @@ -0,0 +1,2 @@ +parser: + allow_remote: true diff --git a/src/autoscaler/scalingengine/client/security.go b/src/autoscaler/scalingengine/client/security.go new file mode 100644 index 0000000000..244f27088f --- /dev/null +++ b/src/autoscaler/scalingengine/client/security.go @@ -0,0 +1,4 @@ +package client + +type SecuritySource struct { +} diff --git a/src/autoscaler/scalingengine/cmd/scalingengine/main.go b/src/autoscaler/scalingengine/cmd/scalingengine/main.go index 86a66994c7..91679fbc6d 100644 --- a/src/autoscaler/scalingengine/cmd/scalingengine/main.go +++ b/src/autoscaler/scalingengine/cmd/scalingengine/main.go @@ -4,11 +4,9 @@ import ( "flag" "fmt" "os" - "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cf" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db/sqldb" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/healthendpoint" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/config" @@ -16,7 +14,6 @@ import ( "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/server" "code.cloudfoundry.org/clock" "code.cloudfoundry.org/lager/v3" - "github.com/prometheus/client_golang/prometheus" "github.com/tedsuo/ifrit" "github.com/tedsuo/ifrit/grouper" "github.com/tedsuo/ifrit/sigmon" @@ -79,25 +76,17 @@ func main() { } defer func() { _ = schedulerDB.Close() }() - httpStatusCollector := healthendpoint.NewHTTPStatusCollector("autoscaler", "scalingengine") - promRegistry := prometheus.NewRegistry() - healthendpoint.RegisterCollectors(promRegistry, []prometheus.Collector{ - healthendpoint.NewDatabaseStatusCollector("autoscaler", "scalingengine", "policyDB", policyDb), - healthendpoint.NewDatabaseStatusCollector("autoscaler", "scalingengine", "scalingengineDB", scalingEngineDB), - healthendpoint.NewDatabaseStatusCollector("autoscaler", "scalingengine", "schedulerDB", schedulerDB), - httpStatusCollector, - }, true, logger.Session("scalingengine-prometheus")) - scalingEngine := scalingengine.NewScalingEngine(logger, cfClient, policyDb, scalingEngineDB, eClock, conf.DefaultCoolDownSecs, conf.LockSize) synchronizer := schedule.NewActiveScheduleSychronizer(logger, schedulerDB, scalingEngineDB, scalingEngine) - httpServer, err := server.NewServer(logger.Session("http-server"), conf, scalingEngineDB, scalingEngine, synchronizer, httpStatusCollector) + server := server.NewServer(logger.Session("http-server"), conf, policyDb, scalingEngineDB, schedulerDB, scalingEngine, synchronizer) + httpServer, err := server.GetMtlsServer() if err != nil { logger.Error("failed to create http server", err) os.Exit(1) } - healthServer, err := healthendpoint.NewServerWithBasicAuth(conf.Health, []healthendpoint.Checker{}, logger.Session("health-server"), promRegistry, time.Now) + healthServer, err := server.GetHealthServer() if err != nil { logger.Error("failed to create health server", err) os.Exit(1) diff --git a/src/autoscaler/scalingengine/cmd/scalingengine/scalingengine_suite_test.go b/src/autoscaler/scalingengine/cmd/scalingengine/scalingengine_suite_test.go index c728d943a0..302c7426da 100644 --- a/src/autoscaler/scalingengine/cmd/scalingengine/scalingengine_suite_test.go +++ b/src/autoscaler/scalingengine/cmd/scalingengine/scalingengine_suite_test.go @@ -78,7 +78,7 @@ var _ = SynchronizedBeforeSuite( conf.Server.TLS.KeyFile = filepath.Join(testCertDir, "scalingengine.key") conf.Server.TLS.CertFile = filepath.Join(testCertDir, "scalingengine.crt") conf.Server.TLS.CACertFile = filepath.Join(testCertDir, "autoscaler-ca.crt") - conf.Health.Port = healthport + conf.Health.ServerConfig.Port = healthport conf.Logging.Level = "debug" dbUrl := GetDbUrl() @@ -105,8 +105,8 @@ var _ = SynchronizedBeforeSuite( conf.LockSize = 32 conf.HttpClientTimeout = 10 * time.Second - conf.Health.HealthCheckUsername = "scalingenginehealthcheckuser" - conf.Health.HealthCheckPassword = "scalingenginehealthcheckpassword" + conf.Health.BasicAuth.Username = "scalingenginehealthcheckuser" + conf.Health.BasicAuth.Password = "scalingenginehealthcheckpassword" configFile = writeConfig(&conf) @@ -137,7 +137,7 @@ var _ = SynchronizedBeforeSuite( _, err = testDB.Exec(testDB.Rebind("INSERT INTO policy_json(app_id, policy_json, guid) values(?, ?, ?)"), appId, policy, "1234") FailOnError("insert failed", err) - httpClient = NewEventGeneratorClient() + httpClient = NewScalingEngineClient() healthHttpClient = &http.Client{} }) diff --git a/src/autoscaler/scalingengine/cmd/scalingengine/scalingengine_test.go b/src/autoscaler/scalingengine/cmd/scalingengine/scalingengine_test.go index 9c19c16ecc..cc94c100b9 100644 --- a/src/autoscaler/scalingengine/cmd/scalingengine/scalingengine_test.go +++ b/src/autoscaler/scalingengine/cmd/scalingengine/scalingengine_test.go @@ -2,6 +2,7 @@ package main_test import ( "io" + "strconv" "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cf" @@ -17,17 +18,24 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" "os" ) var _ = Describe("Main", func() { - var ( runner *ScalingEngineRunner + err error + + healthURL *url.URL + serverURL *url.URL ) BeforeEach(func() { runner = NewScalingEngineRunner() + serverURL, err = url.Parse("https://127.0.0.1:" + strconv.Itoa(conf.Server.Port)) + healthURL, err = url.Parse("http://127.0.0.1:" + strconv.Itoa(conf.Health.ServerConfig.Port)) + Expect(err).ToNot(HaveOccurred()) }) JustBeforeEach(func() { @@ -39,7 +47,6 @@ var _ = Describe("Main", func() { }) Describe("with a correct config", func() { - Context("when starting 1 scaling engine instance", func() { It("scaling engine should start", func() { Eventually(runner.Session.Buffer, 2*time.Second).Should(gbytes.Say(runner.startCheck)) @@ -49,10 +56,6 @@ var _ = Describe("Main", func() { It("http server starts directly", func() { Eventually(runner.Session.Buffer, 2*time.Second).Should(gbytes.Say("scalingengine.http-server.new-http-server")) }) - - It("health server starts directly", func() { - Eventually(runner.Session.Buffer, 2*time.Second).Should(gbytes.Say("scalingengine.health-server.new-http-server")) - }) }) Context("when starting multiple scaling engine instances", func() { @@ -65,7 +68,7 @@ var _ = Describe("Main", func() { secondConf := conf secondConf.Server.Port += 500 - secondConf.Health.Port += 500 + secondConf.Health.ServerConfig.Port += 500 secondRunner.configPath = writeConfig(&secondConf).Name() secondRunner.Start() }) @@ -160,9 +163,10 @@ var _ = Describe("Main", func() { body, err := json.Marshal(models.Trigger{Adjustment: "+1"}) Expect(err).NotTo(HaveOccurred()) - rsp, err := httpClient.Post(fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/scale", port, appId), - "application/json", bytes.NewReader(body)) + serverURL.Path = fmt.Sprintf("/v1/apps/%s/scale", appId) + rsp, err := httpClient.Post(serverURL.String(), "application/json", bytes.NewReader(body)) Expect(err).NotTo(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusOK)) rsp.Body.Close() }) @@ -170,9 +174,9 @@ var _ = Describe("Main", func() { Context("when a request to retrieve scaling history comes", func() { It("returns with a 200", func() { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/scaling_histories", port, appId), nil) + serverURL.Path = fmt.Sprintf("/v1/apps/%s/scaling_histories", appId) + req, err := http.NewRequest(http.MethodGet, serverURL.String(), nil) Expect(err).NotTo(HaveOccurred()) - req.Header.Set("Authorization", "Bearer none") rsp, err := httpClient.Do(req) Expect(err).NotTo(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) @@ -182,10 +186,11 @@ var _ = Describe("Main", func() { It("handles the start and end of a schedule", func() { By("start of a schedule") - url := fmt.Sprintf("https://127.0.0.1:%d/v1/apps/%s/active_schedules/111111", port, appId) + serverURL.Path = fmt.Sprintf("/v1/apps/%s/active_schedules/111111", appId) + bodyReader := bytes.NewReader([]byte(`{"instance_min_count":1, "instance_max_count":5, "initial_min_instance_count":3}`)) - req, err := http.NewRequest(http.MethodPut, url, bodyReader) + req, err := http.NewRequest(http.MethodPut, serverURL.String(), bodyReader) Expect(err).NotTo(HaveOccurred()) rsp, err := httpClient.Do(req) @@ -194,7 +199,7 @@ var _ = Describe("Main", func() { rsp.Body.Close() By("end of a schedule") - req, err = http.NewRequest(http.MethodDelete, url, nil) + req, err = http.NewRequest(http.MethodDelete, serverURL.String(), nil) Expect(err).NotTo(HaveOccurred()) rsp, err = httpClient.Do(req) @@ -205,11 +210,10 @@ var _ = Describe("Main", func() { }) Describe("when Health server is ready to serve RESTful API", func() { - BeforeEach(func() { basicAuthConfig := conf - basicAuthConfig.Health.HealthCheckUsername = "" - basicAuthConfig.Health.HealthCheckPassword = "" + basicAuthConfig.Health.BasicAuth.Username = "" + basicAuthConfig.Health.BasicAuth.Password = "" runner.configPath = writeConfig(&basicAuthConfig).Name() }) @@ -219,7 +223,7 @@ var _ = Describe("Main", func() { Context("when a request to query health comes", func() { It("returns with a 200", func() { - rsp, err := healthHttpClient.Get(fmt.Sprintf("http://127.0.0.1:%d", healthport)) + rsp, err := httpClient.Get(healthURL.String()) Expect(err).NotTo(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) raw, _ := io.ReadAll(rsp.Body) @@ -237,19 +241,22 @@ var _ = Describe("Main", func() { }) Describe("when Health server is ready to serve RESTful API with basic Auth", func() { + BeforeEach(func() { + healthURL.Path = "/health" + }) + JustBeforeEach(func() { Eventually(runner.Session.Buffer, 2).Should(gbytes.Say("scalingengine.started")) }) Context("when username and password are incorrect for basic authentication during health check", func() { It("should return 401", func() { - - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) + req, err := http.NewRequest(http.MethodGet, healthURL.String(), nil) Expect(err).NotTo(HaveOccurred()) req.SetBasicAuth("wrongusername", "wrongpassword") - rsp, err := healthHttpClient.Do(req) + rsp, err := httpClient.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusUnauthorized)) }) @@ -258,12 +265,12 @@ var _ = Describe("Main", func() { Context("when username and password are correct for basic authentication during health check", func() { It("should return 200", func() { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) + req, err := http.NewRequest(http.MethodGet, healthURL.String(), nil) Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth(conf.Health.HealthCheckUsername, conf.Health.HealthCheckPassword) + req.SetBasicAuth(conf.Health.BasicAuth.Username, conf.Health.BasicAuth.Password) - rsp, err := healthHttpClient.Do(req) + rsp, err := httpClient.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) }) @@ -271,6 +278,10 @@ var _ = Describe("Main", func() { }) Describe("when Health server is ready to serve RESTful API with basic Auth", func() { + BeforeEach(func() { + healthURL.Path = "/health" + }) + JustBeforeEach(func() { Eventually(runner.Session.Buffer, 2).Should(gbytes.Say("scalingengine.started")) }) @@ -278,12 +289,12 @@ var _ = Describe("Main", func() { Context("when username and password are incorrect for basic authentication during health check", func() { It("should return 401", func() { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) + req, err := http.NewRequest(http.MethodGet, healthURL.String(), nil) Expect(err).NotTo(HaveOccurred()) req.SetBasicAuth("wrongusername", "wrongpassword") - rsp, err := healthHttpClient.Do(req) + rsp, err := httpClient.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusUnauthorized)) }) @@ -292,12 +303,12 @@ var _ = Describe("Main", func() { Context("when username and password are correct for basic authentication during health check", func() { It("should return 200", func() { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) + req, err := http.NewRequest(http.MethodGet, healthURL.String(), nil) Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth(conf.Health.HealthCheckUsername, conf.Health.HealthCheckPassword) + req.SetBasicAuth(conf.Health.BasicAuth.Username, conf.Health.BasicAuth.Password) - rsp, err := healthHttpClient.Do(req) + rsp, err := httpClient.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) }) diff --git a/src/autoscaler/scalingengine/config/config_test.go b/src/autoscaler/scalingengine/config/config_test.go index eab991c81e..50aaa06eae 100644 --- a/src/autoscaler/scalingengine/config/config_test.go +++ b/src/autoscaler/scalingengine/config/config_test.go @@ -55,7 +55,7 @@ var _ = Describe("Config", func() { Expect(conf.Server.TLS.CertFile).To(Equal("/var/vcap/jobs/autoscaler/config/certs/server.crt")) Expect(conf.Server.TLS.CACertFile).To(Equal("/var/vcap/jobs/autoscaler/config/certs/ca.crt")) - Expect(conf.Health.Port).To(Equal(9999)) + Expect(conf.Health.ServerConfig.Port).To(Equal(9999)) Expect(conf.Logging.Level).To(Equal("debug")) Expect(conf.DB.PolicyDB).To(Equal( @@ -98,7 +98,7 @@ var _ = Describe("Config", func() { Expect(conf.CF.SkipSSLValidation).To(Equal(false)) Expect(conf.Server.Port).To(Equal(8080)) - Expect(conf.Health.Port).To(Equal(8081)) + Expect(conf.Health.ServerConfig.Port).To(Equal(8081)) Expect(conf.Logging.Level).To(Equal("info")) Expect(conf.DB.PolicyDB).To(Equal( db.DatabaseConfig{ @@ -144,7 +144,8 @@ server: BeforeEach(func() { configBytes = []byte(` health: - port: port + server_config: + port: port `) }) diff --git a/src/autoscaler/scalingengine/config/testdata/valid.yml b/src/autoscaler/scalingengine/config/testdata/valid.yml index 2aef197852..487d50b22c 100644 --- a/src/autoscaler/scalingengine/config/testdata/valid.yml +++ b/src/autoscaler/scalingengine/config/testdata/valid.yml @@ -10,7 +10,8 @@ server: cert_file: /var/vcap/jobs/autoscaler/config/certs/server.crt ca_file: /var/vcap/jobs/autoscaler/config/certs/ca.crt health: - port: 9999 + server_config: + port: 9999 logging: level: DeBug @@ -32,4 +33,4 @@ db: connection_max_lifetime: 60s defaultCoolDownSecs: 300 lockSize: 32 -http_client_timeout: 10s \ No newline at end of file +http_client_timeout: 10s diff --git a/src/autoscaler/scalingengine/server/scaling_history_handler.go b/src/autoscaler/scalingengine/server/scaling_history_handler.go index cdbf114df8..80f91a4655 100644 --- a/src/autoscaler/scalingengine/server/scaling_history_handler.go +++ b/src/autoscaler/scalingengine/server/scaling_history_handler.go @@ -12,17 +12,15 @@ import ( "github.com/ogen-go/ogen/ogenerrors" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers/apis/scalinghistory" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/routes" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/apis/scalinghistory" "code.cloudfoundry.org/lager/v3" "net/http" ) -var ( - _ = scalinghistory.SecurityHandler(&ScalingHistoryHandler{}) -) +type SecuritySource struct{} type ScalingHistoryHandler struct { logger lager.Logger @@ -45,7 +43,7 @@ func (h *ScalingHistoryHandler) NewError(_ context.Context, err error) *scalingh result.SetStatusCode(http.StatusUnauthorized) result.SetResponse(scalinghistory.ErrorResponse{ Code: scalinghistory.NewOptString(http.StatusText(http.StatusUnauthorized)), - Message: scalinghistory.NewOptString("missing bearer authentication"), + Message: scalinghistory.NewOptString("missing authentication"), }) } else { result.SetStatusCode(http.StatusInternalServerError) @@ -57,11 +55,6 @@ func (h *ScalingHistoryHandler) NewError(_ context.Context, err error) *scalingh return result } -func (h *ScalingHistoryHandler) HandleBearerAuth(ctx context.Context, operationName string, t scalinghistory.BearerAuth) (context.Context, error) { - // This handler is a no-op, as this handler shall only be available internally via mTLS - return ctx, nil -} - func (h *ScalingHistoryHandler) V1AppsGUIDScalingHistoriesGet(ctx context.Context, params scalinghistory.V1AppsGUIDScalingHistoriesGetParams) (*scalinghistory.History, error) { appId := params.GUID // actually not necessary if a default is provided in the schema, however this is not exposed yet: diff --git a/src/autoscaler/scalingengine/server/scaling_history_handler_test.go b/src/autoscaler/scalingengine/server/scaling_history_handler_test.go index 7b6fb582b8..9e1666f9df 100644 --- a/src/autoscaler/scalingengine/server/scaling_history_handler_test.go +++ b/src/autoscaler/scalingengine/server/scaling_history_handler_test.go @@ -8,7 +8,7 @@ import ( "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" "code.cloudfoundry.org/lager/v3/lagertest" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers/apis/scalinghistory" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/apis/scalinghistory" . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/server" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" diff --git a/src/autoscaler/scalingengine/server/server.go b/src/autoscaler/scalingengine/server/server.go index b9133179d1..a6325e8667 100644 --- a/src/autoscaler/scalingengine/server/server.go +++ b/src/autoscaler/scalingengine/server/server.go @@ -1,17 +1,20 @@ package server import ( + "time" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/healthendpoint" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers/apis/scalinghistory" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/routes" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/apis/scalinghistory" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/schedule" "code.cloudfoundry.org/lager/v3" "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" "github.com/tedsuo/ifrit" "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" @@ -26,15 +29,89 @@ func (vh VarsFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { vh(w, r, vars) } -func NewServer(logger lager.Logger, conf *config.Config, scalingEngineDB db.ScalingEngineDB, scalingEngine scalingengine.ScalingEngine, synchronizer schedule.ActiveScheduleSychronizer, httpStatusCollector healthendpoint.HTTPStatusCollector) (ifrit.Runner, error) { - handler := NewScalingHandler(logger, scalingEngineDB, scalingEngine) - syncHandler := NewSyncHandler(logger, synchronizer) +type Server struct { + logger lager.Logger + conf *config.Config + policyDB db.PolicyDB + scalingEngineDB db.ScalingEngineDB + schedulerDB db.SchedulerDB + scalingEngine scalingengine.ScalingEngine + synchronizer schedule.ActiveScheduleSychronizer +} + +func NewServer(logger lager.Logger, conf *config.Config, policyDB db.PolicyDB, scalingEngineDB db.ScalingEngineDB, schedulerDB db.SchedulerDB, scalingEngine scalingengine.ScalingEngine, synchronizer schedule.ActiveScheduleSychronizer) *Server { + return &Server{ + logger: logger, + conf: conf, + policyDB: policyDB, + scalingEngineDB: scalingEngineDB, + schedulerDB: schedulerDB, + scalingEngine: scalingEngine, + synchronizer: synchronizer, + } +} + +func (s *Server) GetHealthServer() (ifrit.Runner, error) { + httpStatusCollector := healthendpoint.NewHTTPStatusCollector("autoscaler", "scalingengine") + healthRouter, err := createHealthRouter(s.logger, s.conf, s.policyDB, s.scalingEngineDB, s.schedulerDB, httpStatusCollector) + if err != nil { + return nil, fmt.Errorf("failed to create health router: %w", err) + } + + return helpers.NewHTTPServer(s.logger, s.conf.Health.ServerConfig, healthRouter) +} + +func (s *Server) GetMtlsServer() (ifrit.Runner, error) { + httpStatusCollector := healthendpoint.NewHTTPStatusCollector("autoscaler", "scalingengine") + scalingEngineRouter, err := createScalingEngineRouter(s.logger, s.scalingEngineDB, s.scalingEngine, s.synchronizer, httpStatusCollector, s.conf.Server) + if err != nil { + return nil, fmt.Errorf("failed to create scaling engine router: %w", err) + } + + // mainRouter := setupMainRouter(scalingEngineRouter, healthRouter) + + return helpers.NewHTTPServer(s.logger, s.conf.Server, scalingEngineRouter) +} + +func createPrometheusRegistry(policyDB db.PolicyDB, scalingEngineDB db.ScalingEngineDB, schedulerDB db.SchedulerDB, httpStatusCollector healthendpoint.HTTPStatusCollector, logger lager.Logger) *prometheus.Registry { + promRegistry := prometheus.NewRegistry() + //validate that db are not nil + + if policyDB == nil || scalingEngineDB == nil || schedulerDB == nil { + logger.Error("failed-to-create-prometheus-registry", fmt.Errorf("db is nil: have policyDB: %t, have scalingEngineDB: %t, have schedulerDB: %t", policyDB != nil, scalingEngineDB != nil, schedulerDB != nil)) + return promRegistry + } + + healthendpoint.RegisterCollectors(promRegistry, []prometheus.Collector{ + healthendpoint.NewDatabaseStatusCollector("autoscaler", "scalingengine", "policyDB", policyDB), + healthendpoint.NewDatabaseStatusCollector("autoscaler", "scalingengine", "scalingengineDB", scalingEngineDB), + healthendpoint.NewDatabaseStatusCollector("autoscaler", "scalingengine", "schedulerDB", schedulerDB), + httpStatusCollector, + }, true, logger.Session("scalingengine-prometheus")) + return promRegistry +} + +func createHealthRouter(logger lager.Logger, conf *config.Config, policyDB db.PolicyDB, scalingEngineDB db.ScalingEngineDB, schedulerDB db.SchedulerDB, httpStatusCollector healthendpoint.HTTPStatusCollector) (*mux.Router, error) { + checkers := []healthendpoint.Checker{} + gatherer := createPrometheusRegistry(policyDB, scalingEngineDB, schedulerDB, httpStatusCollector, logger) + healthRouter, err := healthendpoint.NewHealthRouter(conf.Health, checkers, logger.Session("health-server"), gatherer, time.Now) + if err != nil { + return nil, fmt.Errorf("failed to create health router: %w", err) + } + return healthRouter, nil +} + +func createScalingEngineRouter(logger lager.Logger, scalingEngineDB db.ScalingEngineDB, scalingEngine scalingengine.ScalingEngine, synchronizer schedule.ActiveScheduleSychronizer, httpStatusCollector healthendpoint.HTTPStatusCollector, serverConfig helpers.ServerConfig) (*mux.Router, error) { httpStatusCollectMiddleware := healthendpoint.NewHTTPStatusCollectMiddleware(httpStatusCollector) + + se := NewScalingHandler(logger, scalingEngineDB, scalingEngine) + syncHandler := NewSyncHandler(logger, synchronizer) + r := routes.ScalingEngineRoutes() r.Use(otelmux.Middleware("scalingengine")) r.Use(httpStatusCollectMiddleware.Collect) - r.Get(routes.ScaleRouteName).Handler(VarsFunc(handler.Scale)) + r.Get(routes.ScaleRouteName).Handler(VarsFunc(se.Scale)) scalingHistoryHandler, err := newScalingHistoryHandler(logger, scalingEngineDB) if err != nil { @@ -42,13 +119,20 @@ func NewServer(logger lager.Logger, conf *config.Config, scalingEngineDB db.Scal } r.Get(routes.GetScalingHistoriesRouteName).Handler(scalingHistoryHandler) - r.Get(routes.SetActiveScheduleRouteName).Handler(VarsFunc(handler.StartActiveSchedule)) - r.Get(routes.DeleteActiveScheduleRouteName).Handler(VarsFunc(handler.RemoveActiveSchedule)) - r.Get(routes.GetActiveSchedulesRouteName).Handler(VarsFunc(handler.GetActiveSchedule)) + r.Get(routes.SetActiveScheduleRouteName).Handler(VarsFunc(se.StartActiveSchedule)) + r.Get(routes.DeleteActiveScheduleRouteName).Handler(VarsFunc(se.RemoveActiveSchedule)) + r.Get(routes.GetActiveSchedulesRouteName).Handler(VarsFunc(se.GetActiveSchedule)) r.Get(routes.SyncActiveSchedulesRouteName).Handler(VarsFunc(syncHandler.Sync)) + return r, nil +} - return helpers.NewHTTPServer(logger, conf.Server, r) +func setupMainRouter(r *mux.Router, healthRouter *mux.Router) *mux.Router { + mainRouter := mux.NewRouter() + mainRouter.PathPrefix("/v1").Handler(r) + mainRouter.PathPrefix("/health").Handler(healthRouter) + mainRouter.PathPrefix("/").Handler(healthRouter) + return mainRouter } func newScalingHistoryHandler(logger lager.Logger, scalingEngineDB db.ScalingEngineDB) (http.Handler, error) { @@ -56,7 +140,7 @@ func newScalingHistoryHandler(logger lager.Logger, scalingEngineDB db.ScalingEng if err != nil { return nil, fmt.Errorf("error creating scaling history handler: %w", err) } - server, err := scalinghistory.NewServer(scalingHistoryHandler, scalingHistoryHandler) + server, err := scalinghistory.NewServer(scalingHistoryHandler) if err != nil { return nil, fmt.Errorf("error creating ogen scaling history server: %w", err) } diff --git a/src/autoscaler/scalingengine/server/server_test.go b/src/autoscaler/scalingengine/server/server_test.go index d3fbff3192..f36e331baf 100644 --- a/src/autoscaler/scalingengine/server/server_test.go +++ b/src/autoscaler/scalingengine/server/server_test.go @@ -1,6 +1,8 @@ package server_test import ( + "strconv" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" @@ -15,47 +17,20 @@ import ( "bytes" "encoding/json" - "fmt" "io" "net/http" + "net/url" ) -var ( - server ifrit.Process - serverUrl string - scalingEngineDB *fakes.FakeScalingEngineDB - sychronizer *fakes.FakeActiveScheduleSychronizer - httpStatusCollector *fakes.FakeHTTPStatusCollector -) - -var _ = SynchronizedBeforeSuite(func() []byte { - return nil -}, func(_ []byte) { - port := 2222 + GinkgoParallelProcess() - conf := &config.Config{ - Server: helpers.ServerConfig{ - Port: port, - }, - } - scalingEngineDB = &fakes.FakeScalingEngineDB{} - scalingEngine := &fakes.FakeScalingEngine{} - sychronizer = &fakes.FakeActiveScheduleSychronizer{} - httpStatusCollector = &fakes.FakeHTTPStatusCollector{} - - httpServer, err := NewServer(lager.NewLogger("test"), conf, scalingEngineDB, scalingEngine, sychronizer, httpStatusCollector) - Expect(err).NotTo(HaveOccurred()) - server = ginkgomon_v2.Invoke(httpServer) - serverUrl = fmt.Sprintf("http://127.0.0.1:%d", conf.Server.Port) -}) - -var _ = SynchronizedAfterSuite(func() { - ginkgomon_v2.Interrupt(server) -}, func() { -}) - var _ = Describe("Server", func() { var ( - urlPath string + serverUrl *url.URL + server ifrit.Process + scalingEngineDB *fakes.FakeScalingEngineDB + sychronizer *fakes.FakeActiveScheduleSychronizer + + conf *config.Config + rsp *http.Response req *http.Request body []byte @@ -66,22 +41,48 @@ var _ = Describe("Server", func() { ) BeforeEach(func() { + port := 2222 + GinkgoParallelProcess() + conf = &config.Config{ + Server: helpers.ServerConfig{ + Port: port, + }, + } + scalingEngineDB = &fakes.FakeScalingEngineDB{} + scalingEngine := &fakes.FakeScalingEngine{} + policyDb := &fakes.FakePolicyDB{} + schedulerDB := &fakes.FakeSchedulerDB{} + sychronizer = &fakes.FakeActiveScheduleSychronizer{} + + httpServer, err := NewServer(lager.NewLogger("test"), conf, policyDb, scalingEngineDB, schedulerDB, scalingEngine, sychronizer).GetMtlsServer() + Expect(err).NotTo(HaveOccurred()) + server = ginkgomon_v2.Invoke(httpServer) + serverUrl, err = url.Parse("http://127.0.0.1:" + strconv.Itoa(port)) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + ginkgomon_v2.Interrupt(server) + }) + JustBeforeEach(func() { + req, err = http.NewRequest(method, serverUrl.String(), bodyReader) + Expect(err).NotTo(HaveOccurred()) + rsp, err = http.DefaultClient.Do(req) }) - Context("when triggering scaling action", func() { + When("triggering scaling action", func() { BeforeEach(func() { body, err = json.Marshal(models.Trigger{Adjustment: "+1"}) Expect(err).NotTo(HaveOccurred()) + bodyReader = bytes.NewReader(body) uPath, err := route.Get(routes.ScaleRouteName).URLPath("appid", "test-app-id") Expect(err).NotTo(HaveOccurred()) - urlPath = uPath.Path + serverUrl.Path = uPath.Path }) - Context("when requesting correctly", func() { - JustBeforeEach(func() { - rsp, err = http.Post(serverUrl+urlPath, "application/json", bytes.NewReader(body)) + When("requesting correctly", func() { + BeforeEach(func() { + method = http.MethodPost }) It("should return 200", func() { @@ -90,76 +91,44 @@ var _ = Describe("Server", func() { rsp.Body.Close() }) }) - - Context("when requesting the wrong path", func() { - JustBeforeEach(func() { - rsp, err = http.Post(serverUrl+"/not-exist-path", "application/json", bytes.NewReader(body)) - }) - - It("should return 404", func() { - Expect(err).ToNot(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusNotFound)) - rsp.Body.Close() - }) - }) - }) - Context("when getting scaling histories", func() { + When("getting scaling histories", func() { BeforeEach(func() { uPath, err := route.Get(routes.GetScalingHistoriesRouteName).URLPath("guid", "8ea70e4e-e0bc-4e15-9d32-cd69daaf012a") Expect(err).NotTo(HaveOccurred()) - urlPath = uPath.Path + method = http.MethodGet + serverUrl.Path = uPath.Path }) - Context("when requesting correctly", func() { - JustBeforeEach(func() { - req, err = http.NewRequest(http.MethodGet, serverUrl+urlPath, nil) - req.Header.Set("Authorization", "Bearer ignore") - Expect(err).NotTo(HaveOccurred()) - rsp, err = (&http.Client{}).Do(req) - }) + JustBeforeEach(func() { + req, err = http.NewRequest(method, serverUrl.String(), nil) + Expect(err).NotTo(HaveOccurred()) - It("should return 200", func() { - Expect(err).ToNot(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusOK)) - rsp.Body.Close() - }) }) - Context("when requesting the wrong path", func() { - JustBeforeEach(func() { - rsp, err = http.Get(serverUrl + "/not-exist-path") - }) - - It("should return 404", func() { - Expect(err).ToNot(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusNotFound)) - rsp.Body.Close() - }) + It("should return 200", func() { + Expect(err).ToNot(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusOK)) + rsp.Body.Close() }) }) - Context("when requesting active shedule", func() { + When("requesting active shedule", func() { - JustBeforeEach(func() { - req, err = http.NewRequest(method, serverUrl+urlPath, bodyReader) + BeforeEach(func() { + uPath, err := route.Get(routes.SetActiveScheduleRouteName).URLPath("appid", "test-app-id", "scheduleid", "test-schedule-id") Expect(err).NotTo(HaveOccurred()) - rsp, err = http.DefaultClient.Do(req) + serverUrl.Path = uPath.Path + method = http.MethodPut }) - Context("when setting active schedule", func() { + When("setting active schedule", func() { BeforeEach(func() { - uPath, err := route.Get(routes.SetActiveScheduleRouteName).URLPath("appid", "test-app-id", "scheduleid", "test-schedule-id") - Expect(err).NotTo(HaveOccurred()) - urlPath = uPath.Path bodyReader = bytes.NewReader([]byte(`{"instance_min_count":1, "instance_max_count":5, "initial_min_instance_count":3}`)) }) - Context("when requesting correctly", func() { - BeforeEach(func() { - method = http.MethodPut - }) + When("credentials are correct", func() { It("should return 200", func() { Expect(err).ToNot(HaveOccurred()) @@ -170,8 +139,7 @@ var _ = Describe("Server", func() { Context("when requesting the wrong path", func() { BeforeEach(func() { - method = http.MethodPut - urlPath = "/not-exist" + serverUrl.Path = "/not-exist" }) It("should return 404", func() { @@ -182,15 +150,16 @@ var _ = Describe("Server", func() { }) }) - Context("when deleting active schedule", func() { + When("deleting active schedule", func() { BeforeEach(func() { uPath, err := route.Get(routes.DeleteActiveScheduleRouteName).URLPath("appid", "test-app-id", "scheduleid", "test-schedule-id") Expect(err).NotTo(HaveOccurred()) - urlPath = uPath.Path + serverUrl.Path = uPath.Path bodyReader = nil method = http.MethodDelete }) - Context("when requesting correctly", func() { + + When("requesting correctly", func() { It("should return 200", func() { Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) @@ -200,7 +169,7 @@ var _ = Describe("Server", func() { Context("when requesting the wrong path", func() { BeforeEach(func() { - urlPath = "/not-exist" + serverUrl.Path = "/not-exist" }) It("should return 404", func() { @@ -211,16 +180,16 @@ var _ = Describe("Server", func() { }) }) - Context("when getting active schedule", func() { + When("getting active schedule", func() { BeforeEach(func() { uPath, err := route.Get(routes.GetActiveSchedulesRouteName).URLPath("appid", "test-app-id") Expect(err).NotTo(HaveOccurred()) - urlPath = uPath.Path + serverUrl.Path = uPath.Path bodyReader = nil method = http.MethodGet }) - Context("when requesting correctly", func() { + When("requesting correctly", func() { BeforeEach(func() { activeSchedule := &models.ActiveSchedule{ ScheduleId: "a-schedule-id", @@ -241,20 +210,15 @@ var _ = Describe("Server", func() { }) }) - Context("when requesting sync shedule", func() { - JustBeforeEach(func() { + When("requesting sync shedule", func() { + BeforeEach(func() { uPath, err := route.Get(routes.SyncActiveSchedulesRouteName).URLPath() Expect(err).NotTo(HaveOccurred()) - urlPath = uPath.Path + serverUrl.Path = uPath.Path bodyReader = nil - - req, err = http.NewRequest(method, serverUrl+urlPath, bodyReader) - Expect(err).NotTo(HaveOccurred()) - rsp, err = http.DefaultClient.Do(req) - Expect(err).NotTo(HaveOccurred()) }) - Context("when requesting correctly", func() { + When("requesting correctly", func() { BeforeEach(func() { method = http.MethodPut }) @@ -267,7 +231,7 @@ var _ = Describe("Server", func() { }) }) - Context("when requesting with incorrect http method", func() { + When("requesting with incorrect http method", func() { BeforeEach(func() { method = http.MethodGet }) From fa178f43dca16ca9e3d398182a79299af7409f0e Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:37:23 +0200 Subject: [PATCH 03/39] Remove scaling engine client and refactor scaling history handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Removed scalingEngineClient from PublicApiHandler and associated creation logic. • Replaced CreateHTTPClient with CreateHTTPSClient for eventGeneratorClient. • Deleted GetScalingHistories function as it's no longer needed. • Refactored NewPublicApiServer to use a new PublicApiServer struct and split into GetHealthServer and GetMtlsServer methods. • Updated import paths and variable names to reflect internal scaling history API changes. • Added SecuritySource struct to handle security for scaling history handler. • Modified ScalingHistoryHandler to use the new internal scaling history client and updated JSON marshaling logic. --- .../api/publicapiserver/public_api_handler.go | 48 +-------- .../api/publicapiserver/public_api_server.go | 102 ++++++++++++++++-- .../publicapiserver/public_api_server_test.go | 37 +++---- .../publicapiserver_suite_test.go | 20 ++-- .../scaling_history_handler.go | 79 +++++++++----- 5 files changed, 172 insertions(+), 114 deletions(-) diff --git a/src/autoscaler/api/publicapiserver/public_api_handler.go b/src/autoscaler/api/publicapiserver/public_api_handler.go index e8c7c485f1..f764d710f2 100644 --- a/src/autoscaler/api/publicapiserver/public_api_handler.go +++ b/src/autoscaler/api/publicapiserver/public_api_handler.go @@ -29,7 +29,6 @@ type PublicApiHandler struct { conf *config.Config policydb db.PolicyDB bindingdb db.BindingDB - scalingEngineClient *http.Client eventGeneratorClient *http.Client policyValidator *policyvalidator.PolicyValidator schedulerUtil *schedulerclient.Client @@ -43,13 +42,7 @@ const ( ) func NewPublicApiHandler(logger lager.Logger, conf *config.Config, policydb db.PolicyDB, bindingdb db.BindingDB, credentials cred_helper.Credentials) *PublicApiHandler { - seClient, err := helpers.CreateHTTPClient(&conf.ScalingEngine.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("scaling_client")) - if err != nil { - logger.Error("Failed to create http client for ScalingEngine", err, lager.Data{"scalingengine": conf.ScalingEngine.TLSClientCerts}) - os.Exit(1) - } - - egClient, err := helpers.CreateHTTPClient(&conf.EventGenerator.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("event_client")) + egClient, err := helpers.CreateHTTPSClient(&conf.EventGenerator.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("event_client")) if err != nil { logger.Error("Failed to create http client for EventGenerator", err, lager.Data{"eventgenerator": conf.EventGenerator.TLSClientCerts}) os.Exit(1) @@ -60,7 +53,6 @@ func NewPublicApiHandler(logger lager.Logger, conf *config.Config, policydb db.P conf: conf, policydb: policydb, bindingdb: bindingdb, - scalingEngineClient: seClient, eventGeneratorClient: egClient, policyValidator: policyvalidator.NewPolicyValidator( conf.PolicySchemaPath, @@ -233,44 +225,6 @@ func (h *PublicApiHandler) DetachScalingPolicy(w http.ResponseWriter, r *http.Re } } -func (h *PublicApiHandler) GetScalingHistories(w http.ResponseWriter, req *http.Request, vars map[string]string) { - appId := vars["appId"] - logger := h.logger.Session("GetScalingHistories", lager.Data{"appId": appId}) - logger.Info("Get ScalingHistories") - - // be careful about removing this call! There's some backwards compatibility being done in this function - parameters, err := parseParameter(req, vars) - if err != nil { - logger.Error("bad-request", err, lager.Data{"appId": appId}) - writeErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - path, _ := routes.ScalingEngineRoutes().Get(routes.GetScalingHistoriesRouteName).URLPath("guid", appId) - targetURL := h.conf.ScalingEngine.ScalingEngineUrl + path.RequestURI() + "?" + parameters.Encode() - - targetRequest, _ := http.NewRequest(http.MethodGet, targetURL, nil) - targetRequest.Header.Set("Authorization", "Bearer none") - - response, err := h.scalingEngineClient.Do(targetRequest) - - if err != nil { - logger.Error("error-getting-scaling-history", err, lager.Data{"url": targetURL}) - writeErrorResponse(w, http.StatusInternalServerError, "Error retrieving scaling history from scaling engine") - return - } - w.Header().Set("Content-Type", response.Header.Get("Content-Type")) - w.Header().Set("Content-Length", response.Header.Get("Content-Length")) - - if _, err := io.Copy(w, response.Body); err != nil { - logger.Error("copy-response", err) - return - } - err = response.Body.Close() - if err != nil { - logger.Error("body-close", err) - } -} - func proxyRequest(pathFn func() string, call func(url string) (*http.Response, error), w http.ResponseWriter, reqUrl *url.URL, parameters *url.Values, requestDescription string, logger lager.Logger) { aUrl := pathFn() resp, err := call(aUrl) diff --git a/src/autoscaler/api/publicapiserver/public_api_server.go b/src/autoscaler/api/publicapiserver/public_api_server.go index 80f5159431..7069d4b274 100644 --- a/src/autoscaler/api/publicapiserver/public_api_server.go +++ b/src/autoscaler/api/publicapiserver/public_api_server.go @@ -3,13 +3,14 @@ package publicapiserver import ( "fmt" "net/http" + "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cred_helper" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers/apis/scalinghistory" "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/apis/scalinghistory" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cf" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" @@ -19,6 +20,7 @@ import ( "code.cloudfoundry.org/lager/v3" "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" "github.com/tedsuo/ifrit" ) @@ -29,22 +31,43 @@ func (vh VarsFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { vh(w, r, vars) } -func NewPublicApiServer(logger lager.Logger, conf *config.Config, policydb db.PolicyDB, credentials cred_helper.Credentials, - checkBindingFunc api.CheckBindingFunc, cfclient cf.CFClient, httpStatusCollector healthendpoint.HTTPStatusCollector, - rateLimiter ratelimiter.Limiter, bindingdb db.BindingDB) (ifrit.Runner, error) { - pah := NewPublicApiHandler(logger, conf, policydb, bindingdb, credentials) +type PublicApiServer struct { + logger lager.Logger + conf *config.Config + policyDB db.PolicyDB + bindingDB db.BindingDB + credentials cred_helper.Credentials + checkBindingFunc api.CheckBindingFunc + cfClient cf.CFClient + httpStatusCollector healthendpoint.HTTPStatusCollector + rateLimiter ratelimiter.Limiter +} + +func (s *PublicApiServer) GetHealthServer() (ifrit.Runner, error) { + healthRouter, err := createHealthRouter(s.logger, s.conf, s.policyDB, s.bindingDB, s.httpStatusCollector) + if err != nil { + return nil, fmt.Errorf("failed to create health router: %w", err) + } + + return helpers.NewHTTPServer(s.logger, s.conf.Health.ServerConfig, healthRouter) +} + +func (s *PublicApiServer) GetMtlsServer() (ifrit.Runner, error) { + pah := NewPublicApiHandler(s.logger, s.conf, s.policyDB, s.bindingDB, s.credentials) - scalingHistoryHandler, err := newScalingHistoryHandler(logger, conf) + scalingHistoryHandler, err := newScalingHistoryHandler(s.logger, s.conf) if err != nil { return nil, err } - mw := NewMiddleware(logger, cfclient, checkBindingFunc, conf.APIClientId) - rateLimiterMiddleware := ratelimiter.NewRateLimiterMiddleware("appId", rateLimiter, logger.Session("api-ratelimiter-middleware")) - httpStatusCollectMiddleware := healthendpoint.NewHTTPStatusCollectMiddleware(httpStatusCollector) + mw := NewMiddleware(s.logger, s.cfClient, s.checkBindingFunc, s.conf.APIClientId) + rateLimiterMiddleware := ratelimiter.NewRateLimiterMiddleware("appId", s.rateLimiter, s.logger.Session("api-ratelimiter-middleware")) + httpStatusCollectMiddleware := healthendpoint.NewHTTPStatusCollectMiddleware(s.httpStatusCollector) + r := routes.ApiOpenRoutes() r.Use(otelmux.Middleware("apiserver")) r.Use(httpStatusCollectMiddleware.Collect) + r.Get(routes.PublicApiInfoRouteName).Handler(VarsFunc(pah.GetApiInfo)) r.Get(routes.PublicApiHealthRouteName).Handler(VarsFunc(pah.GetHealth)) @@ -75,15 +98,72 @@ func NewPublicApiServer(logger lager.Logger, conf *config.Config, policydb db.Po rcredential.Use(mw.HasClientToken) rcredential.Use(mw.Oauth) - return helpers.NewHTTPServer(logger, conf.PublicApiServer, r) + healthRouter, err := createHealthRouter(s.logger, s.conf, s.policyDB, s.bindingDB, s.httpStatusCollector) + if err != nil { + return nil, fmt.Errorf("failed to create health router: %w", err) + } + + mainRouter := setupMainRouter(r, healthRouter) + + return helpers.NewHTTPServer(s.logger, s.conf.PublicApiServer, mainRouter) +} + +func NewPublicApiServer(logger lager.Logger, conf *config.Config, policyDB db.PolicyDB, + bindingDB db.BindingDB, credentials cred_helper.Credentials, checkBindingFunc api.CheckBindingFunc, + cfClient cf.CFClient, httpStatusCollector healthendpoint.HTTPStatusCollector, + rateLimiter ratelimiter.Limiter) *PublicApiServer { + return &PublicApiServer{ + logger: logger, + conf: conf, + policyDB: policyDB, + bindingDB: bindingDB, + credentials: credentials, + checkBindingFunc: checkBindingFunc, + cfClient: cfClient, + httpStatusCollector: httpStatusCollector, + rateLimiter: rateLimiter, + } +} + +func setupMainRouter(r *mux.Router, healthRouter *mux.Router) *mux.Router { + mainRouter := mux.NewRouter() + mainRouter.PathPrefix("/v1").Handler(r) + mainRouter.PathPrefix("/health").Handler(healthRouter) + mainRouter.PathPrefix("/").Handler(healthRouter) + return mainRouter +} + +func createPrometheusRegistry(policyDB db.PolicyDB, bindingDB db.BindingDB, httpStatusCollector healthendpoint.HTTPStatusCollector, logger lager.Logger) *prometheus.Registry { + promRegistry := prometheus.NewRegistry() + healthendpoint.RegisterCollectors(promRegistry, + []prometheus.Collector{ + healthendpoint.NewDatabaseStatusCollector("autoscaler", "golangapiserver", "policyDB", policyDB), + healthendpoint.NewDatabaseStatusCollector("autoscaler", "golangapiserver", "bindingDB", bindingDB), + httpStatusCollector, + }, + true, logger.Session("golangapiserver-prometheus")) + return promRegistry +} + +func createHealthRouter(logger lager.Logger, conf *config.Config, policyDB db.PolicyDB, bindingDB db.BindingDB, httpStatusCollector healthendpoint.HTTPStatusCollector) (*mux.Router, error) { + checkers := []healthendpoint.Checker{} + gatherer := createPrometheusRegistry(policyDB, bindingDB, httpStatusCollector, logger) + healthRouter, err := healthendpoint.NewHealthRouter(conf.Health, checkers, logger.Session("health-server"), gatherer, time.Now) + if err != nil { + return nil, fmt.Errorf("failed to create health router: %w", err) + } + + logger.Debug("Successfully created health server") + return healthRouter, nil } func newScalingHistoryHandler(logger lager.Logger, conf *config.Config) (http.Handler, error) { + ss := SecuritySource{} scalingHistoryHandler, err := NewScalingHistoryHandler(logger, conf) if err != nil { return nil, fmt.Errorf("error creating scaling history handler: %w", err) } - scalingHistoryServer, err := scalinghistory.NewServer(scalingHistoryHandler, scalingHistoryHandler) + scalingHistoryServer, err := scalinghistory.NewServer(scalingHistoryHandler, ss) if err != nil { return nil, fmt.Errorf("error creating ogen scaling history server: %w", err) } diff --git a/src/autoscaler/api/publicapiserver/public_api_server_test.go b/src/autoscaler/api/publicapiserver/public_api_server_test.go index 7b17a46518..1751a49ab2 100644 --- a/src/autoscaler/api/publicapiserver/public_api_server_test.go +++ b/src/autoscaler/api/publicapiserver/public_api_server_test.go @@ -6,7 +6,7 @@ import ( "net/url" "strings" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers/apis/scalinghistory" + internalscalinghistory "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/apis/scalinghistory" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" @@ -42,24 +42,24 @@ var _ = Describe("PublicApiServer", func() { ) BeforeEach(func() { - scalingHistoryEntry := []scalinghistory.HistoryEntry{ + scalingHistoryEntry := []internalscalinghistory.HistoryEntry{ { - Status: scalinghistory.NewOptHistoryEntryStatus(scalinghistory.HistoryEntryStatus0), - AppID: scalinghistory.NewOptGUID(TEST_APP_ID), - Timestamp: scalinghistory.NewOptInt(300), - ScalingType: scalinghistory.NewOptHistoryEntryScalingType(scalinghistory.HistoryEntryScalingType0), - OldInstances: scalinghistory.NewOptInt64(2), - NewInstances: scalinghistory.NewOptInt64(4), - Reason: scalinghistory.NewOptString("a reason"), + Status: internalscalinghistory.NewOptHistoryEntryStatus(internalscalinghistory.HistoryEntryStatus0), + AppID: internalscalinghistory.NewOptGUID(TEST_APP_ID), + Timestamp: internalscalinghistory.NewOptInt(300), + ScalingType: internalscalinghistory.NewOptHistoryEntryScalingType(internalscalinghistory.HistoryEntryScalingType0), + OldInstances: internalscalinghistory.NewOptInt64(2), + NewInstances: internalscalinghistory.NewOptInt64(4), + Reason: internalscalinghistory.NewOptString("a reason"), }, } - scalingEngineResponse = scalinghistory.History{ - TotalResults: scalinghistory.NewOptInt64(1), - TotalPages: scalinghistory.NewOptInt64(1), - Page: scalinghistory.NewOptInt64(1), - PrevURL: scalinghistory.OptURI{}, - NextURL: scalinghistory.OptURI{}, + scalingEngineResponse = internalscalinghistory.History{ + TotalResults: internalscalinghistory.NewOptInt64(1), + TotalPages: internalscalinghistory.NewOptInt64(1), + Page: internalscalinghistory.NewOptInt64(1), + PrevURL: internalscalinghistory.OptURI{}, + NextURL: internalscalinghistory.OptURI{}, Resources: scalingHistoryEntry, } @@ -425,6 +425,7 @@ var _ = Describe("PublicApiServer", func() { }) }) }) + Describe("UnProtected Routes", func() { Context("when calling info endpoint", func() { It("should succeed", func() { @@ -437,12 +438,6 @@ var _ = Describe("PublicApiServer", func() { }) }) }) - - Context("when requesting non existing path", func() { - It("should get 404", func() { - verifyResponse(httpClient, serverUrl, "/non-existing-path", nil, http.MethodGet, "", http.StatusNotFound) - }) - }) }) func verifyResponse(httpClient *http.Client, serverUrl *url.URL, path string, headers map[string]string, httpRequestMethod string, httpRequestBody string, expectResponseStatusCode int) { diff --git a/src/autoscaler/api/publicapiserver/publicapiserver_suite_test.go b/src/autoscaler/api/publicapiserver/publicapiserver_suite_test.go index 4c73bc9d28..455f6c23f6 100644 --- a/src/autoscaler/api/publicapiserver/publicapiserver_suite_test.go +++ b/src/autoscaler/api/publicapiserver/publicapiserver_suite_test.go @@ -11,7 +11,8 @@ import ( "strconv" "testing" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers/apis/scalinghistory" + internalscalinghistory "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/apis/scalinghistory" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" "code.cloudfoundry.org/lager/v3/lagertest" . "github.com/onsi/ginkgo/v2" @@ -62,18 +63,18 @@ var ( schedulerStatus int schedulerErrJson string - scalingEngineResponse scalinghistory.History + scalingEngineResponse internalscalinghistory.History metricsCollectorResponse []models.AppInstanceMetric eventGeneratorResponse []models.AppMetric fakeCFClient *fakes.FakeCFClient fakePolicyDB *fakes.FakePolicyDB + fakeBindingDB *fakes.FakeBindingDB fakeRateLimiter *fakes.FakeLimiter fakeCredentials *fakes.FakeCredentials checkBindingFunc api.CheckBindingFunc hasBinding = true apiPort = 0 - testCertDir = "../../../../test-certs" ) func TestPublicapiserver(t *testing.T) { @@ -111,6 +112,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) fakePolicyDB = &fakes.FakePolicyDB{} + fakeBindingDB = &fakes.FakeBindingDB{} checkBindingFunc = func(appId string) bool { return hasBinding } @@ -118,10 +120,13 @@ var _ = BeforeSuite(func() { httpStatusCollector := &fakes.FakeHTTPStatusCollector{} fakeRateLimiter = &fakes.FakeLimiter{} fakeCredentials = &fakes.FakeCredentials{} - httpServer, err := publicapiserver.NewPublicApiServer(lagertest.NewTestLogger("public_apiserver"), conf, - fakePolicyDB, fakeCredentials, - checkBindingFunc, fakeCFClient, - httpStatusCollector, fakeRateLimiter, nil) + + publicApiServer := publicapiserver.NewPublicApiServer( + lagertest.NewTestLogger("public_apiserver"), conf, fakePolicyDB, + fakeBindingDB, fakeCredentials, checkBindingFunc, fakeCFClient, + httpStatusCollector, fakeRateLimiter) + + httpServer, err := publicApiServer.GetMtlsServer() Expect(err).NotTo(HaveOccurred()) serverUrl, err = url.Parse("http://127.0.0.1:" + strconv.Itoa(apiPort)) @@ -177,6 +182,7 @@ func CheckResponse(resp *httptest.ResponseRecorder, statusCode int, errResponse } func CreateConfig(apiServerPort int) *config.Config { + testCertDir := testhelpers.TestCertFolder() return &config.Config{ Logging: helpers.LoggingConfig{ Level: "debug", diff --git a/src/autoscaler/api/publicapiserver/scaling_history_handler.go b/src/autoscaler/api/publicapiserver/scaling_history_handler.go index 2e8ad706d0..85be1d557b 100644 --- a/src/autoscaler/api/publicapiserver/scaling_history_handler.go +++ b/src/autoscaler/api/publicapiserver/scaling_history_handler.go @@ -5,37 +5,47 @@ import ( "fmt" "net/http" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/apis/scalinghistory" + internalscalingenginehistory "code.cloudfoundry.org/app-autoscaler/src/autoscaler/scalingengine/apis/scalinghistory" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers/apis/scalinghistory" "code.cloudfoundry.org/lager/v3" ) var ( - _ = scalinghistory.SecurityHandler(&ScalingHistoryHandler{}) - _ = scalinghistory.SecuritySource(&ScalingHistoryHandler{}) + _ = scalinghistory.SecurityHandler(&SecuritySource{}) + _ = scalinghistory.SecuritySource(&SecuritySource{}) ) +type SecuritySource struct{} + +func (h *SecuritySource) BearerAuth(_ context.Context, _ string) (scalinghistory.BearerAuth, error) { + // We are calling the scalingengine server authenticated via mTLS, so no bearer token is necessary. + // Having this function is required by the interface `SecuritySource`in “oas_security_gen”. + return scalinghistory.BearerAuth{Token: "none"}, nil +} + +func (h SecuritySource) HandleBearerAuth(ctx context.Context, operationName string, t scalinghistory.BearerAuth) (context.Context, error) { + // this handler is a no-op, as this handler shall only be available used behind our own auth middleware. + // having this handler is required by the interface `securityhandler` in “oas_security_gen”. + return ctx, nil +} + type ScalingHistoryHandler struct { - logger lager.Logger - conf *config.Config - scalingEngineClient *http.Client - client *scalinghistory.Client + logger lager.Logger + conf *config.Config + client *internalscalingenginehistory.Client } func NewScalingHistoryHandler(logger lager.Logger, conf *config.Config) (*ScalingHistoryHandler, error) { - seClient, err := helpers.CreateHTTPClient(&conf.ScalingEngine.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("scaling_client")) - if err != nil { - return nil, fmt.Errorf("error creating scaling history HTTP client: %w", err) - } newHandler := &ScalingHistoryHandler{ - logger: logger.Session("scaling-history-handler"), - conf: conf, - scalingEngineClient: seClient, + logger: logger.Session("scaling-history-handler"), + conf: conf, } - if client, err := scalinghistory.NewClient(conf.ScalingEngine.ScalingEngineUrl, newHandler, scalinghistory.WithClient(seClient)); err != nil { + if client, err := internalscalingenginehistory.NewClient(conf.ScalingEngine.ScalingEngineUrl); err != nil { return nil, fmt.Errorf("error creating ogen scaling history client: %w", err) } else { newHandler.client = client @@ -54,26 +64,39 @@ func (h *ScalingHistoryHandler) NewError(_ context.Context, _ error) *scalinghis return result } -func (h *ScalingHistoryHandler) HandleBearerAuth(ctx context.Context, operationName string, t scalinghistory.BearerAuth) (context.Context, error) { - // This handler is a no-op, as this handler shall only be available used behind our own auth middleware. - // Having this handler is required by the interface `SecurityHandler` in “oas_security_gen”. - return ctx, nil -} - func (h *ScalingHistoryHandler) V1AppsGUIDScalingHistoriesGet(ctx context.Context, params scalinghistory.V1AppsGUIDScalingHistoriesGetParams) (*scalinghistory.History, error) { + result := &scalinghistory.History{} logger := h.logger.Session("get-scaling-histories", helpers.AddTraceID(ctx, lager.Data{"app_guid": params.GUID})) logger.Info("start") defer logger.Info("end") - result, err := h.client.V1AppsGUIDScalingHistoriesGet(ctx, params) + internalParams := internalscalingenginehistory.V1AppsGUIDScalingHistoriesGetParams{ + GUID: internalscalingenginehistory.GUID(params.GUID), + StartTime: internalscalingenginehistory.OptInt(params.StartTime), + EndTime: internalscalingenginehistory.OptInt(params.EndTime), + OrderDirection: internalscalingenginehistory.OptV1AppsGUIDScalingHistoriesGetOrderDirection{ + Value: internalscalingenginehistory.V1AppsGUIDScalingHistoriesGetOrderDirection(params.OrderDirection.Value), + Set: params.OrderDirection.Set, + }, + Page: internalscalingenginehistory.OptInt(params.Page), + ResultsPerPage: internalscalingenginehistory.OptInt(params.ResultsPerPage), + } + internalResult, err := h.client.V1AppsGUIDScalingHistoriesGet(ctx, internalParams) if err != nil { logger.Error("get", err) + return nil, err + } + jsonResult, err := internalResult.MarshalJSON() + if err != nil { + logger.Error("marshal", err) + return nil, err } - return result, err -} -func (h *ScalingHistoryHandler) BearerAuth(_ context.Context, _ string) (scalinghistory.BearerAuth, error) { - // We are calling the scalingengine server authenticated via mTLS, so no bearer token is necessary. - // Having this function is required by the interface `SecuritySource`in “oas_security_gen”. - return scalinghistory.BearerAuth{Token: "none"}, nil + err = result.UnmarshalJSON(jsonResult) + if err != nil { + logger.Error("unmarshal", err) + return nil, err + } + + return result, err } From 758f0fd2d9fdb725fb4d8fe0e9a6a185e99dd453 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:37:43 +0200 Subject: [PATCH 04/39] Refactor API test suite and update health check configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Update API test suite to use testhelpers package for utility functions. • Modify health check configuration to use BasicAuth struct instead of separate username and password fields. • Remove unused variables and imports, and clean up test setup for clarity. • Adjust API server creation to directly instantiate servers without a separate function. --- src/autoscaler/api/cmd/api/api_suite_test.go | 41 ++++++++-------- src/autoscaler/api/cmd/api/api_test.go | 49 ++++++++++++++++---- src/autoscaler/api/cmd/api/main.go | 36 +++++--------- 3 files changed, 70 insertions(+), 56 deletions(-) diff --git a/src/autoscaler/api/cmd/api/api_suite_test.go b/src/autoscaler/api/cmd/api/api_suite_test.go index 8c87854992..24ba1a868e 100644 --- a/src/autoscaler/api/cmd/api/api_suite_test.go +++ b/src/autoscaler/api/cmd/api/api_suite_test.go @@ -11,8 +11,7 @@ import ( "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cf/mocks" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" - - . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" @@ -37,18 +36,17 @@ const ( ) var ( - apPath string - cfg config.Config - configFile *os.File - apiHttpClient *http.Client - healthHttpClient *http.Client - catalogBytes string - schedulerServer *ghttp.Server - brokerPort int - publicApiPort int - healthport int - infoBytes string - ccServer *mocks.Server + apPath string + cfg config.Config + configFile *os.File + apiHttpClient *http.Client + schedulerServer *ghttp.Server + catalogBytes string + brokerPort int + publicApiPort int + healthport int + infoBytes string + ccServer *mocks.Server ) func TestApi(t *testing.T) { @@ -64,7 +62,7 @@ type testdata struct { var _ = SynchronizedBeforeSuite(func() []byte { info := testdata{} - dbUrl := GetDbUrl() + dbUrl := testhelpers.GetDbUrl() database, e := db.GetConnection(dbUrl) if e != nil { @@ -135,7 +133,7 @@ var _ = SynchronizedBeforeSuite(func() []byte { } cfg.Logging.Level = "info" cfg.DB = make(map[string]db.DatabaseConfig) - dbUrl := GetDbUrl() + dbUrl := testhelpers.GetDbUrl() cfg.DB[db.BindingDb] = db.DatabaseConfig{ URL: dbUrl, MaxOpenConnections: 10, @@ -201,8 +199,10 @@ var _ = SynchronizedBeforeSuite(func() []byte { ServerConfig: helpers.ServerConfig{ Port: healthport, }, - HealthCheckUsername: "healthcheckuser", - HealthCheckPassword: "healthcheckpassword", + BasicAuth: models.BasicAuth{ + Username: "healthcheckuser", + Password: "healthcheckpassword", + }, } cfg.RateLimit.MaxAmount = 10 cfg.RateLimit.ValidDuration = 1 * time.Second @@ -211,9 +211,6 @@ var _ = SynchronizedBeforeSuite(func() []byte { configFile = writeConfig(&cfg) - apiHttpClient = NewApiClient() - - healthHttpClient = &http.Client{} }) var _ = SynchronizedAfterSuite(func() { @@ -281,6 +278,6 @@ func (ap *ApiRunner) Interrupt() { func readFile(filename string) string { contents, err := os.ReadFile(filename) - FailOnError("Failed to read file:"+filename+" ", err) + testhelpers.FailOnError("Failed to read file:"+filename+" ", err) return string(contents) } diff --git a/src/autoscaler/api/cmd/api/api_test.go b/src/autoscaler/api/cmd/api/api_test.go index 4a2b35689b..467422ad88 100644 --- a/src/autoscaler/api/cmd/api/api_test.go +++ b/src/autoscaler/api/cmd/api/api_test.go @@ -4,8 +4,10 @@ import ( "fmt" "io" "net/http" + "net/url" "os" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" @@ -20,14 +22,36 @@ import ( var _ = Describe("Api", func() { var ( - runner *ApiRunner - rsp *http.Response + runner *ApiRunner + rsp *http.Response + brokerHttpClient *http.Client + healthHttpClient *http.Client + apiHttpClient *http.Client + + serverURL *url.URL + brokerURL *url.URL + healthURL *url.URL + + err error ) BeforeEach(func() { - brokerHttpClient = NewServiceBrokerClient() runner = NewApiRunner() + + brokerHttpClient = NewServiceBrokerClient() + healthHttpClient = &http.Client{} + apiHttpClient = testhelpers.NewPublicApiClient() + + serverURL, err = url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfg.PublicApiServer.Port)) + Expect(err).NotTo(HaveOccurred()) + + brokerURL, err = url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfg.BrokerServer.Port)) + Expect(err).NotTo(HaveOccurred()) + + healthURL, err = url.Parse(fmt.Sprintf("http://127.0.0.1:%d", cfg.Health.ServerConfig.Port)) + Expect(err).NotTo(HaveOccurred()) + }) Describe("Api configuration check", func() { @@ -118,14 +142,17 @@ var _ = Describe("Api", func() { BeforeEach(func() { runner.Start() }) + It("succeeds with a 200", func() { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://127.0.0.1:%d/v2/catalog", brokerPort), nil) + brokerURL.Path = "/v2/catalog" + req, err := http.NewRequest(http.MethodGet, brokerURL.String(), nil) Expect(err).NotTo(HaveOccurred()) req.SetBasicAuth(username, password) rsp, err = brokerHttpClient.Do(req) Expect(err).ToNot(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusOK)) if rsp.StatusCode != http.StatusOK { Fail(fmt.Sprintf("Not ok:%d", rsp.StatusCode)) @@ -153,7 +180,8 @@ var _ = Describe("Api", func() { runner.Start() }) It("succeeds with a 200", func() { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://127.0.0.1:%d/v1/info", publicApiPort), nil) + serverURL.Path = "/v1/info" + req, err := http.NewRequest(http.MethodGet, serverURL.String(), nil) Expect(err).NotTo(HaveOccurred()) rsp, err = apiHttpClient.Do(req) @@ -169,8 +197,8 @@ var _ = Describe("Api", func() { Describe("when Health server is ready to serve RESTful API", func() { BeforeEach(func() { basicAuthConfig := cfg - basicAuthConfig.Health.HealthCheckUsername = "" - basicAuthConfig.Health.HealthCheckPassword = "" + basicAuthConfig.Health.BasicAuth.Username = "" + basicAuthConfig.Health.BasicAuth.Password = "" runner.configPath = writeConfig(&basicAuthConfig).Name() runner.Start() }) @@ -180,7 +208,8 @@ var _ = Describe("Api", func() { }) Context("when a request to query health comes", func() { It("returns with a 200", func() { - rsp, err := healthHttpClient.Get(fmt.Sprintf("http://127.0.0.1:%d", healthport)) + rsp, err := healthHttpClient.Get(healthURL.String()) + Expect(err).NotTo(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) raw, _ := io.ReadAll(rsp.Body) @@ -224,7 +253,7 @@ var _ = Describe("Api", func() { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth(cfg.Health.HealthCheckUsername, cfg.Health.HealthCheckPassword) + req.SetBasicAuth(cfg.Health.BasicAuth.Username, cfg.Health.BasicAuth.Password) rsp, err := healthHttpClient.Do(req) Expect(err).ToNot(HaveOccurred()) @@ -246,7 +275,7 @@ var _ = Describe("Api", func() { }) Context("when a request to query health comes", func() { It("returns with a 200", func() { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://127.0.0.1:%d/v1/info", publicApiPort), nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v1/info", serverURL), nil) Expect(err).NotTo(HaveOccurred()) rsp, err = apiHttpClient.Do(req) diff --git a/src/autoscaler/api/cmd/api/main.go b/src/autoscaler/api/cmd/api/main.go index ee9c782c2c..37ef97d2cd 100644 --- a/src/autoscaler/api/cmd/api/main.go +++ b/src/autoscaler/api/cmd/api/main.go @@ -6,7 +6,6 @@ import ( "os" "time" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/brokerserver" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/publicapiserver" @@ -20,7 +19,6 @@ import ( "code.cloudfoundry.org/clock" "code.cloudfoundry.org/lager/v3" - "github.com/prometheus/client_golang/prometheus" "github.com/tedsuo/ifrit" "github.com/tedsuo/ifrit/grouper" "github.com/tedsuo/ifrit/sigmon" @@ -69,10 +67,6 @@ func main() { defer func() { _ = credentialProvider.Close() }() httpStatusCollector := healthendpoint.NewHTTPStatusCollector("autoscaler", "golangapiserver") - prometheusCollectors := []prometheus.Collector{ - healthendpoint.NewDatabaseStatusCollector("autoscaler", "golangapiserver", "policyDB", policyDb), - httpStatusCollector, - } paClock := clock.NewClock() cfClient := cf.NewCFClient(&conf.CF, logger.Session("cf"), paClock) @@ -89,7 +83,6 @@ func main() { os.Exit(1) } defer func() { _ = bindingDB.Close() }() - prometheusCollectors = append(prometheusCollectors, healthendpoint.NewDatabaseStatusCollector("autoscaler", "golangapiserver", "bindingDB", bindingDB)) checkBindingFunc := func(appId string) bool { return bindingDB.CheckServiceBinding(appId) } @@ -99,21 +92,26 @@ func main() { logger.Error("failed to create broker http server", err) os.Exit(1) } - members = append(members, grouper.Member{"broker_http_server", brokerHttpServer}) - promRegistry := prometheus.NewRegistry() - healthendpoint.RegisterCollectors(promRegistry, prometheusCollectors, true, logger.Session("golangapiserver-prometheus")) + rateLimiter := ratelimiter.DefaultRateLimiter(conf.RateLimit.MaxAmount, conf.RateLimit.ValidDuration, logger.Session("api-ratelimiter")) + + publicApiHttpServer := publicapiserver.NewPublicApiServer( + logger.Session("public_api_http_server"), conf, policyDb, bindingDB, + credentialProvider, checkBindingFunc, cfClient, httpStatusCollector, + rateLimiter) - publicApiHttpServer := createApiServer(conf, logger, policyDb, credentialProvider, checkBindingFunc, cfClient, httpStatusCollector, bindingDB) - healthServer, err := healthendpoint.NewServerWithBasicAuth(conf.Health, []healthendpoint.Checker{}, logger.Session("health-server"), promRegistry, time.Now) + vmServer, err := publicApiHttpServer.GetMtlsServer() if err != nil { - logger.Fatal("Failed to create health server", err) + logger.Error("failed to create public api http server", err) os.Exit(1) } + logger.Debug("Successfully created health server") + healthServer, _ := publicApiHttpServer.GetHealthServer() members = append(members, - grouper.Member{"public_api_http_server", publicApiHttpServer}, + grouper.Member{"public_api_http_server", vmServer}, + grouper.Member{"broker", brokerHttpServer}, grouper.Member{"health_server", healthServer}, ) @@ -130,13 +128,3 @@ func main() { logger.Info("exited") } - -func createApiServer(conf *config.Config, logger lager.Logger, policyDb *sqldb.PolicySQLDB, credentialProvider cred_helper.Credentials, checkBindingFunc api.CheckBindingFunc, cfClient cf.CFClient, httpStatusCollector healthendpoint.HTTPStatusCollector, bindingDB db.BindingDB) ifrit.Runner { - rateLimiter := ratelimiter.DefaultRateLimiter(conf.RateLimit.MaxAmount, conf.RateLimit.ValidDuration, logger.Session("api-ratelimiter")) - publicApiHttpServer, err := publicapiserver.NewPublicApiServer(logger.Session("public_api_http_server"), conf, policyDb, credentialProvider, checkBindingFunc, cfClient, httpStatusCollector, rateLimiter, bindingDB) - if err != nil { - logger.Error("failed to create public api http server", err) - os.Exit(1) - } - return publicApiHttpServer -} From a45b9c54ae3502a13c402a2b3de8fb6c77bec541 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:37:59 +0200 Subject: [PATCH 05/39] Add go:generate directive and remote parsing to ogen, update health port config, and switch to HTTPS client in schedulerclient --- src/autoscaler/api/apis/generate.go | 3 +++ src/autoscaler/api/apis/ogen-config.yaml | 2 ++ src/autoscaler/api/config/config_test.go | 3 ++- src/autoscaler/api/schedulerclient/client.go | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/autoscaler/api/apis/generate.go create mode 100644 src/autoscaler/api/apis/ogen-config.yaml diff --git a/src/autoscaler/api/apis/generate.go b/src/autoscaler/api/apis/generate.go new file mode 100644 index 0000000000..dfb2141a54 --- /dev/null +++ b/src/autoscaler/api/apis/generate.go @@ -0,0 +1,3 @@ +package apis + +//go:generate go run github.com/ogen-go/ogen/cmd/ogen --config ogen-config.yaml --package scalinghistory --target scalinghistory --clean ../../../../api/scaling-history-api.openapi.yaml diff --git a/src/autoscaler/api/apis/ogen-config.yaml b/src/autoscaler/api/apis/ogen-config.yaml new file mode 100644 index 0000000000..5b85450eaa --- /dev/null +++ b/src/autoscaler/api/apis/ogen-config.yaml @@ -0,0 +1,2 @@ +parser: + allow_remote: true diff --git a/src/autoscaler/api/config/config_test.go b/src/autoscaler/api/config/config_test.go index 8bc5389ec3..265d2b396f 100644 --- a/src/autoscaler/api/config/config_test.go +++ b/src/autoscaler/api/config/config_test.go @@ -188,7 +188,8 @@ public_api_server: BeforeEach(func() { configBytes = ` health: - port: port + server_config: + port: port ` }) diff --git a/src/autoscaler/api/schedulerclient/client.go b/src/autoscaler/api/schedulerclient/client.go index 214601296e..86c0632932 100644 --- a/src/autoscaler/api/schedulerclient/client.go +++ b/src/autoscaler/api/schedulerclient/client.go @@ -25,7 +25,7 @@ type Client struct { func New(conf *config.Config, logger lager.Logger) *Client { logger = logger.Session("schedulerclient") - client, err := helpers.CreateHTTPClient(&conf.Scheduler.TLSClientCerts, helpers.DefaultClientConfig(), logger) + client, err := helpers.CreateHTTPSClient(&conf.Scheduler.TLSClientCerts, helpers.DefaultClientConfig(), logger) if err != nil { logger.Error("Failed to create http client for Scheduler", err, lager.Data{"scheduler": conf.Scheduler.TLSClientCerts}) os.Exit(1) From 16bbb99c8a2a85daf7f24f930223029a623d4e60 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:38:31 +0200 Subject: [PATCH 06/39] Add Makefile for eventgenerator and refactor eventgenerator tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Add a new Makefile for the eventgenerator component with a fetch-config target to pull configuration and certificates from the metricsforwarder VM. • Refactor eventgenerator test suites to improve readability and organization, including the use of When blocks for context-specific conditions. • Update eventgenerator REST API tests to use helper functions for creating HTTP clients and constructing request URLs. • Modify the eventgenerator main.go to use a new server creation function that handles both MTLS and health endpoints. • Adjust eventgenerator configuration tests to reflect changes in the health check server configuration structure. • Implement server.go changes to separate the creation of the event generator and health routers, and to provide functions for retrieving MTLS and health servers. • Update server_suite_test.go to remove unnecessary setup code due to refactoring. • Refactor server_test.go to use the new server creation functions and to organize tests into When and Describe blocks for clarity. --- src/autoscaler/eventgenerator/Makefile | 32 ++++ .../eventgenerator_suite_test.go | 11 +- .../cmd/eventgenerator/eventgenerator_test.go | 170 ++++++++++-------- .../eventgenerator/cmd/eventgenerator/main.go | 19 +- .../eventgenerator/config/config_test.go | 6 +- .../eventgenerator/server/server.go | 84 ++++++++- .../server/server_suite_test.go | 44 ----- .../eventgenerator/server/server_test.go | 92 +++++++--- 8 files changed, 299 insertions(+), 159 deletions(-) create mode 100644 src/autoscaler/eventgenerator/Makefile diff --git a/src/autoscaler/eventgenerator/Makefile b/src/autoscaler/eventgenerator/Makefile new file mode 100644 index 0000000000..af77ad173d --- /dev/null +++ b/src/autoscaler/eventgenerator/Makefile @@ -0,0 +1,32 @@ +.PHONY: fetch-config +fetch-config: start-metricsforwarder-vm + # how to define variables in deployment name + mkdir -p assets/certs/policy_db assets/certs/storedprocedure_db assets/certs/syslog_client + + echo "POSTGRES IP: $(POSTGRES_IP)" + echo "LOG_CACHE IP: $(LOG_CACHE_IP)" + + @echo "Pulling metricforwarder config from $(METIRCSFORWARDER_VM)..." + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/metricsforwarder.yml assets/metricsforwarder.yml + + @echo "Pulling policy db certs from $(METIRCSFORWARDER_VM)..." + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/policy_db/ca.crt assets/certs/policy_db/. + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/policy_db/crt assets/certs/policy_db/. + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/policy_db/key assets/certs/policy_db/. + + @echo "Pulling storeprocedure db certs from $(METIRCSFORWARDER_VM)..." + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/storedprocedure_db/ca.crt assets/certs/storedprocedure_db/. + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/storedprocedure_db/crt assets/certs/storedprocedure_db/. + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/storedprocedure_db/key assets/certs/storedprocedure_db/. + + @echo "Pulling syslog-client certs from $(METIRCSFORWARDER_VM)..." + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/syslog_client/ca.crt assets/certs/syslog_client/. + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/syslog_client/client.crt assets/certs/syslog_client/. + bosh -d $(DEPLOYMENT_NAME) scp $(METIRCSFORWARDER_VM):/var/vcap/jobs/metricsforwarder/config/certs/syslog_client/client.key assets/certs/syslog_client/. + + @echo "Build metricsforwarder config yaml" + cp assets/metricsforwarder.yml metricsforwarder.yml + + # remove SERVER TLS CONFIG so that it starts a http server + sed -i'' -e 's|\/var\/vcap\/jobs\/metricsforwarder\/config|\/home\/vcap\/app/assets|g' metricsforwarder.yml + sed -i'' -e 's|$(DEPLOYMENT_NAME).autoscalerpostgres.service.cf.internal|$(POSTGRES_IP)|g' metricsforwarder.yml diff --git a/src/autoscaler/eventgenerator/cmd/eventgenerator/eventgenerator_suite_test.go b/src/autoscaler/eventgenerator/cmd/eventgenerator/eventgenerator_suite_test.go index c53cd7057a..212e3267a0 100644 --- a/src/autoscaler/eventgenerator/cmd/eventgenerator/eventgenerator_suite_test.go +++ b/src/autoscaler/eventgenerator/cmd/eventgenerator/eventgenerator_suite_test.go @@ -73,7 +73,10 @@ var _ = SynchronizedBeforeSuite(func() []byte { }) var _ = SynchronizedAfterSuite(func() { - _ = os.Remove(configFile.Name()) + if configFile != nil { + err := os.Remove(configFile.Name()) + Expect(err).NotTo(HaveOccurred()) + } }, func() { gexec.CleanupBuildArtifacts() }) @@ -312,8 +315,10 @@ func initConfig() { ServerConfig: helpers.ServerConfig{ Port: healthport, }, - HealthCheckUsername: "healthcheckuser", - HealthCheckPassword: "healthcheckpassword", + BasicAuth: models.BasicAuth{ + Username: "healthcheckuser", + Password: "healthcheckpassword", + }, }, } configFile = writeConfig(&conf) diff --git a/src/autoscaler/eventgenerator/cmd/eventgenerator/eventgenerator_test.go b/src/autoscaler/eventgenerator/cmd/eventgenerator/eventgenerator_test.go index b9d1a86a9b..5cd5847861 100644 --- a/src/autoscaler/eventgenerator/cmd/eventgenerator/eventgenerator_test.go +++ b/src/autoscaler/eventgenerator/cmd/eventgenerator/eventgenerator_test.go @@ -1,14 +1,16 @@ package main_test import ( - "fmt" "io" "net/http" + "net/url" "os" + "strconv" "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/eventgenerator/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -19,11 +21,27 @@ import ( var _ = Describe("Eventgenerator", func() { var ( - runner *EventGeneratorRunner + runner *EventGeneratorRunner + httpClientForEventGenerator *http.Client + httpClientForHealth *http.Client + + serverURL *url.URL + healthURL *url.URL + + err error ) BeforeEach(func() { runner = NewEventGeneratorRunner() + + httpClientForEventGenerator = testhelpers.NewEventGeneratorClient() + httpClientForHealth = &http.Client{} + + serverURL, err = url.Parse("https://127.0.0.1:" + strconv.Itoa(conf.Server.Port)) + healthURL, err = url.Parse("http://127.0.0.1:" + strconv.Itoa(conf.Health.ServerConfig.Port)) + + Expect(err).ToNot(HaveOccurred()) + }) AfterEach(func() { @@ -111,7 +129,7 @@ var _ = Describe("Eventgenerator", func() { }) }) - Context("when an interrupt is sent", func() { + When("an interrupt is sent", func() { BeforeEach(func() { runner.Start() }) @@ -123,113 +141,119 @@ var _ = Describe("Eventgenerator", func() { }) Describe("EventGenerator REST API", func() { - Context("when a request for aggregated metrics history comes", func() { + When("a request for aggregated metrics history comes", func() { BeforeEach(func() { + serverURL.Path = "/v1/apps/an-app-id/aggregated_metric_histories/a-metric-type" runner.Start() }) It("returns with a 200", func() { - rsp, err := httpClient.Get(fmt.Sprintf("https://127.0.0.1:%d/v1/apps/an-app-id/aggregated_metric_histories/a-metric-type", egPort)) + rsp, err := httpClientForEventGenerator.Get(serverURL.String()) Expect(err).NotTo(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) rsp.Body.Close() }) - }) - }) - Describe("when Health server is ready to serve RESTful API", func() { + Describe("EventGenerator Health endpoint", func() { + BeforeEach(func() { - basicAuthConfig := conf - basicAuthConfig.Health.HealthCheckUsername = "" - basicAuthConfig.Health.HealthCheckPassword = "" - runner.configPath = writeConfig(&basicAuthConfig).Name() + serverURL.Path = "/health" + }) - runner.Start() + When("Health server is ready to serve RESTful API", func() { + BeforeEach(func() { + basicAuthConfig := conf + basicAuthConfig.Health.BasicAuth.Username = "" + basicAuthConfig.Health.BasicAuth.Password = "" + runner.configPath = writeConfig(&basicAuthConfig).Name() - }) + runner.Start() - Context("when a request to query health comes", func() { - It("returns with a 200", func() { - rsp, err := healthHttpClient.Get(fmt.Sprintf("http://127.0.0.1:%d/health", healthport)) - Expect(err).NotTo(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusOK)) - raw, _ := io.ReadAll(rsp.Body) - healthData := string(raw) - Expect(healthData).To(ContainSubstring("autoscaler_eventgenerator_concurrent_http_request")) - Expect(healthData).To(ContainSubstring("autoscaler_eventgenerator_policyDB")) - Expect(healthData).To(ContainSubstring("autoscaler_eventgenerator_appMetricDB")) - Expect(healthData).To(ContainSubstring("go_goroutines")) - Expect(healthData).To(ContainSubstring("go_memstats_alloc_bytes")) - rsp.Body.Close() + }) + When("a request to query health comes", func() { + It("returns with a 200", func() { + rsp, err := httpClientForHealth.Get(healthURL.String()) + Expect(err).NotTo(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusOK)) + + raw, err := io.ReadAll(rsp.Body) + Expect(err).NotTo(HaveOccurred()) + + healthData := string(raw) + Expect(healthData).To(ContainSubstring("autoscaler_eventgenerator_concurrent_http_request")) + Expect(healthData).To(ContainSubstring("autoscaler_eventgenerator_policyDB")) + Expect(healthData).To(ContainSubstring("autoscaler_eventgenerator_appMetricDB")) + Expect(healthData).To(ContainSubstring("go_goroutines")) + Expect(healthData).To(ContainSubstring("go_memstats_alloc_bytes")) + rsp.Body.Close() + }) }) }) - }) - Describe("when Health server is ready to serve RESTful API with basic Auth", func() { - BeforeEach(func() { - runner.Start() - }) - Context("when username and password are incorrect for basic authentication during health check", func() { - It("should return 401", func() { + When("Health server is ready to serve RESTful API with basic Auth", func() { + BeforeEach(func() { + runner.Start() + }) - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) - Expect(err).NotTo(HaveOccurred()) + When("username and password are incorrect for basic authentication during health check", func() { + It("should return 401", func() { + req, err := http.NewRequest(http.MethodGet, healthURL.String(), nil) + Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth("wrongusername", "wrongpassword") + req.SetBasicAuth("wrongusername", "wrongpassword") - rsp, err := healthHttpClient.Do(req) - Expect(err).ToNot(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusUnauthorized)) + rsp, err := httpClientForHealth.Do(req) + Expect(err).ToNot(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusUnauthorized)) + }) }) - }) - Context("when username and password are correct for basic authentication during health check", func() { - It("should return 200", func() { + When("username and password are correct for basic authentication during health check", func() { + It("should return 200", func() { + req, err := http.NewRequest(http.MethodGet, healthURL.String(), nil) + Expect(err).NotTo(HaveOccurred()) - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) - Expect(err).NotTo(HaveOccurred()) - - req.SetBasicAuth(conf.Health.HealthCheckUsername, conf.Health.HealthCheckPassword) + req.SetBasicAuth(conf.Health.BasicAuth.Username, conf.Health.BasicAuth.Password) - rsp, err := healthHttpClient.Do(req) - Expect(err).ToNot(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusOK)) + rsp, err := httpClientForHealth.Do(req) + Expect(err).ToNot(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusOK)) + }) }) }) - }) - Describe("when Health server is ready to serve RESTful API with basic Auth", func() { - BeforeEach(func() { - runner.Start() - }) - Context("when username and password are incorrect for basic authentication during health check", func() { - It("should return 401", func() { + When("Health server is ready to serve RESTful API with basic Auth", func() { + BeforeEach(func() { + runner.Start() + }) - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) - Expect(err).NotTo(HaveOccurred()) + When("username and password are incorrect for basic authentication during health check", func() { + It("should return 401", func() { + req, err := http.NewRequest(http.MethodGet, healthURL.String(), nil) + Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth("wrongusername", "wrongpassword") + req.SetBasicAuth("wrongusername", "wrongpassword") - rsp, err := healthHttpClient.Do(req) - Expect(err).ToNot(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusUnauthorized)) + rsp, err := httpClientForHealth.Do(req) + Expect(err).ToNot(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusUnauthorized)) + }) }) - }) - Context("when username and password are correct for basic authentication during health check", func() { - It("should return 200", func() { + When("username and password are correct for basic authentication during health check", func() { + It("should return 200", func() { + req, err := http.NewRequest(http.MethodGet, healthURL.String(), nil) + Expect(err).NotTo(HaveOccurred()) - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) - Expect(err).NotTo(HaveOccurred()) + req.SetBasicAuth(conf.Health.BasicAuth.Username, conf.Health.BasicAuth.Password) - req.SetBasicAuth(conf.Health.HealthCheckUsername, conf.Health.HealthCheckPassword) - - rsp, err := healthHttpClient.Do(req) - Expect(err).ToNot(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusOK)) + rsp, err := httpClientForHealth.Do(req) + Expect(err).ToNot(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusOK)) + }) }) }) }) diff --git a/src/autoscaler/eventgenerator/cmd/eventgenerator/main.go b/src/autoscaler/eventgenerator/cmd/eventgenerator/main.go index d16ec0196b..47c41d7f5e 100644 --- a/src/autoscaler/eventgenerator/cmd/eventgenerator/main.go +++ b/src/autoscaler/eventgenerator/cmd/eventgenerator/main.go @@ -2,7 +2,6 @@ package main import ( "io" - "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db/sqldb" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/eventgenerator/aggregator" @@ -13,6 +12,7 @@ import ( "code.cloudfoundry.org/app-autoscaler/src/autoscaler/healthendpoint" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" + "github.com/prometheus/client_golang/prometheus" circuit "github.com/rubyist/circuitbreaker" "flag" @@ -21,7 +21,6 @@ import ( "code.cloudfoundry.org/clock" "code.cloudfoundry.org/lager/v3" - "github.com/prometheus/client_golang/prometheus" "github.com/tedsuo/ifrit" "github.com/tedsuo/ifrit/grouper" "github.com/tedsuo/ifrit/sigmon" @@ -103,19 +102,22 @@ func main() { eventGenerator := ifrit.RunFunc(runFunc(appManager, evaluators, evaluationManager, metricPollers, anAggregator)) - httpServer, err := server.NewServer(logger.Session("http_server"), conf, appManager.QueryAppMetrics, httpStatusCollector) + httpServer := server.NewServer(logger.Session("http_server"), conf, appMetricDB, policyDb, appManager.QueryAppMetrics, httpStatusCollector) + + vmServer, err := httpServer.GetMtlsServer() if err != nil { logger.Error("failed to create http server", err) os.Exit(1) } - healthServer, err := healthendpoint.NewServerWithBasicAuth(conf.Health, []healthendpoint.Checker{}, logger.Session("health-server"), promRegistry, time.Now) + + healthServer, err := httpServer.GetHealthServer() if err != nil { logger.Error("failed to create health server", err) os.Exit(1) } members := grouper.Members{ {"eventGenerator", eventGenerator}, - {"http_server", httpServer}, + {"https_server", vmServer}, {"health_server", healthServer}, } monitor := ifrit.Invoke(sigmon.New(grouper.NewOrdered(os.Interrupt, members))) @@ -162,7 +164,8 @@ func loadConfig(path string) (*config.Config, error) { } configFileBytes, err := io.ReadAll(configFile) - _ = configFile.Close() + defer func() { _ = configFile.Close() }() + if err != nil { return nil, fmt.Errorf("failed to read data from config file %q: %w", path, err) } @@ -182,7 +185,7 @@ func loadConfig(path string) (*config.Config, error) { func createEvaluators(logger lager.Logger, conf *config.Config, triggersChan chan []*models.Trigger, queryMetrics aggregator.QueryAppMetricsFunc, getBreaker func(string) *circuit.Breaker, setCoolDownExpired func(string, int64)) ([]*generator.Evaluator, error) { count := conf.Evaluator.EvaluatorCount - aClient, err := helpers.CreateHTTPClient(&conf.ScalingEngine.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("scaling_client")) + seClient, err := helpers.CreateHTTPSClient(&conf.ScalingEngine.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("scaling_client")) if err != nil { logger.Error("failed to create http client for ScalingEngine", err, lager.Data{"scalingengineTLS": conf.ScalingEngine.TLSClientCerts}) os.Exit(1) @@ -190,7 +193,7 @@ func createEvaluators(logger lager.Logger, conf *config.Config, triggersChan cha evaluators := make([]*generator.Evaluator, count) for i := 0; i < count; i++ { - evaluators[i] = generator.NewEvaluator(logger, aClient, conf.ScalingEngine.ScalingEngineURL, triggersChan, + evaluators[i] = generator.NewEvaluator(logger, seClient, conf.ScalingEngine.ScalingEngineURL, triggersChan, conf.DefaultBreachDurationSecs, queryMetrics, getBreaker, setCoolDownExpired) } diff --git a/src/autoscaler/eventgenerator/config/config_test.go b/src/autoscaler/eventgenerator/config/config_test.go index 05f3108054..192143cf4c 100644 --- a/src/autoscaler/eventgenerator/config/config_test.go +++ b/src/autoscaler/eventgenerator/config/config_test.go @@ -42,7 +42,8 @@ server: node_addrs: [address1, address2] node_index: 1 health: - port: 9999 + server_config: + port: 9999 db: policy_db: url: postgres://postgres:password@localhost/autoscaler?sslmode=disable @@ -1060,7 +1061,8 @@ metricCollector: defaultStatWindowSecs: 300 defaultBreachDurationSecs: 300 health: - port: NOT-INTEGER-VALUE + server_config: + port: NOT-INTEGER-VALUE `) }) diff --git a/src/autoscaler/eventgenerator/server/server.go b/src/autoscaler/eventgenerator/server/server.go index 195af0e2ac..60144e49ea 100644 --- a/src/autoscaler/eventgenerator/server/server.go +++ b/src/autoscaler/eventgenerator/server/server.go @@ -1,8 +1,11 @@ package server import ( + "fmt" "net/http" + "time" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/eventgenerator/aggregator" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux" @@ -13,6 +16,7 @@ import ( "code.cloudfoundry.org/lager/v3" "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" "github.com/tedsuo/ifrit" ) @@ -22,19 +26,87 @@ func (vh VarsFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) vh(w, r, vars) } - -func NewServer(logger lager.Logger, conf *config.Config, queryAppMetric aggregator.QueryAppMetricsFunc, httpStatusCollector healthendpoint.HTTPStatusCollector) (ifrit.Runner, error) { - eh := NewEventGenHandler(logger, queryAppMetric) +func createEventGeneratorRouter(logger lager.Logger, queryAppMetric aggregator.QueryAppMetricsFunc, httpStatusCollector healthendpoint.HTTPStatusCollector, serverConfig config.ServerConfig) (*mux.Router, error) { httpStatusCollectMiddleware := healthendpoint.NewHTTPStatusCollectMiddleware(httpStatusCollector) + eh := NewEventGenHandler(logger, queryAppMetric) r := routes.EventGeneratorRoutes() r.Use(otelmux.Middleware("eventgenerator")) r.Use(httpStatusCollectMiddleware.Collect) r.Get(routes.GetAggregatedMetricHistoriesRouteName).Handler(VarsFunc(eh.GetAggregatedMetricHistories)) + return r, nil +} - httpServerConfig := helpers.ServerConfig{ - Port: conf.Server.Port, +type Server struct { + logger lager.Logger + conf *config.Config + appMetricDB db.AppMetricDB + policyDb db.PolicyDB + queryAppMetric aggregator.QueryAppMetricsFunc + httpStatusCollector healthendpoint.HTTPStatusCollector +} + +func (s *Server) GetMtlsServer() (ifrit.Runner, error) { + + eventGeneratorRouter, err := createEventGeneratorRouter(s.logger, s.queryAppMetric, s.httpStatusCollector, s.conf.Server) + if err != nil { + return nil, fmt.Errorf("failed to create event generator router: %w", err) + } + + return helpers.NewHTTPServer(s.logger, serverConfigFrom(s.conf), eventGeneratorRouter) +} + +func NewServer(logger lager.Logger, conf *config.Config, appMetricDB db.AppMetricDB, policyDb db.PolicyDB, queryAppMetric aggregator.QueryAppMetricsFunc, httpStatusCollector healthendpoint.HTTPStatusCollector) *Server { + return &Server{ + logger: logger, + conf: conf, + appMetricDB: appMetricDB, + policyDb: policyDb, + queryAppMetric: queryAppMetric, + httpStatusCollector: httpStatusCollector, + } +} + +func serverConfigFrom(conf *config.Config) helpers.ServerConfig { + return helpers.ServerConfig{ TLS: conf.Server.TLS, + Port: conf.Server.Port, } +} + +func (s *Server) GetHealthServer() (ifrit.Runner, error) { + + healthRouter, err := createHealthRouter(s.appMetricDB, s.policyDb, s.logger, s.conf, s.httpStatusCollector) + if err != nil { + return nil, fmt.Errorf("failed to create health router: %w", err) + } + return helpers.NewHTTPServer(s.logger, s.conf.Health.ServerConfig, healthRouter) +} + +func createHealthRouter(appMetricDB db.AppMetricDB, policyDb db.PolicyDB, logger lager.Logger, conf *config.Config, httpStatusCollector healthendpoint.HTTPStatusCollector) (*mux.Router, error) { + checkers := []healthendpoint.Checker{} + gatherer := CreatePrometheusRegistry(appMetricDB, policyDb, httpStatusCollector, logger) + healthRouter, err := healthendpoint.NewHealthRouter(conf.Health, checkers, logger.Session("health-server"), gatherer, time.Now) + if err != nil { + return nil, fmt.Errorf("failed to create health router: %w", err) + } + + return healthRouter, nil +} + +func CreatePrometheusRegistry(appMetricDB db.AppMetricDB, policyDb db.PolicyDB, httpStatusCollector healthendpoint.HTTPStatusCollector, logger lager.Logger) *prometheus.Registry { + promRegistry := prometheus.NewRegistry() + healthendpoint.RegisterCollectors(promRegistry, []prometheus.Collector{ + healthendpoint.NewDatabaseStatusCollector("autoscaler", "eventgenerator", "appMetricDB", appMetricDB), + healthendpoint.NewDatabaseStatusCollector("autoscaler", "eventgenerator", "policyDB", policyDb), + httpStatusCollector, + }, true, logger.Session("eventgenerator-prometheus")) + return promRegistry +} - return helpers.NewHTTPServer(logger, httpServerConfig, r) +func setupMainRouter(egRouter, healthRouter *mux.Router) *mux.Router { + mainRouter := mux.NewRouter() + mainRouter.PathPrefix("/v1").Handler(egRouter) + //mainRouter.PathPrefix("/health").Handler(healthRouter) + //mainRouter.PathPrefix("/").Handler(healthRouter) + return mainRouter } diff --git a/src/autoscaler/eventgenerator/server/server_suite_test.go b/src/autoscaler/eventgenerator/server/server_suite_test.go index 0868c4d8c4..957f3f9ff4 100644 --- a/src/autoscaler/eventgenerator/server/server_suite_test.go +++ b/src/autoscaler/eventgenerator/server/server_suite_test.go @@ -1,57 +1,13 @@ package server_test import ( - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/eventgenerator/config" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/eventgenerator/server" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" - - "net/url" - "strconv" "testing" - "code.cloudfoundry.org/lager/v3" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/tedsuo/ifrit" - "github.com/tedsuo/ifrit/ginkgomon_v2" -) - -var ( - serverProcess ifrit.Process - serverUrl *url.URL ) func TestServer(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Server Suite") } - -var _ = BeforeSuite(func() { - port := 1111 + GinkgoParallelProcess() - conf := &config.Config{ - Server: config.ServerConfig{ - ServerConfig: helpers.ServerConfig{ - Port: port, - }, - }, - } - queryAppMetrics := func(appID string, metricType string, start int64, end int64, orderType db.OrderType) ([]*models.AppMetric, error) { - return nil, nil - } - - httpStatusCollector := &fakes.FakeHTTPStatusCollector{} - httpServer, err := server.NewServer(lager.NewLogger("test"), conf, queryAppMetrics, httpStatusCollector) - Expect(err).NotTo(HaveOccurred()) - - serverUrl, err = url.Parse("http://127.0.0.1:" + strconv.Itoa(port)) - Expect(err).ToNot(HaveOccurred()) - - serverProcess = ginkgomon_v2.Invoke(httpServer) -}) - -var _ = AfterSuite(func() { - ginkgomon_v2.Interrupt(serverProcess) -}) diff --git a/src/autoscaler/eventgenerator/server/server_test.go b/src/autoscaler/eventgenerator/server/server_test.go index 1727cb8dd1..391f45de7f 100644 --- a/src/autoscaler/eventgenerator/server/server_test.go +++ b/src/autoscaler/eventgenerator/server/server_test.go @@ -2,22 +2,73 @@ package server_test import ( "net/http" + "net/url" + "strconv" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/eventgenerator/aggregator" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/eventgenerator/config" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/eventgenerator/server" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" + "code.cloudfoundry.org/lager/v3" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/tedsuo/ifrit" + "github.com/tedsuo/ifrit/ginkgomon_v2" ) -const TestPathAggregatedMetricHistories = "/v1/apps/an-app-id/aggregated_metric_histories/a-metric-type" - var _ = Describe("Server", func() { var ( - rsp *http.Response - err error + rsp *http.Response + err error + serverProcess ifrit.Process + serverUrl *url.URL + policyDB *fakes.FakePolicyDB + httpStatusCollector *fakes.FakeHTTPStatusCollector + + appMetricDB *fakes.FakeAppMetricDB + conf *config.Config + queryAppMetrics aggregator.QueryAppMetricsFunc ) - Context("when retrieving aggregared metrics history", func() { + BeforeEach(func() { + port := 1111 + GinkgoParallelProcess() + conf = &config.Config{ + Server: config.ServerConfig{ + ServerConfig: helpers.ServerConfig{ + Port: port, + }, + }, + } + + serverUrl, err = url.Parse("http://127.0.0.1:" + strconv.Itoa(port)) + Expect(err).ToNot(HaveOccurred()) + + queryAppMetrics = func(appID string, metricType string, start int64, end int64, orderType db.OrderType) ([]*models.AppMetric, error) { + return nil, nil + } + + httpStatusCollector = &fakes.FakeHTTPStatusCollector{} + policyDB = &fakes.FakePolicyDB{} + appMetricDB = &fakes.FakeAppMetricDB{} + + }) + + AfterEach(func() { + ginkgomon_v2.Interrupt(serverProcess) + }) + + JustBeforeEach(func() { + httpServer, err := server.NewServer(lager.NewLogger("test"), conf, appMetricDB, policyDB, queryAppMetrics, httpStatusCollector).GetMtlsServer() + Expect(err).NotTo(HaveOccurred()) + serverProcess = ginkgomon_v2.Invoke(httpServer) + }) + + Describe("request on /v1/apps/an-app-id/aggregated_metric_histories/a-metric-type", func() { BeforeEach(func() { - serverUrl.Path = TestPathAggregatedMetricHistories + serverUrl.Path = "/v1/apps/an-app-id/aggregated_metric_histories/a-metric-type" }) JustBeforeEach(func() { @@ -29,9 +80,20 @@ var _ = Describe("Server", func() { Expect(rsp.StatusCode).To(Equal(http.StatusOK)) rsp.Body.Close() }) + When("using wrong method to retrieve aggregared metrics history", func() { + JustBeforeEach(func() { + rsp, err = http.Post(serverUrl.String(), "garbage", nil) + }) + + It("should return 405", func() { + Expect(err).ToNot(HaveOccurred()) + Expect(rsp.StatusCode).To(Equal(http.StatusMethodNotAllowed)) + rsp.Body.Close() + }) + }) }) - Context("when requesting the wrong path", func() { + When("requesting the wrong path", func() { BeforeEach(func() { serverUrl.Path = "/not-exist-path" }) @@ -45,22 +107,6 @@ var _ = Describe("Server", func() { Expect(rsp.StatusCode).To(Equal(http.StatusNotFound)) rsp.Body.Close() }) - }) - - Context("when using wrong method to retrieve aggregared metrics history", func() { - BeforeEach(func() { - serverUrl.Path = TestPathAggregatedMetricHistories - }) - - JustBeforeEach(func() { - rsp, err = http.Post(serverUrl.String(), "garbage", nil) - }) - It("should return 405", func() { - Expect(err).ToNot(HaveOccurred()) - Expect(rsp.StatusCode).To(Equal(http.StatusMethodNotAllowed)) - rsp.Body.Close() - }) }) - }) From 9285c855e353131da2f228ed24750d3ea5243ed3 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:39:37 +0200 Subject: [PATCH 07/39] Refactor HealthConfig to use BasicAuth model and update HTTP client creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Replace individual HealthCheckUsername and HealthCheckPassword fields with a BasicAuth struct in HealthConfig. • Update validation logic in HealthConfig to work with the new BasicAuth fields. • Modify tests to reflect changes in HealthConfig structure. • Implement TransportWithBasicAuth to add basic authentication headers to HTTP requests. • Adjust CreateHTTPClient function to accept BasicAuth and use TransportWithBasicAuth. • Add BasicAuth struct to models package to encapsulate basic authentication data. --- src/autoscaler/helpers/health.go | 26 ++++++++--------- src/autoscaler/helpers/health_test.go | 34 +++++++++++----------- src/autoscaler/helpers/httpclient.go | 42 ++++++++++++++++++++++++++- src/autoscaler/models/security.go | 7 +++++ 4 files changed, 77 insertions(+), 32 deletions(-) diff --git a/src/autoscaler/helpers/health.go b/src/autoscaler/helpers/health.go index 6631359ba7..96bea4a603 100644 --- a/src/autoscaler/helpers/health.go +++ b/src/autoscaler/helpers/health.go @@ -3,46 +3,44 @@ package helpers import ( "fmt" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" "golang.org/x/crypto/bcrypt" ) type HealthConfig struct { - ServerConfig `yaml:",inline"` - HealthCheckUsername string `yaml:"username"` - HealthCheckUsernameHash string `yaml:"username_hash"` - HealthCheckPassword string `yaml:"password"` - HealthCheckPasswordHash string `yaml:"password_hash"` - ReadinessCheckEnabled bool `yaml:"readiness_enabled"` + ServerConfig ServerConfig `yaml:"server_config"` + BasicAuth models.BasicAuth `yaml:"basic_auth"` + ReadinessCheckEnabled bool `yaml:"readiness_enabled"` } var ErrConfiguration = fmt.Errorf("configuration error") func (c *HealthConfig) Validate() error { - if c.HealthCheckUsername != "" && c.HealthCheckUsernameHash != "" { + if c.BasicAuth.Username != "" && c.BasicAuth.UsernameHash != "" { return fmt.Errorf("%w: both healthcheck username and healthcheck username_hash are set, please provide only one of them", ErrConfiguration) } - if c.HealthCheckPassword != "" && c.HealthCheckPasswordHash != "" { + if c.BasicAuth.Password != "" && c.BasicAuth.PasswordHash != "" { return fmt.Errorf("%w: both healthcheck password and healthcheck password_hash are provided, please provide only one of them", ErrConfiguration) } - if c.HealthCheckUsernameHash != "" { - if _, err := bcrypt.Cost([]byte(c.HealthCheckUsernameHash)); err != nil { + if c.BasicAuth.UsernameHash != "" { + if _, err := bcrypt.Cost([]byte(c.BasicAuth.UsernameHash)); err != nil { return fmt.Errorf("%w: healthcheck username_hash is not a valid bcrypt hash", ErrConfiguration) } } - if c.HealthCheckPasswordHash != "" { - if _, err := bcrypt.Cost([]byte(c.HealthCheckPasswordHash)); err != nil { + if c.BasicAuth.PasswordHash != "" { + if _, err := bcrypt.Cost([]byte(c.BasicAuth.PasswordHash)); err != nil { return fmt.Errorf("%w: healthcheck password_hash is not a valid bcrypt hash", ErrConfiguration) } } - if c.HealthCheckUsername == "" && c.HealthCheckPassword != "" { + if c.BasicAuth.Username == "" && c.BasicAuth.Password != "" { return fmt.Errorf("%w: healthcheck username is empty", ErrConfiguration) } - if c.HealthCheckUsername != "" && c.HealthCheckPassword == "" { + if c.BasicAuth.Username != "" && c.BasicAuth.Password == "" { return fmt.Errorf("%w: healthcheck password is empty", ErrConfiguration) } diff --git a/src/autoscaler/helpers/health_test.go b/src/autoscaler/helpers/health_test.go index 5a3a411b88..1df5b4acc4 100644 --- a/src/autoscaler/helpers/health_test.go +++ b/src/autoscaler/helpers/health_test.go @@ -4,6 +4,7 @@ import ( "errors" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" . "github.com/onsi/ginkgo/v2" @@ -25,9 +26,9 @@ var _ = Describe("Health Config", func() { When("Readiness is not supplied", func() { BeforeEach(func() { healthConfigBytes = []byte(` -port: 9999 -username: test-username -password: password +basic_auth: + username: test-username + password: password readiness_enabled: false `) }) @@ -38,11 +39,10 @@ readiness_enabled: false Expect(err).ToNot(HaveOccurred()) Expect(healthConfig).To(Equal(helpers.HealthConfig{ - ServerConfig: helpers.ServerConfig{ - Port: 9999, + BasicAuth: models.BasicAuth{ + Username: "test-username", + Password: "password", }, - HealthCheckUsername: "test-username", - HealthCheckPassword: "password", ReadinessCheckEnabled: false, })) }) @@ -51,8 +51,9 @@ readiness_enabled: false BeforeEach(func() { healthConfigBytes = []byte(` port: 9999 -username: test-username -password: password +basic_auth: + username: test-username + password: password readiness_enabled: true `) }) @@ -63,11 +64,10 @@ readiness_enabled: true Expect(err).ToNot(HaveOccurred()) Expect(healthConfig).To(Equal(helpers.HealthConfig{ - ServerConfig: helpers.ServerConfig{ - Port: 9999, + BasicAuth: models.BasicAuth{ + Username: "test-username", + Password: "password", }, - HealthCheckUsername: "test-username", - HealthCheckPassword: "password", ReadinessCheckEnabled: true, })) }) @@ -76,10 +76,10 @@ readiness_enabled: true When("both password password_hash are supplied", func() { BeforeEach(func() { healthConfigBytes = []byte(` -port: 9999 -username: test-username -password: password -password_hash: password_hash +basic_auth: + username: test-username + password: password + password_hash: password_hash `) }) It("should fail validation", func() { diff --git a/src/autoscaler/helpers/httpclient.go b/src/autoscaler/helpers/httpclient.go index 5fcd0b93ad..49076b390d 100644 --- a/src/autoscaler/helpers/httpclient.go +++ b/src/autoscaler/helpers/httpclient.go @@ -1,6 +1,7 @@ package helpers import ( + "encoding/base64" "fmt" "net/http" "time" @@ -13,13 +14,52 @@ import ( "code.cloudfoundry.org/cfhttp/v2" ) +type TransportWithBasicAuth struct { + Username string + Password string + Base http.RoundTripper +} + +func (t *TransportWithBasicAuth) base() http.RoundTripper { + if t.Base != nil { + return t.Base + } + return http.DefaultTransport +} + +func (t *TransportWithBasicAuth) RoundTrip(req *http.Request) (*http.Response, error) { + credentials := t.Username + ":" + t.Password + basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(credentials)) + fmt.Println("banana TransportWithBasicAuth:credentials", credentials) + fmt.Println("banana TransportWithBasicAuth:", basicAuth) + req.Header.Add("Authorization", basicAuth) + return t.base().RoundTrip(req) +} + func DefaultClientConfig() cf.ClientConfig { return cf.ClientConfig{ MaxIdleConnsPerHost: 200, IdleConnectionTimeoutMs: 5 * 1000, } } -func CreateHTTPClient(tlsCerts *models.TLSCerts, config cf.ClientConfig, logger lager.Logger) (*http.Client, error) { + +func CreateHTTPClient(ba *models.BasicAuth, config cf.ClientConfig, logger lager.Logger) (*http.Client, error) { + client := cfhttp.NewClient( + cfhttp.WithDialTimeout(30*time.Second), + cfhttp.WithIdleConnTimeout(time.Duration(config.IdleConnectionTimeoutMs)*time.Millisecond), + cfhttp.WithMaxIdleConnsPerHost(config.MaxIdleConnsPerHost), + ) + + client = cf.RetryClient(config, client, logger) + client.Transport = &TransportWithBasicAuth{ + Username: ba.Username, + Password: ba.Password, + } + + return client, nil +} + +func CreateHTTPSClient(tlsCerts *models.TLSCerts, config cf.ClientConfig, logger lager.Logger) (*http.Client, error) { tlsConfig, err := tlsCerts.CreateClientConfig() if err != nil { return nil, fmt.Errorf("failed to create tls config: %w", err) diff --git a/src/autoscaler/models/security.go b/src/autoscaler/models/security.go index cf94d8d5d5..62ca0e9e36 100644 --- a/src/autoscaler/models/security.go +++ b/src/autoscaler/models/security.go @@ -6,6 +6,13 @@ import ( "code.cloudfoundry.org/tlsconfig" ) +type BasicAuth struct { + Username string `yaml:"username"` + UsernameHash string `yaml:"username_hash"` + Password string `yaml:"password"` + PasswordHash string `yaml:"password_hash"` +} + type TLSCerts struct { KeyFile string `yaml:"key_file" json:"keyFile"` CertFile string `yaml:"cert_file" json:"certFile"` From aa9a71cf01798a7b70c7c4b422808bef9c88be2b Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:39:59 +0200 Subject: [PATCH 08/39] Refactor basic auth configuration in healthendpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Rename HealthCheckUsername and related fields to BasicAuth struct fields • Update health readiness tests to use new BasicAuth struct fields • Remove basic auth middleware implementation from server.go • Simplify health router creation by using helpers.CreateBasicAuthMiddleware --- .../healthendpoint/health_readiness_test.go | 32 +++--- src/autoscaler/healthendpoint/server.go | 108 ++---------------- 2 files changed, 24 insertions(+), 116 deletions(-) diff --git a/src/autoscaler/healthendpoint/health_readiness_test.go b/src/autoscaler/healthendpoint/health_readiness_test.go index 8c254bd941..da001048ce 100644 --- a/src/autoscaler/healthendpoint/health_readiness_test.go +++ b/src/autoscaler/healthendpoint/health_readiness_test.go @@ -48,10 +48,10 @@ var _ = Describe("Health Readiness", func() { logger = lager.NewLogger("healthendpoint-test") logger.RegisterSink(lager.NewWriterSink(GinkgoWriter, lager.DEBUG)) - config.HealthCheckUsername = "test-user-name" - config.HealthCheckPassword = "test-user-password" - config.HealthCheckPasswordHash = "" - config.HealthCheckUsernameHash = "" + config.BasicAuth.Username = "test-user-name" + config.BasicAuth.Password = "test-user-password" + config.BasicAuth.PasswordHash = "" + config.BasicAuth.UsernameHash = "" config.ReadinessCheckEnabled = true checkers = []healthendpoint.Checker{} tmsttr := time.Now() @@ -67,10 +67,10 @@ var _ = Describe("Health Readiness", func() { Context("Authentication parameter checks", func() { When("username and password are defined", func() { BeforeEach(func() { - config.HealthCheckUsername = "username" - config.HealthCheckPassword = "password" - config.HealthCheckUsernameHash = "" - config.HealthCheckPasswordHash = "" + config.BasicAuth.Username = "username" + config.BasicAuth.Password = "password" + config.BasicAuth.UsernameHash = "" + config.BasicAuth.PasswordHash = "" }) When("Prometheus Health endpoint is called", func() { It("should require basic auth", func() { @@ -85,10 +85,10 @@ var _ = Describe("Health Readiness", func() { }) When("username_hash and password_hash are defined", func() { BeforeEach(func() { - config.HealthCheckUsername = "" - config.HealthCheckPassword = "" - config.HealthCheckUsernameHash = "username_hash" - config.HealthCheckPasswordHash = "username_hash" + config.BasicAuth.Username = "" + config.BasicAuth.Password = "" + config.BasicAuth.UsernameHash = "username_hash" + config.BasicAuth.PasswordHash = "username_hash" }) When("Prometheus Health endpoint is called without basic auth", func() { It("should require basic auth", func() { @@ -109,8 +109,8 @@ var _ = Describe("Health Readiness", func() { Context("without basic auth configured", func() { BeforeEach(func() { - config.HealthCheckUsername = "" - config.HealthCheckPassword = "" + config.BasicAuth.Username = "" + config.BasicAuth.Password = "" }) When("Prometheus Health endpoint is called", func() { It("should respond OK", func() { @@ -326,8 +326,8 @@ var _ = Describe("Health Readiness", func() { Context("pprof endpoint", func() { When("basic auth is not configured", func() { BeforeEach(func() { - config.HealthCheckUsername = "" - config.HealthCheckPassword = "" + config.BasicAuth.Username = "" + config.BasicAuth.Password = "" }) It("should not be available", func() { apitest.New(). diff --git a/src/autoscaler/healthendpoint/server.go b/src/autoscaler/healthendpoint/server.go index 4e4ca9272e..5c0d5c5211 100644 --- a/src/autoscaler/healthendpoint/server.go +++ b/src/autoscaler/healthendpoint/server.go @@ -25,7 +25,6 @@ package healthendpoint // - scheduler import ( - "net/http" "net/http/pprof" "time" @@ -36,50 +35,17 @@ import ( "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/tedsuo/ifrit" - "golang.org/x/crypto/bcrypt" ) // basic authentication credentials struct -type basicAuthenticationMiddleware struct { - usernameHash []byte - passwordHash []byte -} - -// middleware basic authentication middleware functionality for healthcheck -func (bam *basicAuthenticationMiddleware) middleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - username, password, authOK := r.BasicAuth() - - if !authOK || bcrypt.CompareHashAndPassword(bam.usernameHash, []byte(username)) != nil || bcrypt.CompareHashAndPassword(bam.passwordHash, []byte(password)) != nil { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - next.ServeHTTP(w, r) - }) -} - -// NewServerWithBasicAuth open the healthcheck port with basic authentication. -// Make sure that username and password is not empty -func NewServerWithBasicAuth(conf helpers.HealthConfig, healthCheckers []Checker, logger lager.Logger, gatherer prometheus.Gatherer, time func() time.Time) (ifrit.Runner, error) { - healthRouter, err := NewHealthRouter(conf, healthCheckers, logger, gatherer, time) - if err != nil { - return nil, err - } - httpServerConfig := helpers.ServerConfig{ - Port: conf.Port, - TLS: conf.TLS, - } - return helpers.NewHTTPServer(logger, httpServerConfig, healthRouter) -} func NewHealthRouter(conf helpers.HealthConfig, healthCheckers []Checker, logger lager.Logger, gatherer prometheus.Gatherer, time func() time.Time) (*mux.Router, error) { var healthRouter *mux.Router var err error - username := conf.HealthCheckUsername - password := conf.HealthCheckPassword - usernameHash := conf.HealthCheckUsernameHash - passwordHash := conf.HealthCheckPasswordHash + username := conf.BasicAuth.Username + password := conf.BasicAuth.Password + usernameHash := conf.BasicAuth.UsernameHash + passwordHash := conf.BasicAuth.PasswordHash if username == "" && password == "" && usernameHash == "" && passwordHash == "" { //when username and password are not set then don't use basic authentication healthRouter = mux.NewRouter() @@ -97,7 +63,7 @@ func NewHealthRouter(conf helpers.HealthConfig, healthCheckers []Checker, logger } func healthBasicAuthRouter(conf helpers.HealthConfig, healthCheckers []Checker, logger lager.Logger, gatherer prometheus.Gatherer, time func() time.Time) (*mux.Router, error) { - basicAuthentication, err := createBasicAuthMiddleware(logger, conf.HealthCheckUsernameHash, conf.HealthCheckUsername, conf.HealthCheckPasswordHash, conf.HealthCheckPassword) + ba, err := helpers.CreateBasicAuthMiddleware(logger, conf.BasicAuth) if err != nil { return nil, err } @@ -111,10 +77,9 @@ func healthBasicAuthRouter(conf helpers.HealthConfig, healthCheckers []Checker, } //authenticated paths health := router.Path("/health").Subrouter() - health.Use(basicAuthentication.middleware) - + health.Use(ba.BasicAuthenticationMiddleware) pprofRouter := router.PathPrefix("/debug/pprof").Subrouter() - pprofRouter.Use(basicAuthentication.middleware) + pprofRouter.Use(ba.BasicAuthenticationMiddleware) pprofRouter.HandleFunc("/cmdline", pprof.Cmdline) pprofRouter.HandleFunc("/profile", pprof.Profile) @@ -123,65 +88,8 @@ func healthBasicAuthRouter(conf helpers.HealthConfig, healthCheckers []Checker, pprofRouter.PathPrefix("").HandlerFunc(pprof.Index) everything := router.PathPrefix("").Subrouter() - everything.Use(basicAuthentication.middleware) + everything.Use(ba.BasicAuthenticationMiddleware) everything.PathPrefix("").Handler(promHandler) return router, nil } - -func createBasicAuthMiddleware(logger lager.Logger, usernameHash string, username string, passwordHash string, password string) (*basicAuthenticationMiddleware, error) { - usernameHashByte, err := getUserHashBytes(logger, usernameHash, username) - if err != nil { - return nil, err - } - - passwordHashByte, err := getPasswordHashBytes(logger, passwordHash, password) - if err != nil { - return nil, err - } - - basicAuthentication := &basicAuthenticationMiddleware{ - usernameHash: usernameHashByte, - passwordHash: passwordHashByte, - } - return basicAuthentication, nil -} - -func getPasswordHashBytes(logger lager.Logger, passwordHash string, password string) ([]byte, error) { - var passwordHashByte []byte - var err error - if passwordHash == "" { - if len(password) > 72 { - logger.Error("warning-configured-password-too-long-using-only-first-72-characters", bcrypt.ErrPasswordTooLong, lager.Data{"password-length": len(password)}) - password = password[:72] - } - passwordHashByte, err = bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost) // use MinCost as the config already provided it as cleartext - if err != nil { - logger.Error("failed-new-server-password", err) - return nil, err - } - } else { - passwordHashByte = []byte(passwordHash) - } - return passwordHashByte, nil -} - -func getUserHashBytes(logger lager.Logger, usernameHash string, username string) ([]byte, error) { - var usernameHashByte []byte - var err error - if usernameHash == "" { - if len(username) > 72 { - logger.Error("warning-configured-username-too-long-using-only-first-72-characters", bcrypt.ErrPasswordTooLong, lager.Data{"username-length": len(username)}) - username = username[:72] - } - // when username and password are set for health check - usernameHashByte, err = bcrypt.GenerateFromPassword([]byte(username), bcrypt.MinCost) // use MinCost as the config already provided it as cleartext - if err != nil { - logger.Error("failed-new-server-username", err) - return nil, err - } - } else { - usernameHashByte = []byte(usernameHash) - } - return usernameHashByte, err -} From 2821c00291f9ef09c2d13278bbb4b418b9f000b1 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:40:27 +0200 Subject: [PATCH 09/39] Refactor health endpoint and update HTTP client creation - Replace direct Prometheus registry creation with `createPrometheusRegistry` function. - Change `CreateHTTPClient` to `CreateHTTPSClient` for both scaling engine and scheduler clients. - Update health endpoint creation to use `NewHealthRouter` and `NewHTTPServer`. - Adjust health check configuration structure in tests and sample config. --- src/autoscaler/operator/cmd/operator/main.go | 31 ++++++++++++------- .../cmd/operator/operator_suite_test.go | 6 ++-- .../operator/cmd/operator/operator_test.go | 24 +++++++------- src/autoscaler/operator/config/config_test.go | 6 ++-- .../operator/config/testdata/valid.yml | 3 +- 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/autoscaler/operator/cmd/operator/main.go b/src/autoscaler/operator/cmd/operator/main.go index 370fef6553..513fbcef95 100644 --- a/src/autoscaler/operator/cmd/operator/main.go +++ b/src/autoscaler/operator/cmd/operator/main.go @@ -82,20 +82,12 @@ func main() { policyDb := sqldb.CreatePolicyDb(conf.AppSyncer.DB, logger) defer func() { _ = policyDb.Close() }() - promRegistry := prometheus.NewRegistry() - healthendpoint.RegisterCollectors(promRegistry, []prometheus.Collector{ - healthendpoint.NewDatabaseStatusCollector("autoscaler", "operator", "policyDB", policyDb), - - healthendpoint.NewDatabaseStatusCollector("autoscaler", "operator", "appMetricsDB", appMetricsDB), - healthendpoint.NewDatabaseStatusCollector("autoscaler", "operator", "scalingEngineDB", scalingEngineDB), - }, true, logger.Session("operator-prometheus")) - - scalingEngineHttpclient, err := helpers.CreateHTTPClient(&conf.ScalingEngine.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("scaling_client")) + scalingEngineHttpclient, err := helpers.CreateHTTPSClient(&conf.ScalingEngine.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("scaling_client")) if err != nil { logger.Error("failed to create http client for scalingengine", err, lager.Data{"scalingengineTLS": conf.ScalingEngine.TLSClientCerts}) os.Exit(1) } - schedulerHttpclient, err := helpers.CreateHTTPClient(&conf.Scheduler.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("scheduler_client")) + schedulerHttpclient, err := helpers.CreateHTTPSClient(&conf.Scheduler.TLSClientCerts, helpers.DefaultClientConfig(), logger.Session("scheduler_client")) if err != nil { logger.Error("failed to create http client for scheduler", err, lager.Data{"schedulerTLS": conf.Scheduler.TLSClientCerts}) os.Exit(1) @@ -143,7 +135,14 @@ func main() { }) members = append(grouper.Members{{"db-lock-maintainer", dbLockMaintainer}}, members...) - healthServer, err := healthendpoint.NewServerWithBasicAuth(conf.Health, []healthendpoint.Checker{}, logger.Session("health-server"), promRegistry, time.Now) + gatherer := createPrometheusRegistry(policyDb, appMetricsDB, scalingEngineDB, logger) + healthRouter, err := healthendpoint.NewHealthRouter(conf.Health, []healthendpoint.Checker{}, logger, gatherer, time.Now) + if err != nil { + logger.Error("failed to create health router", err) + os.Exit(1) + } + + healthServer, err := helpers.NewHTTPServer(logger, conf.Health.ServerConfig, healthRouter) if err != nil { logger.Error("failed to create health server", err) os.Exit(1) @@ -162,3 +161,13 @@ func main() { logger.Info("exited") } + +func createPrometheusRegistry(policyDB db.PolicyDB, appMetricsDB db.AppMetricDB, scalingEngineDB db.ScalingEngineDB, logger lager.Logger) *prometheus.Registry { + promRegistry := prometheus.NewRegistry() + healthendpoint.RegisterCollectors(promRegistry, []prometheus.Collector{ + healthendpoint.NewDatabaseStatusCollector("autoscaler", "operator", "policyDB", policyDB), + healthendpoint.NewDatabaseStatusCollector("autoscaler", "operator", "appMetricsDB", appMetricsDB), + healthendpoint.NewDatabaseStatusCollector("autoscaler", "operator", "scalingEngineDB", scalingEngineDB), + }, true, logger.Session("operator-prometheus")) + return promRegistry +} diff --git a/src/autoscaler/operator/cmd/operator/operator_suite_test.go b/src/autoscaler/operator/cmd/operator/operator_suite_test.go index 3faa160def..c81ae5c029 100644 --- a/src/autoscaler/operator/cmd/operator/operator_suite_test.go +++ b/src/autoscaler/operator/cmd/operator/operator_suite_test.go @@ -74,7 +74,7 @@ func initConfig() { Secret: "secret", } healthport = 8000 + GinkgoParallelProcess() - cfg.Health.Port = healthport + cfg.Health.ServerConfig.Port = healthport cfg.Logging.Level = "debug" dbUrl := testhelpers.GetDbUrl() @@ -124,8 +124,8 @@ func initConfig() { cfg.AppSyncer.SyncInterval = 60 * time.Second cfg.HttpClientTimeout = 10 * time.Second - cfg.Health.HealthCheckUsername = "operatorhealthcheckuser" - cfg.Health.HealthCheckPassword = "operatorhealthcheckuser" + cfg.Health.BasicAuth.Username = "operatorhealthcheckuser" + cfg.Health.BasicAuth.Password = "operatorhealthcheckuser" } func writeConfig(c *config.Config) *os.File { diff --git a/src/autoscaler/operator/cmd/operator/operator_test.go b/src/autoscaler/operator/cmd/operator/operator_test.go index adc2e9f1aa..1db13b2099 100644 --- a/src/autoscaler/operator/cmd/operator/operator_test.go +++ b/src/autoscaler/operator/cmd/operator/operator_test.go @@ -133,9 +133,9 @@ var _ = Describe("Operator", Serial, func() { Eventually(runner.Session.Buffer, 5*time.Second).Should(Say("operator.started")) secondRunner = NewOperatorRunner() secondRunner.startCheck = "" - cfg.Health.HealthCheckUsername = "" - cfg.Health.HealthCheckPassword = "" - cfg.Health.Port = 9000 + GinkgoParallelProcess() + cfg.Health.BasicAuth.Username = "" + cfg.Health.BasicAuth.Password = "" + cfg.Health.ServerConfig.Port = 9000 + GinkgoParallelProcess() secondRunner.configPath = writeConfig(&cfg).Name() secondRunner.Start() @@ -150,7 +150,7 @@ var _ = Describe("Operator", Serial, func() { Consistently(secondRunner.Session.Buffer, 5*time.Second).ShouldNot(Say("operator.successfully-acquired-lock")) By("checking the health endpoint of the standing-by instance") - rsp, err := healthHttpClient.Get(fmt.Sprintf("http://127.0.0.1:%d/health", cfg.Health.Port)) + rsp, err := healthHttpClient.Get(fmt.Sprintf("http://127.0.0.1:%d/health", cfg.Health.ServerConfig.Port)) Expect(err).NotTo(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) @@ -166,9 +166,9 @@ var _ = Describe("Operator", Serial, func() { secondRunner = NewOperatorRunner() secondRunner.startCheck = "" - cfg.Health.HealthCheckUsername = "" - cfg.Health.HealthCheckPassword = "" - cfg.Health.Port = 9000 + GinkgoParallelProcess() + cfg.Health.BasicAuth.Username = "" + cfg.Health.BasicAuth.Password = "" + cfg.Health.ServerConfig.Port = 9000 + GinkgoParallelProcess() secondRunner.configPath = writeConfig(&cfg).Name() secondRunner.Start() }) @@ -210,7 +210,7 @@ var _ = Describe("Operator", Serial, func() { runner.Start() Eventually(runner.Session.Buffer, 10*time.Second).Should(Say("operator.started")) secondRunner = NewOperatorRunner() - cfg.Health.Port = 9000 + GinkgoParallelProcess() + cfg.Health.ServerConfig.Port = 9000 + GinkgoParallelProcess() secondRunner.configPath = writeConfig(&cfg).Name() secondRunner.startCheck = "" secondRunner.Start() @@ -315,8 +315,8 @@ var _ = Describe("Operator", Serial, func() { Describe("when Health server is ready to serve RESTful API", func() { BeforeEach(func() { basicAuthConfig := cfg - basicAuthConfig.Health.HealthCheckUsername = "" - basicAuthConfig.Health.HealthCheckPassword = "" + basicAuthConfig.Health.BasicAuth.Username = "" + basicAuthConfig.Health.BasicAuth.Password = "" runner.configPath = writeConfig(&basicAuthConfig).Name() runner.Start() @@ -375,7 +375,7 @@ var _ = Describe("Operator", Serial, func() { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth(cfg.Health.HealthCheckUsername, cfg.Health.HealthCheckPassword) + req.SetBasicAuth(cfg.Health.BasicAuth.Username, cfg.Health.BasicAuth.Password) rsp, err := healthHttpClient.Do(req) Expect(err).ToNot(HaveOccurred()) @@ -415,7 +415,7 @@ var _ = Describe("Operator", Serial, func() { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/health", healthport), nil) Expect(err).NotTo(HaveOccurred()) - req.SetBasicAuth(cfg.Health.HealthCheckUsername, cfg.Health.HealthCheckPassword) + req.SetBasicAuth(cfg.Health.BasicAuth.Username, cfg.Health.BasicAuth.Password) rsp, err := healthHttpClient.Do(req) Expect(err).ToNot(HaveOccurred()) diff --git a/src/autoscaler/operator/config/config_test.go b/src/autoscaler/operator/config/config_test.go index 936e3d19c0..80210fee9d 100644 --- a/src/autoscaler/operator/config/config_test.go +++ b/src/autoscaler/operator/config/config_test.go @@ -50,7 +50,7 @@ var _ = Describe("Config", func() { Expect(conf.CF.ClientID).To(Equal("client-id")) Expect(conf.CF.Secret).To(Equal("client-secret")) Expect(conf.CF.SkipSSLValidation).To(Equal(false)) - Expect(conf.Health.Port).To(Equal(9999)) + Expect(conf.Health.ServerConfig.Port).To(Equal(9999)) Expect(conf.Logging.Level).To(Equal("debug")) Expect(conf.AppMetricsDB.DB).To(Equal(db.DatabaseConfig{ @@ -95,7 +95,7 @@ var _ = Describe("Config", func() { Expect(err).NotTo(HaveOccurred()) Expect(conf.Logging.Level).To(Equal(config.DefaultLoggingLevel)) - Expect(conf.Health.Port).To(Equal(8081)) + Expect(conf.Health.ServerConfig.Port).To(Equal(8081)) Expect(conf.AppMetricsDB.DB).To(Equal(db.DatabaseConfig{ URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", MaxOpenConnections: 0, @@ -195,7 +195,7 @@ scheduler: conf.AppSyncer.DB.URL = "postgres://pqgotest:password@exampl.com/pqgotest" conf.DBLock.DB.URL = "postgres://pqgotest:password@exampl.com/pqgotest" conf.HttpClientTimeout = 10 * time.Second - conf.Health.Port = 8081 + conf.Health.ServerConfig.Port = 8081 }) diff --git a/src/autoscaler/operator/config/testdata/valid.yml b/src/autoscaler/operator/config/testdata/valid.yml index 08d141c610..51418f6e8a 100644 --- a/src/autoscaler/operator/config/testdata/valid.yml +++ b/src/autoscaler/operator/config/testdata/valid.yml @@ -5,7 +5,8 @@ cf: secret: client-secret skip_ssl_validation: false health: - port: 9999 + server_config: + port: 9999 logging: level: "debug" app_metrics_db: From 4f8ea8638d40036703ea36c660dd645ac7115915 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:40:45 +0200 Subject: [PATCH 10/39] Add BasicAuthenticationMiddleware for healthcheck endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Implement BasicAuthenticationMiddleware with bcrypt password hashing • Create middleware to protect healthcheck endpoint using basic auth • Add unit tests for BasicAuthenticationMiddleware with correct and incorrect credentials --- src/autoscaler/helpers/basic_auth.go | 95 +++++++++++++++++++++++ src/autoscaler/helpers/basic_auth_test.go | 82 +++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 src/autoscaler/helpers/basic_auth.go create mode 100644 src/autoscaler/helpers/basic_auth_test.go diff --git a/src/autoscaler/helpers/basic_auth.go b/src/autoscaler/helpers/basic_auth.go new file mode 100644 index 0000000000..18fe3f77af --- /dev/null +++ b/src/autoscaler/helpers/basic_auth.go @@ -0,0 +1,95 @@ +package helpers + +import ( + "net/http" + + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" + "code.cloudfoundry.org/lager/v3" + "golang.org/x/crypto/bcrypt" +) + +type BasicAuthenticationMiddleware struct { + usernameHash []byte + passwordHash []byte + logger lager.Logger +} + +// middleware basic authentication middleware functionality for healthcheck +func (bam *BasicAuthenticationMiddleware) BasicAuthenticationMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + username, password, authOK := r.BasicAuth() + + bam.logger.Info("basic-authentication-middleware", lager.Data{"usernameHash": bam.usernameHash, "passwordHash": bam.passwordHash}) + if bam.usernameHash == nil && bam.passwordHash == nil { + next.ServeHTTP(w, r) + return + } + + if !authOK || bcrypt.CompareHashAndPassword(bam.usernameHash, []byte(username)) != nil || bcrypt.CompareHashAndPassword(bam.passwordHash, []byte(password)) != nil { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + next.ServeHTTP(w, r) + }) +} + +func CreateBasicAuthMiddleware(logger lager.Logger, ba models.BasicAuth) (*BasicAuthenticationMiddleware, error) { + var basicAuthentication *BasicAuthenticationMiddleware + usernameHash, username, passwordHash, password := ba.UsernameHash, ba.Username, ba.PasswordHash, ba.Password + + usernameHashByte, err := getUserHashBytes(logger, usernameHash, username) + if err != nil { + return nil, err + } + + passwordHashByte, err := getPasswordHashBytes(logger, passwordHash, password) + if err != nil { + return nil, err + } + + basicAuthentication = &BasicAuthenticationMiddleware{ + usernameHash: usernameHashByte, + passwordHash: passwordHashByte, + logger: logger, + } + return basicAuthentication, nil +} + +func getUserHashBytes(logger lager.Logger, usernameHash string, username string) ([]byte, error) { + var usernameHashByte []byte + var err error + if usernameHash == "" { + if len(username) > 72 { + logger.Error("warning-configured-username-too-long-using-only-first-72-characters", bcrypt.ErrPasswordTooLong, lager.Data{"username-length": len(username)}) + username = username[:72] + } + // when username and password are set for health check + usernameHashByte, err = bcrypt.GenerateFromPassword([]byte(username), bcrypt.MinCost) // use MinCost as the config already provided it as cleartext + if err != nil { + logger.Error("failed-new-server-username", err) + return nil, err + } + } else { + usernameHashByte = []byte(usernameHash) + } + return usernameHashByte, err +} + +func getPasswordHashBytes(logger lager.Logger, passwordHash string, password string) ([]byte, error) { + var passwordHashByte []byte + var err error + if passwordHash == "" { + if len(password) > 72 { + logger.Error("warning-configured-password-too-long-using-only-first-72-characters", bcrypt.ErrPasswordTooLong, lager.Data{"password-length": len(password)}) + password = password[:72] + } + passwordHashByte, err = bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost) // use MinCost as the config already provided it as cleartext + if err != nil { + logger.Error("failed-new-server-password", err) + return nil, err + } + } else { + passwordHashByte = []byte(passwordHash) + } + return passwordHashByte, nil +} diff --git a/src/autoscaler/helpers/basic_auth_test.go b/src/autoscaler/helpers/basic_auth_test.go new file mode 100644 index 0000000000..00c57f31f7 --- /dev/null +++ b/src/autoscaler/helpers/basic_auth_test.go @@ -0,0 +1,82 @@ +package helpers_test + +import ( + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" + "code.cloudfoundry.org/lager/v3" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "net/http" + "net/http/httptest" +) + +var handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +}) + +var _ = Describe("BasicAuthenticationMiddleware", func() { + var ( + server *httptest.Server + ba models.BasicAuth + resp *http.Response + username string + password string + logger lager.Logger + ) + + BeforeEach(func() { + logger = lager.NewLogger("helper-test") + }) + + AfterEach(func() { + server.Close() + }) + + JustBeforeEach(func() { + bam, err := helpers.CreateBasicAuthMiddleware(logger, ba) + Expect(err).NotTo(HaveOccurred()) + + server = httptest.NewServer(bam.BasicAuthenticationMiddleware(handler)) + + req, err := http.NewRequest("GET", server.URL+"/some-protected-endpoint", nil) + req.SetBasicAuth(username, password) + Expect(err).NotTo(HaveOccurred()) + + resp, err = http.DefaultClient.Do(req) + Expect(err).NotTo(HaveOccurred()) + + defer resp.Body.Close() + }) + + When("basic auth is enabled", func() { + BeforeEach(func() { + ba = models.BasicAuth{ + Username: "username", + Password: "password", + } + }) + + When("credentials are correct", func() { + BeforeEach(func() { + username = ba.Username + password = ba.Password + }) + + It("should return 200", func() { + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + }) + }) + + When("credentials are incorrect", func() { + BeforeEach(func() { + username = "wrong-username" + password = "wrong-password" + }) + + It("should return 401", func() { + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + }) + }) + }) +}) From 204b2dcaa447e5c6bdda78d420074f3b802cf6e8 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:42:11 +0200 Subject: [PATCH 11/39] Refactor scaling history API and update Makefile for new paths - Move scaling history OpenAPI generation from helpers to api and scalingengine directories - Add new internal-scaling-history-api.openapi.yaml for Scaling History API - Update Makefile to handle OpenAPI client and server generation in new locations - Add NewScalingEngineClient function in testhelpers --- .gitignore | 3 +- api/internal-scaling-history-api.openapi.yaml | 213 ++++++++++++++++++ src/autoscaler/Makefile | 22 +- src/autoscaler/testhelpers/clients.go | 4 + 4 files changed, 233 insertions(+), 9 deletions(-) create mode 100644 api/internal-scaling-history-api.openapi.yaml diff --git a/.gitignore b/.gitignore index 43035b09c2..b44c20df32 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,8 @@ src/acceptance/assets/app/go_app/internal/openapi-specs.bundled src/acceptance/assets/app/go_app/internal/applicationmetric/oas*gen.go src/acceptance/assets/app/go_app/internal/custommetrics/oas*gen.go src/acceptance/assets/app/go_app/internal/policy/oas*gen.go -src/autoscaler/helpers/apis/scalinghistory/oas*gen.go +src/autoscaler/api/apis/scalinghistory/oas*gen.go +src/autoscaler/scalingengine/apis/scalinghistory/oas*gen.go # Autogenerated files go.work.sum diff --git a/api/internal-scaling-history-api.openapi.yaml b/api/internal-scaling-history-api.openapi.yaml new file mode 100644 index 0000000000..ee6614de43 --- /dev/null +++ b/api/internal-scaling-history-api.openapi.yaml @@ -0,0 +1,213 @@ +openapi: 3.0.0 +info: + title: Scaling History API + description: List scaling history of an application + version: 1.0.0 + license: + name: "Apache License Version 2.0" + # identifier: "Apache-2.0" # Requires at least OpenAPI 3.1.0 + url: "http://www.apache.org/licenses/LICENSE-2.0.html" +tags: +- name: Scaling History API V1 + description: List the scaling history of an Application +paths: + /v1/apps/{guid}/scaling_histories: + parameters: + - name: guid + in: path + required: true + description: | + The GUID identifying the application for which the scaling history is fetched. + + It can be found in the `application_id` property of the JSON object stored in the + `VCAP_APPLICATION` environment variable. + schema: + $ref: "./shared_definitions.yaml#/schemas/GUID" + - name: start-time + in: query + description: | + The start time in the number of nanoseconds elapsed since January 1, 1970 UTC. + schema: + type: integer + default: 0 + example: start-time=1494989539138350432 + - name: end-time + in: query + description: | + The end time in the number of nanoseconds elapsed since January 1, 1970 UTC. + schema: + type: integer + default: -1 + example: end-time=1494989549117047288 + - name: order-direction + in: query + description: | + The sorting order. The scaling history will be order by timestamp ascending or descending. + schema: + type: string + enum: ["asc", "desc"] + default: desc + example: order-direction=desc + - name: order + in: query + description: | + Deprecated: Use order-direction instead. + schema: + type: string + enum: ["asc", "desc"] + deprecated: true + example: order=desc + - name: page + in: query + description: The page number to query + schema: + type: integer + minimum: 1 + default: 1 + example: page=1 + - name: results-per-page + in: query + description: Number of entries shown per page. + schema: + type: integer + minimum: 0 + default: 50 + example: results-per-page=10 + get: + summary: Retrieves the scaling history of an application. + description: | + Use to retrieve scaling history for an app. + tags: + - Scaling History API V1 + responses: + "200": + description: "OK" + content: + application/json: + schema: + $ref: "#/components/schemas/History" + default: + $ref: "./shared_definitions.yaml#/responses/Error" + security: [] + x-codegen-request-body-name: body +components: + schemas: + History: + description: Object containing scaling history. + type: object + properties: + total_results: + type: integer + format: int64 + description: Number of history entries found for the given query. + example: 2 + total_pages: + type: integer + format: int64 + description: Number of Pages from the query + example: 1 + page: + type: integer + format: int64 + description: Number of the current page. + example: 1 + prev_url: + type: string + format: uri + next_url: + type: string + format: uri + resources: + type: array + items: + $ref: '#/components/schemas/HistoryEntry' + HistoryEntry: + description: "Properties common for each entry in the scaling history." + type: object + oneOf: + - $ref: "#/components/schemas/HistoryErrorEntry" + - $ref: "#/components/schemas/HistoryIgnoreEntry" + - $ref: "#/components/schemas/HistorySuccessEntry" +# Unfortunately, we cannot use a discriminator here, as the property MUST be a string, see also https://github.com/OAI/OpenAPI-Specification/issues/2731 +# discriminator: +# propertyName: status +# mapping: +# 0: "#/components/schemas/HistorySuccessEntry" +# 1: "#/components/schemas/HistoryErrorEntry" +# 2: "#/components/schemas/HistoryIgnoreEntry" + properties: + status: + type: integer + format: int64 + enum: [0, 1, 2] + description: | + Following stati are possible: + + 0: The scaling was done successfully. + + 1: The scaling failed explicitly. + + 2: The scaling was ignored. + This field is as well a selector of which of the other ones are used and which not. + example: 0 + app_id: + $ref: "./shared_definitions.yaml#/schemas/GUID" + timestamp: + type: integer + description: | + The scaling time in the number of nanoseconds elapsed since January 1, 1970 UTC. + example: 1494989539138350432 + scaling_type: + type: integer + format: int64 + enum: [0, 1] + description: | + There are two different scaling types: + + 0: This represents `ScalingTypeDynamic`. The scaling has been done due to a dynamic + scaling rule, reacting on metrics provided by the app. + + 1: This represents `ScalingTypeSchedule`. The scaling has been done due to a + scheduled period changing the default instance limits. + example: 0 + old_instances: + type: integer + format: int64 + minimum: -1 + description: The number of instances before the scaling. -1 means that the value is not applicable. + example: 1 + new_instances: + type: integer + format: int64 + minimum: -1 + description: The number of instances after the scaling. -1 means that the value is not applicable. + example: 2 + reason: + type: string + description: Textual information about what triggered the scaling event. + example: -1 instance(s) because cpu < 20% for 60 seconds + message: + type: string + description: Textual information about the scaling event. + example: app + HistoryErrorEntry: + description: Description of a failed scaling even in history. + type: object + properties: + error: + type: string + description: | + In case the scaling failed, the reason is provided in this field. + example: failed to compute new app instances + HistoryIgnoreEntry: + description: Description of an ignored scaling event in history. + type: object + properties: + ignore_reason: + type: string + description: | + In case the scaling was ignored, the reason is provided in this field. + example: app in cooldown period + HistorySuccessEntry: + description: Description of a successful scaling event event in history. + type: object + properties: {} # No extra fields needed in this variant. + securitySchemes: + basicAuth: + type: http + scheme: basic diff --git a/src/autoscaler/Makefile b/src/autoscaler/Makefile index f5dc764c0a..2a138f6d35 100644 --- a/src/autoscaler/Makefile +++ b/src/autoscaler/Makefile @@ -27,19 +27,24 @@ GINKGO_VERSION = v$(shell cat ../../.tool-versions | grep ginkgo | cut --delimi # ogen generated OpenAPI clients and servers -openapi-generated-clients-and-servers-dir := ./helpers/apis/scalinghistory +openapi-generated-clients-and-servers-api-dir := ./api/apis/scalinghistory +openapi-generated-clients-and-servers-scalingengine-dir := ./scalingengine/apis/scalinghistory + openapi-spec-path := ../../api openapi-specs-list = $(wildcard ${openapi-spec-path}/*.yaml) -openapi-generated-clients-and-servers-files = $(wildcard ${openapi-generated-clients-and-servers-dir}/*.go) +openapi-generated-clients-and-servers-api-files = $(wildcard ${openapi-generated-clients-and-servers-api-dir}/*.go) +openapi-generated-clients-and-servers-scalingengine-files = $(wildcard ${openapi-generated-clients-and-servers-scalingengine-dir}/*.go) + .PHONY: generate-openapi-generated-clients-and-servers -generate-openapi-generated-clients-and-servers: ${openapi-generated-clients-and-servers-dir} ${openapi-generated-clients-and-servers-files} -${openapi-generated-clients-and-servers-dir} ${openapi-generated-clients-and-servers-files} &: $(wildcard ./helpers/apis/generate.go) ${openapi-specs-list} ./go.mod ./go.sum +generate-openapi-generated-clients-and-servers: ${openapi-generated-clients-and-servers-api-dir} ${openapi-generated-clients-and-servers-api-files} ${openapi-generated-clients-and-servers-scalingengine-dir} ${openapi-generated-clients-and-servers-scalingengine-files} +${openapi-generated-clients-and-servers-api-dir} ${openapi-generated-clients-and-servers-api-files} ${openapi-generated-clients-and-servers-scalingengine-dir} ${openapi-generated-clients-and-servers-scalingengine-files} &: $(wildcard ./scalingengine/apis/generate.go) $(wildcard ./api/apis/generate.go) ${openapi-specs-list} ./go.mod ./go.sum @echo "# Generating OpenAPI clients and servers" - # $(wildcard ./helpers/apis/generate.go) causes the target to always being executed, no matter if file exists or not. + # $(wildcard ./api/apis/generate.go) causes the target to always being executed, no matter if file exists or not. # so let's don't fail if file can't be found, e.g. the eventgenerator bosh package does not contain it. - go generate ./helpers/apis/generate.go || true + go generate ./api/apis/generate.go || true + go generate ./scalingengine/apis/generate.go || true # The presence of the subsequent directory indicates whether the fakes still need to be generated # or not. @@ -91,7 +96,7 @@ build-cf-%: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $*/$* $*/cmd/$*/main.go # CGO_ENABLED := 1 is required to enforce dynamic linking which is a requirement of dynatrace. -build-%: ${openapi-generated-clients-and-servers-dir} ${openapi-generated-clients-and-servers-files} +build-%: ${openapi-generated-clients-and-servers-api-dir} ${openapi-generated-clients-and-servers-api-files} ${openapi-generated-clients-and-servers-scalingengine-dir} ${openapi-generated-clients-and-servers-scalingengine-files} @echo "# building $*" @CGO_ENABLED=1 go build $(BUILDTAGS) $(BUILDFLAGS) -o build/$* $*/cmd/$*/main.go @@ -145,4 +150,5 @@ clean: @rm --force --recursive 'build' @rm --force --recursive 'fakes' @rm --force --recursive 'vendor' - @rm --force --recursive "${openapi-generated-clients-and-servers-dir}" + @rm --force --recursive "${openapi-generated-clients-and-servers-api-dir}" + @rm --force --recursive "${openapi-generated-clients-and-servers-scalingengine-dir}" diff --git a/src/autoscaler/testhelpers/clients.go b/src/autoscaler/testhelpers/clients.go index eecf376d1b..0c7c338e6e 100644 --- a/src/autoscaler/testhelpers/clients.go +++ b/src/autoscaler/testhelpers/clients.go @@ -31,6 +31,10 @@ func NewSchedulerClient() *http.Client { return CreateClientFor("scheduler") } +func NewScalingEngineClient() *http.Client { + return CreateClientFor("scalingengine") +} + func CreateClientFor(name string) *http.Client { certFolder := TestCertFolder() return CreateClient(filepath.Join(certFolder, name+".crt"), From 31ddfbb73fe107d6cc28d04fb7ece2416d7709a3 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Wed, 7 Aug 2024 14:42:56 +0200 Subject: [PATCH 12/39] WIP - xfcc middleware for publicapi --- .../helpers/auth/auth_suite_test.go | 13 ++ src/autoscaler/helpers/auth/xfcc_auth.go | 118 ++++++++++++++ src/autoscaler/helpers/auth/xfcc_auth_test.go | 153 ++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 src/autoscaler/helpers/auth/auth_suite_test.go create mode 100644 src/autoscaler/helpers/auth/xfcc_auth.go create mode 100644 src/autoscaler/helpers/auth/xfcc_auth_test.go diff --git a/src/autoscaler/helpers/auth/auth_suite_test.go b/src/autoscaler/helpers/auth/auth_suite_test.go new file mode 100644 index 0000000000..cc266fc4ed --- /dev/null +++ b/src/autoscaler/helpers/auth/auth_suite_test.go @@ -0,0 +1,13 @@ +package auth_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestAuth(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Auth Suite") +} diff --git a/src/autoscaler/helpers/auth/xfcc_auth.go b/src/autoscaler/helpers/auth/xfcc_auth.go new file mode 100644 index 0000000000..2728cb7b5d --- /dev/null +++ b/src/autoscaler/helpers/auth/xfcc_auth.go @@ -0,0 +1,118 @@ +package auth + +import ( + "crypto/x509" + "encoding/base64" + "errors" + "fmt" + "net/http" + "regexp" + "strings" + + "code.cloudfoundry.org/lager/v3" +) + +var ErrorWrongSpace = errors.New("space guid is wrong") +var ErrorWrongOrg = errors.New("org guid is wrong") +var ErrXFCCHeaderNotFound = errors.New("xfcc header not found") + +type XFCCAuthMiddleware struct { + logger lager.Logger + spaceGuid string + orgGuid string +} + +func (m *XFCCAuthMiddleware) checkAuth(r *http.Request) error { + xfccHeader := r.Header.Get("X-Forwarded-Client-Cert") + if xfccHeader == "" { + return ErrXFCCHeaderNotFound + } + + data, err := base64.StdEncoding.DecodeString(removeQuotes(xfccHeader)) + if err != nil { + return fmt.Errorf("base64 parsing failed: %w", err) + } + + cert, err := x509.ParseCertificate(data) + if err != nil { + return fmt.Errorf("failed to parse certificate: %w", err) + } + + if getSpaceGuid(cert) != m.spaceGuid { + return ErrorWrongSpace + } + + if getOrgGuid(cert) != m.orgGuid { + return ErrorWrongOrg + } + + return nil + +} + +func (m *XFCCAuthMiddleware) XFCCAuthenticationMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + err := m.checkAuth(r) + + if err != nil { + m.logger.Error("xfcc-auth-error", err) + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + + next.ServeHTTP(w, r) + }) +} + +func NewXfccAuthMiddleware(logger lager.Logger, orgGuid, spaceGuid string) *XFCCAuthMiddleware { + return &XFCCAuthMiddleware{ + logger: logger, + orgGuid: orgGuid, + spaceGuid: spaceGuid, + } +} + +func getSpaceGuid(cert *x509.Certificate) string { + var certSpaceGuid string + for _, ou := range cert.Subject.OrganizationalUnit { + + if strings.Contains(ou, "space:") { + kv := mapFrom(ou) + certSpaceGuid = kv["space"] + break + } + } + return certSpaceGuid +} + +func mapFrom(input string) map[string]string { + result := make(map[string]string) + + r := regexp.MustCompile(`(\w+):(\w+-\w+)`) + matches := r.FindAllStringSubmatch(input, -1) + + for _, match := range matches { + result[match[1]] = match[2] + } + return result +} + +func getOrgGuid(cert *x509.Certificate) string { + var certOrgGuid string + for _, ou := range cert.Subject.OrganizationalUnit { + // capture from string k:v with regex + if strings.Contains(ou, "org:") { + kv := mapFrom(ou) + certOrgGuid = kv["org"] + break + } + } + return certOrgGuid +} + +func removeQuotes(xfccHeader string) string { + if xfccHeader[0] == '"' { + xfccHeader = xfccHeader[1 : len(xfccHeader)-1] + } + return xfccHeader +} diff --git a/src/autoscaler/helpers/auth/xfcc_auth_test.go b/src/autoscaler/helpers/auth/xfcc_auth_test.go new file mode 100644 index 0000000000..6a5a54ac87 --- /dev/null +++ b/src/autoscaler/helpers/auth/xfcc_auth_test.go @@ -0,0 +1,153 @@ +package auth_test + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/pem" + "fmt" + "math/big" + "net/http" + "net/http/httptest" + + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers/auth" + + "code.cloudfoundry.org/lager/v3/lagertest" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" +) + +var handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +}) + +var _ = Describe("XfccAuthMiddleware", func() { + var ( + server *httptest.Server + resp *http.Response + + buffer *gbytes.Buffer + + err error + xfccClientCert []byte + + orgGuid string + spaceGuid string + ) + + AfterEach(func() { + server.Close() + }) + + JustBeforeEach(func() { + logger := lagertest.NewTestLogger("xfcc-auth-test") + buffer = logger.Buffer() + xm := auth.NewXfccAuthMiddleware(logger, orgGuid, spaceGuid) + + server = httptest.NewServer(xm.XFCCAuthenticationMiddleware(handler)) + + req, err := http.NewRequest("GET", server.URL+"/some-protected-endpoint", nil) + + if len(xfccClientCert) > 0 { + block, _ := pem.Decode(xfccClientCert) + Expect(err).NotTo(HaveOccurred()) + Expect(block).ShouldNot(BeNil()) + + req.Header.Add("X-Forwarded-Client-Cert", base64.StdEncoding.EncodeToString(block.Bytes)) + } + Expect(err).NotTo(HaveOccurred()) + + resp, err = http.DefaultClient.Do(req) + Expect(err).NotTo(HaveOccurred()) + }) + + BeforeEach(func() { + orgGuid = "org-guid" + spaceGuid = "space-guid" + }) + + When("xfcc header is not set", func() { + BeforeEach(func() { + xfccClientCert = []byte{} + }) + + It("should return 401", func() { + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + Eventually(buffer).Should(gbytes.Say(auth.ErrXFCCHeaderNotFound.Error())) + }) + }) + + When("xfcc cert matches org and space guids", func() { + BeforeEach(func() { + xfccClientCert, err = generateClientCert(orgGuid, spaceGuid) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should return 200", func() { + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + }) + }) + + When("xfcc cert does not match org guid", func() { + BeforeEach(func() { + xfccClientCert, err = generateClientCert("wrong-org-guid", spaceGuid) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should return 401", func() { + Eventually(buffer).Should(gbytes.Say(auth.ErrorWrongOrg.Error())) + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + }) + + }) + + When("xfcc cert does not match space guid", func() { + BeforeEach(func() { + xfccClientCert, err = generateClientCert(orgGuid, "wrong-space-guid") + Expect(err).NotTo(HaveOccurred()) + }) + + It("should return 401", func() { + Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + Eventually(buffer).Should(gbytes.Say(auth.ErrorWrongSpace.Error())) + }) + }) +}) + +// generateClientCert generates a client certificate with the specified spaceGUID and orgGUID +// included in the organizational unit string. +func generateClientCert(orgGUID, spaceGUID string) ([]byte, error) { + // Generate a random serial number for the certificate + // + serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + if err != nil { + return nil, err + } + + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + + // Create a new X.509 certificate template + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"My Organization"}, + OrganizationalUnit: []string{fmt.Sprintf("space:%s org:%s", spaceGUID, orgGUID)}, + }, + } + // Generate the certificate + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) + if err != nil { + return nil, err + } + + // Encode the certificate to PEM format + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + + return certPEM, nil +} From 25b355a5beeb011260b9cd09e7ddbdc5d11450ae Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 10 Oct 2024 11:44:55 +0200 Subject: [PATCH 13/39] Merge debox --- devbox.lock | 752 ++++++++++++++++++++++++++-------------------------- 1 file changed, 376 insertions(+), 376 deletions(-) diff --git a/devbox.lock b/devbox.lock index a0d42be71f..b807ee6e35 100644 --- a/devbox.lock +++ b/devbox.lock @@ -50,50 +50,50 @@ } }, "actionlint@latest": { - "last_modified": "2024-09-24T10:20:15Z", - "resolved": "github:NixOS/nixpkgs/965289e5e07243f1cde3212d8bcaf726d36c5c46#actionlint", + "last_modified": "2024-10-06T12:21:13Z", + "resolved": "github:NixOS/nixpkgs/50b3bd3fed0442bcbf7f58355e990da84af1749d#actionlint", "source": "devbox-search", - "version": "1.7.2", + "version": "1.7.3", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/qv1fnp2ifk37jlr1lbzf5r63vr3q6k41-actionlint-1.7.2", + "path": "/nix/store/33rbqx21gcp3imbrr2i0ykhz7x9ixsj5-actionlint-1.7.3", "default": true } ], - "store_path": "/nix/store/qv1fnp2ifk37jlr1lbzf5r63vr3q6k41-actionlint-1.7.2" + "store_path": "/nix/store/33rbqx21gcp3imbrr2i0ykhz7x9ixsj5-actionlint-1.7.3" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/nw0xpacz21b9rszn4k6ay452pf8amf1p-actionlint-1.7.2", + "path": "/nix/store/xa1zzhm3szslprnknblyhvp4lbagk6nx-actionlint-1.7.3", "default": true } ], - "store_path": "/nix/store/nw0xpacz21b9rszn4k6ay452pf8amf1p-actionlint-1.7.2" + "store_path": "/nix/store/xa1zzhm3szslprnknblyhvp4lbagk6nx-actionlint-1.7.3" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/rh6f40c82jp5z3irxsq6lfapg6ckc9br-actionlint-1.7.2", + "path": "/nix/store/z37rjxbvy5z9pk0vbimw2gw2rqdxjjc3-actionlint-1.7.3", "default": true } ], - "store_path": "/nix/store/rh6f40c82jp5z3irxsq6lfapg6ckc9br-actionlint-1.7.2" + "store_path": "/nix/store/z37rjxbvy5z9pk0vbimw2gw2rqdxjjc3-actionlint-1.7.3" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ggpghzgdd1cv04hh93cmqjb7mm3zqc7m-actionlint-1.7.2", + "path": "/nix/store/48asng2d6lcma3rblhq56zwzbp7c7hbf-actionlint-1.7.3", "default": true } ], - "store_path": "/nix/store/ggpghzgdd1cv04hh93cmqjb7mm3zqc7m-actionlint-1.7.2" + "store_path": "/nix/store/48asng2d6lcma3rblhq56zwzbp7c7hbf-actionlint-1.7.3" } } }, @@ -146,8 +146,8 @@ } }, "bundix@latest": { - "last_modified": "2024-09-01T12:44:31Z", - "resolved": "github:NixOS/nixpkgs/b833ff01a0d694b910daca6e2ff4a3f26dee478c#bundix", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#bundix", "source": "devbox-search", "version": "2.5.2", "systems": { @@ -155,47 +155,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/46yrpr6iqfzkas8b7mixjpi2qsdb4rss-bundix-2.5.2", + "path": "/nix/store/pbrz49zxdd92h389cfyk5lnv98w0gc3b-bundix-2.5.2", "default": true } ], - "store_path": "/nix/store/46yrpr6iqfzkas8b7mixjpi2qsdb4rss-bundix-2.5.2" + "store_path": "/nix/store/pbrz49zxdd92h389cfyk5lnv98w0gc3b-bundix-2.5.2" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/z2n8rwcis8dc45ids4apdaabj5f9z7qi-bundix-2.5.2", + "path": "/nix/store/scx6rk9b6h2nz8bnrdy47nkg7bskwsx4-bundix-2.5.2", "default": true } ], - "store_path": "/nix/store/z2n8rwcis8dc45ids4apdaabj5f9z7qi-bundix-2.5.2" + "store_path": "/nix/store/scx6rk9b6h2nz8bnrdy47nkg7bskwsx4-bundix-2.5.2" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/zykb79mvak4ip2r8x3dfmz9y5ic0x1zd-bundix-2.5.2", + "path": "/nix/store/b1kj0931c009gnjb2dxakndx4nhilw5p-bundix-2.5.2", "default": true } ], - "store_path": "/nix/store/zykb79mvak4ip2r8x3dfmz9y5ic0x1zd-bundix-2.5.2" + "store_path": "/nix/store/b1kj0931c009gnjb2dxakndx4nhilw5p-bundix-2.5.2" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/vyn43k2w72b02016v1dwaf396w8f1vmf-bundix-2.5.2", + "path": "/nix/store/r2ycwi3f81sagkjn8ask8d3kqqb9f6h1-bundix-2.5.2", "default": true } ], - "store_path": "/nix/store/vyn43k2w72b02016v1dwaf396w8f1vmf-bundix-2.5.2" + "store_path": "/nix/store/r2ycwi3f81sagkjn8ask8d3kqqb9f6h1-bundix-2.5.2" } } }, "bundler@latest": { - "last_modified": "2024-09-12T11:58:09Z", - "resolved": "github:NixOS/nixpkgs/280db3decab4cbeb22a4599bd472229ab74d25e1#bundler", + "last_modified": "2024-09-10T15:01:03Z", + "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#bundler", "source": "devbox-search", "version": "2.5.16", "systems": { @@ -290,8 +290,8 @@ } }, "coreutils@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#coreutils", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#coreutils", "source": "devbox-search", "version": "9.5", "systems": { @@ -299,65 +299,65 @@ "outputs": [ { "name": "out", - "path": "/nix/store/21myf7jnm3j5myc9yp8nlhgp427si0cw-coreutils-9.5", + "path": "/nix/store/7k0qi2r54imwjfs2bklg7fv0mn5jglil-coreutils-9.5", "default": true }, { "name": "info", - "path": "/nix/store/gngnz090cwq0iv6j8gf90i8ir23qlh8w-coreutils-9.5-info" + "path": "/nix/store/x15d6rpi8icjcq7arr51ci0z2q7pyn2y-coreutils-9.5-info" } ], - "store_path": "/nix/store/21myf7jnm3j5myc9yp8nlhgp427si0cw-coreutils-9.5" + "store_path": "/nix/store/7k0qi2r54imwjfs2bklg7fv0mn5jglil-coreutils-9.5" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/4wha9m6mh2k5a14srl4nmzpi9f7isp56-coreutils-9.5", + "path": "/nix/store/ykgg9l0s62brx3xmnhdyjms94ylgas1i-coreutils-9.5", "default": true }, { "name": "debug", - "path": "/nix/store/6hkfnznanfflydnwwlik54aizjqhqra9-coreutils-9.5-debug" + "path": "/nix/store/ml3apw95278grz137ssqkqh06k9kdkw5-coreutils-9.5-debug" }, { "name": "info", - "path": "/nix/store/lpcs05q46id2l5qzf5pmfjqqj0j5q4sw-coreutils-9.5-info" + "path": "/nix/store/3ncshm0nv5xwlv9j294577sy63xikfpp-coreutils-9.5-info" } ], - "store_path": "/nix/store/4wha9m6mh2k5a14srl4nmzpi9f7isp56-coreutils-9.5" + "store_path": "/nix/store/ykgg9l0s62brx3xmnhdyjms94ylgas1i-coreutils-9.5" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/gsk79pib6zrfm486m1g10m3z8z1lapyb-coreutils-9.5", + "path": "/nix/store/n7gmn34vlgrirqhxr6rg9yy6lg8pm8id-coreutils-9.5", "default": true }, { "name": "info", - "path": "/nix/store/91d0z5pj8740gblgj0ycksf234fhyq4w-coreutils-9.5-info" + "path": "/nix/store/yzhjqzsyjyka58dj33bfvxal4walzahs-coreutils-9.5-info" } ], - "store_path": "/nix/store/gsk79pib6zrfm486m1g10m3z8z1lapyb-coreutils-9.5" + "store_path": "/nix/store/n7gmn34vlgrirqhxr6rg9yy6lg8pm8id-coreutils-9.5" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/vb8mdklw65p9wikp97ybmnyay0xzipx3-coreutils-9.5", + "path": "/nix/store/cnknp3yxfibxjhila0sjd1v3yglqssng-coreutils-9.5", "default": true }, { "name": "debug", - "path": "/nix/store/9818diawz01qd1kjgfq071zyglsw35pg-coreutils-9.5-debug" + "path": "/nix/store/4yz2r9nncispdfdsrrm3w881482i05ca-coreutils-9.5-debug" }, { "name": "info", - "path": "/nix/store/x2rwb8yxlpl4mh6qn85kfsr63v44zxnl-coreutils-9.5-info" + "path": "/nix/store/bfxj13l4y87bd14z9wmjd20lgzk0rjwy-coreutils-9.5-info" } ], - "store_path": "/nix/store/vb8mdklw65p9wikp97ybmnyay0xzipx3-coreutils-9.5" + "store_path": "/nix/store/cnknp3yxfibxjhila0sjd1v3yglqssng-coreutils-9.5" } } }, @@ -410,8 +410,8 @@ } }, "delve@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#delve", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#delve", "source": "devbox-search", "version": "1.23.0", "systems": { @@ -419,47 +419,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/hyk0a1p5bmxsgvyvmy4b5w7dyjp8wm7a-delve-1.23.0", + "path": "/nix/store/rx8vywp2nl9qvd5d2jlqnpjhjm83qmnj-delve-1.23.0", "default": true } ], - "store_path": "/nix/store/hyk0a1p5bmxsgvyvmy4b5w7dyjp8wm7a-delve-1.23.0" + "store_path": "/nix/store/rx8vywp2nl9qvd5d2jlqnpjhjm83qmnj-delve-1.23.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/vkq8h2y846qap2laz1gx63f682f6yq6l-delve-1.23.0", + "path": "/nix/store/39djxclvi16c42xsjrg107g4xr01mndm-delve-1.23.0", "default": true } ], - "store_path": "/nix/store/vkq8h2y846qap2laz1gx63f682f6yq6l-delve-1.23.0" + "store_path": "/nix/store/39djxclvi16c42xsjrg107g4xr01mndm-delve-1.23.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/p9sscbm746fqghl065zc6k4z8w36s28j-delve-1.23.0", + "path": "/nix/store/l1vkgm87ys2yc9hpgnk371hzs7kfn2xq-delve-1.23.0", "default": true } ], - "store_path": "/nix/store/p9sscbm746fqghl065zc6k4z8w36s28j-delve-1.23.0" + "store_path": "/nix/store/l1vkgm87ys2yc9hpgnk371hzs7kfn2xq-delve-1.23.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/2vpk03v85dpxjffvhh7akg3bnsqdciyq-delve-1.23.0", + "path": "/nix/store/hyyl3pzqc3l569dfhkn5wbny355akaqf-delve-1.23.0", "default": true } ], - "store_path": "/nix/store/2vpk03v85dpxjffvhh7akg3bnsqdciyq-delve-1.23.0" + "store_path": "/nix/store/hyyl3pzqc3l569dfhkn5wbny355akaqf-delve-1.23.0" } } }, "direnv@2.34.0": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#direnv", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#direnv", "source": "devbox-search", "version": "2.34.0", "systems": { @@ -467,41 +467,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/439f5yi8i1akxl2669f5mam6iacisycv-direnv-2.34.0", + "path": "/nix/store/83793g0la5p6s7s2wixqb5z0jcljp6nv-direnv-2.34.0", "default": true } ], - "store_path": "/nix/store/439f5yi8i1akxl2669f5mam6iacisycv-direnv-2.34.0" + "store_path": "/nix/store/83793g0la5p6s7s2wixqb5z0jcljp6nv-direnv-2.34.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/1fj0mbpggzc91ykg1n5q86gd5ih7azws-direnv-2.34.0", + "path": "/nix/store/ax24mlnxhsafdjw6vc5dfcm9jml2qzrm-direnv-2.34.0", "default": true } ], - "store_path": "/nix/store/1fj0mbpggzc91ykg1n5q86gd5ih7azws-direnv-2.34.0" + "store_path": "/nix/store/ax24mlnxhsafdjw6vc5dfcm9jml2qzrm-direnv-2.34.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/v05bzbn98fq9qg6fpr5znpkqxgyx0xx4-direnv-2.34.0", + "path": "/nix/store/jdc3yg0gfi9ixhs32lypcdl61f6v489l-direnv-2.34.0", "default": true } ], - "store_path": "/nix/store/v05bzbn98fq9qg6fpr5znpkqxgyx0xx4-direnv-2.34.0" + "store_path": "/nix/store/jdc3yg0gfi9ixhs32lypcdl61f6v489l-direnv-2.34.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ml0yghnf9h5rb4dmv1brkfi60ils8gz6-direnv-2.34.0", + "path": "/nix/store/iq73997k0rsvb1rgglx0s5z224qr86jk-direnv-2.34.0", "default": true } ], - "store_path": "/nix/store/ml0yghnf9h5rb4dmv1brkfi60ils8gz6-direnv-2.34.0" + "store_path": "/nix/store/iq73997k0rsvb1rgglx0s5z224qr86jk-direnv-2.34.0" } } }, @@ -554,56 +554,56 @@ } }, "gh@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#gh", + "last_modified": "2024-08-02T23:16:43Z", + "resolved": "github:NixOS/nixpkgs/81610abc161d4021b29199aa464d6a1a521e0cc9#gh", "source": "devbox-search", - "version": "2.55.0", + "version": "2.54.0", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/9p3lk5rxhvs69qdrh6cl870xmfnw7i9s-gh-2.55.0", + "path": "/nix/store/ffhz14j4v0wgr24li67n58l3r85khyv3-gh-2.54.0", "default": true } ], - "store_path": "/nix/store/9p3lk5rxhvs69qdrh6cl870xmfnw7i9s-gh-2.55.0" + "store_path": "/nix/store/ffhz14j4v0wgr24li67n58l3r85khyv3-gh-2.54.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/cpzrvzha3bhgipkfx0nzsg38aqr5p6c9-gh-2.55.0", + "path": "/nix/store/giqcnp1bkjmggzwylmzi221wwyzvwcj9-gh-2.54.0", "default": true } ], - "store_path": "/nix/store/cpzrvzha3bhgipkfx0nzsg38aqr5p6c9-gh-2.55.0" + "store_path": "/nix/store/giqcnp1bkjmggzwylmzi221wwyzvwcj9-gh-2.54.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/bfnfq466pbqxxfzdwclfkh4wlwpgxlaj-gh-2.55.0", + "path": "/nix/store/xl1kr0ah5vrh0mg2g21zlb6n73w8x4cj-gh-2.54.0", "default": true } ], - "store_path": "/nix/store/bfnfq466pbqxxfzdwclfkh4wlwpgxlaj-gh-2.55.0" + "store_path": "/nix/store/xl1kr0ah5vrh0mg2g21zlb6n73w8x4cj-gh-2.54.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/braa7pb3r5lhrv2cdgrrk5iazgy4x55c-gh-2.55.0", + "path": "/nix/store/yz19xhnk02byz0h0qpaz18nggin56zqw-gh-2.54.0", "default": true } ], - "store_path": "/nix/store/braa7pb3r5lhrv2cdgrrk5iazgy4x55c-gh-2.55.0" + "store_path": "/nix/store/yz19xhnk02byz0h0qpaz18nggin56zqw-gh-2.54.0" } } }, "ginkgo@2.20.2": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#ginkgo", + "last_modified": "2024-09-10T15:01:03Z", + "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#ginkgo", "source": "devbox-search", "version": "2.20.2", "systems": { @@ -611,47 +611,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/bmaxiy62cl8pfv421m31d8yskbj7wzlh-ginkgo-2.20.2", + "path": "/nix/store/nid0ha9rpkvbcp77q6vr5zy351n8yn9m-ginkgo-2.20.2", "default": true } ], - "store_path": "/nix/store/bmaxiy62cl8pfv421m31d8yskbj7wzlh-ginkgo-2.20.2" + "store_path": "/nix/store/nid0ha9rpkvbcp77q6vr5zy351n8yn9m-ginkgo-2.20.2" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/23ssidzz6ws4g5km5nlnlzpsk2hx0kxp-ginkgo-2.20.2", + "path": "/nix/store/ak1481f4za57iaxr4kljslwyxnskfc93-ginkgo-2.20.2", "default": true } ], - "store_path": "/nix/store/23ssidzz6ws4g5km5nlnlzpsk2hx0kxp-ginkgo-2.20.2" + "store_path": "/nix/store/ak1481f4za57iaxr4kljslwyxnskfc93-ginkgo-2.20.2" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/wgnx7y28bpss448qv8aqfryzpx0z9wjd-ginkgo-2.20.2", + "path": "/nix/store/1k9ykqp9a0q6jrbg4zm5jygasl5jcdcy-ginkgo-2.20.2", "default": true } ], - "store_path": "/nix/store/wgnx7y28bpss448qv8aqfryzpx0z9wjd-ginkgo-2.20.2" + "store_path": "/nix/store/1k9ykqp9a0q6jrbg4zm5jygasl5jcdcy-ginkgo-2.20.2" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/lmsqp94ly9vrw8snzpncwjsvjnlj15sm-ginkgo-2.20.2", + "path": "/nix/store/2waqj0vva63gjq24agj96qbf5gb3kicp-ginkgo-2.20.2", "default": true } ], - "store_path": "/nix/store/lmsqp94ly9vrw8snzpncwjsvjnlj15sm-ginkgo-2.20.2" + "store_path": "/nix/store/2waqj0vva63gjq24agj96qbf5gb3kicp-ginkgo-2.20.2" } } }, "gnumake@4.4": { - "last_modified": "2024-09-01T03:39:50Z", - "resolved": "github:NixOS/nixpkgs/4a9443e2a4e06cbaff89056b5cdf6777c1fe5755#gnumake", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#gnumake", "source": "devbox-search", "version": "4.4.1", "systems": { @@ -659,91 +659,91 @@ "outputs": [ { "name": "out", - "path": "/nix/store/92kvph7n3kva2snfj0hq3cl3cbmf8chc-gnumake-4.4.1", + "path": "/nix/store/6gylp4vygmsm12rafhzvklrfkbhwwq40-gnumake-4.4.1", "default": true }, { "name": "man", - "path": "/nix/store/8aa6zhw2lgxarahfgjlk3f7fg8x40k35-gnumake-4.4.1-man", + "path": "/nix/store/wb8icphy7jmgb6iiikn5djsk9rnlvm3d-gnumake-4.4.1-man", "default": true }, { "name": "info", - "path": "/nix/store/zn8336d8r2sknny33k9l1v9vs86cpa4k-gnumake-4.4.1-info" + "path": "/nix/store/537dz619lcxi30vr2nz7fdvkyykd4v18-gnumake-4.4.1-info" } ], - "store_path": "/nix/store/92kvph7n3kva2snfj0hq3cl3cbmf8chc-gnumake-4.4.1" + "store_path": "/nix/store/6gylp4vygmsm12rafhzvklrfkbhwwq40-gnumake-4.4.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/89ifqwbc4335dkfx1fmlhfgjr9dyq42a-gnumake-4.4.1", + "path": "/nix/store/zm3bz71h77594dnry2v5q6rd6wyb6j4y-gnumake-4.4.1", "default": true }, { "name": "man", - "path": "/nix/store/xlijm05z007kshc6qs18mknv6j8vwqq2-gnumake-4.4.1-man", + "path": "/nix/store/ww1zz6d9lbypwxfg1187f9l2qj16abp9-gnumake-4.4.1-man", "default": true }, { "name": "debug", - "path": "/nix/store/6s14q5nxy67mwa0gpjjzsil7v8d46gdm-gnumake-4.4.1-debug" + "path": "/nix/store/2jmxd53sms6h9z95i5rl54s6899pb2ix-gnumake-4.4.1-debug" }, { "name": "info", - "path": "/nix/store/dy4gszlwxsfa73pgqq5skkdw1ffb4n4j-gnumake-4.4.1-info" + "path": "/nix/store/5aq5k0fl2w43iwlfqn4jy3hpbxl7sh9y-gnumake-4.4.1-info" } ], - "store_path": "/nix/store/89ifqwbc4335dkfx1fmlhfgjr9dyq42a-gnumake-4.4.1" + "store_path": "/nix/store/zm3bz71h77594dnry2v5q6rd6wyb6j4y-gnumake-4.4.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/z75g6caslrjk4s8r797ivp8q5cx6f7bj-gnumake-4.4.1", + "path": "/nix/store/ni2pnvdf4hydh6i72l762fqr5rrqzfcy-gnumake-4.4.1", "default": true }, { "name": "man", - "path": "/nix/store/cgv4n8r330047gcz47ci52d5pw6k0qg8-gnumake-4.4.1-man", + "path": "/nix/store/wwl5zd4v5f0p2w97rz1b7whgy6xlqlf3-gnumake-4.4.1-man", "default": true }, { "name": "info", - "path": "/nix/store/v9pch95kwwi7j18wgiw3mn1av89dg230-gnumake-4.4.1-info" + "path": "/nix/store/29kbczjj9j0vv1kxfxxv08jk6im2axq2-gnumake-4.4.1-info" } ], - "store_path": "/nix/store/z75g6caslrjk4s8r797ivp8q5cx6f7bj-gnumake-4.4.1" + "store_path": "/nix/store/ni2pnvdf4hydh6i72l762fqr5rrqzfcy-gnumake-4.4.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/i1ys5lhw9g9wcf6jz44n1m0k1p83zcr9-gnumake-4.4.1", + "path": "/nix/store/3ssglpx5xilkrmkhyl4bg0501wshmsgv-gnumake-4.4.1", "default": true }, { "name": "man", - "path": "/nix/store/nhs9k7cw3kd4l4v0l22s1nwsxfydrc3m-gnumake-4.4.1-man", + "path": "/nix/store/kr3rxnzfskfzhwd10cx2wh0w84psn9hs-gnumake-4.4.1-man", "default": true }, { "name": "debug", - "path": "/nix/store/pi5ls9kbadmxzj2r3xx8c5h65g5iddha-gnumake-4.4.1-debug" + "path": "/nix/store/25qnnb3mh78d5vz0bgj2ax8yv09g90jr-gnumake-4.4.1-debug" }, { "name": "info", - "path": "/nix/store/9qlbz0xav5rns8bmdn2gzajvbl4qw0kz-gnumake-4.4.1-info" + "path": "/nix/store/xmb5hll19li4k59mrrrh54zx7md6brsn-gnumake-4.4.1-info" } ], - "store_path": "/nix/store/i1ys5lhw9g9wcf6jz44n1m0k1p83zcr9-gnumake-4.4.1" + "store_path": "/nix/store/3ssglpx5xilkrmkhyl4bg0501wshmsgv-gnumake-4.4.1" } } }, "gnutar@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#gnutar", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#gnutar", "source": "devbox-search", "version": "1.35", "systems": { @@ -751,105 +751,105 @@ "outputs": [ { "name": "out", - "path": "/nix/store/01091ljf4l5s1gdmqpmvdvxp38wp8zc6-gnutar-1.35", + "path": "/nix/store/lzndf97akrpmd3vjdq06xzwk8fnjc4j7-gnutar-1.35", "default": true }, { "name": "info", - "path": "/nix/store/z2bl9ircb1nsq8a22l88lajm07hgbxfz-gnutar-1.35-info" + "path": "/nix/store/93ybjs28rh93c3gjxg17y5hyqzvj3l24-gnutar-1.35-info" } ], - "store_path": "/nix/store/01091ljf4l5s1gdmqpmvdvxp38wp8zc6-gnutar-1.35" + "store_path": "/nix/store/lzndf97akrpmd3vjdq06xzwk8fnjc4j7-gnutar-1.35" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/r022jb6xrsvl7h3smwqcc88fdavz0gw4-gnutar-1.35", + "path": "/nix/store/c309qwh4x5786ipcf30paci7mf20gl0c-gnutar-1.35", "default": true }, { "name": "info", - "path": "/nix/store/b064hipy67xf7a5kw79f84zx3dcnfxn8-gnutar-1.35-info" + "path": "/nix/store/yg31hri62amv02a959rj6y0j9yjl25zx-gnutar-1.35-info" } ], - "store_path": "/nix/store/r022jb6xrsvl7h3smwqcc88fdavz0gw4-gnutar-1.35" + "store_path": "/nix/store/c309qwh4x5786ipcf30paci7mf20gl0c-gnutar-1.35" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/05pl6avyxhbqhgc78vvghg87f44p4i4g-gnutar-1.35", + "path": "/nix/store/4n04dldm6yfiijgk1p74i8jx4zlyvi9f-gnutar-1.35", "default": true }, { "name": "info", - "path": "/nix/store/hffaa92zahkkzyssrbadmaqf13kw1df1-gnutar-1.35-info" + "path": "/nix/store/z5f9kspkbp32450bmdy14if52y0wpbxn-gnutar-1.35-info" } ], - "store_path": "/nix/store/05pl6avyxhbqhgc78vvghg87f44p4i4g-gnutar-1.35" + "store_path": "/nix/store/4n04dldm6yfiijgk1p74i8jx4zlyvi9f-gnutar-1.35" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/gimzvyxqk4gq9rqbf8lc65gdhrkhj0iv-gnutar-1.35", + "path": "/nix/store/nzzl7dnay9jzgfv9fbwg1zza6ji7bjvr-gnutar-1.35", "default": true }, { "name": "info", - "path": "/nix/store/6f46wh8c1dpdakrwlrnmkyzl3fjr4a1k-gnutar-1.35-info" + "path": "/nix/store/kjrphh6wj48rb3xjdqiw81w2ad8lnrlc-gnutar-1.35-info" } ], - "store_path": "/nix/store/gimzvyxqk4gq9rqbf8lc65gdhrkhj0iv-gnutar-1.35" + "store_path": "/nix/store/nzzl7dnay9jzgfv9fbwg1zza6ji7bjvr-gnutar-1.35" } } }, "go-tools@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#go-tools", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#go-tools", "source": "devbox-search", - "version": "2024.1.1", + "version": "2023.1.7", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/d4lfrv2s9m0lk4pd13kv25z0pxw5x3x2-go-tools-2024.1.1", + "path": "/nix/store/86zl4ynzkcn47lc0w1yqmwgqvj6icjs0-go-tools-2023.1.7", "default": true } ], - "store_path": "/nix/store/d4lfrv2s9m0lk4pd13kv25z0pxw5x3x2-go-tools-2024.1.1" + "store_path": "/nix/store/86zl4ynzkcn47lc0w1yqmwgqvj6icjs0-go-tools-2023.1.7" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/h8pxdx739qijcimdi1y97pbc0vmm4nvf-go-tools-2024.1.1", + "path": "/nix/store/ijpcpfyvnbbp9p3nqafc60scrraj6r6b-go-tools-2023.1.7", "default": true } ], - "store_path": "/nix/store/h8pxdx739qijcimdi1y97pbc0vmm4nvf-go-tools-2024.1.1" + "store_path": "/nix/store/ijpcpfyvnbbp9p3nqafc60scrraj6r6b-go-tools-2023.1.7" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/g8vgngx6jhqq44a3568iay9kg6v2dv3x-go-tools-2024.1.1", + "path": "/nix/store/fh65j490nikqss4qjkncc2f62rq8kf92-go-tools-2023.1.7", "default": true } ], - "store_path": "/nix/store/g8vgngx6jhqq44a3568iay9kg6v2dv3x-go-tools-2024.1.1" + "store_path": "/nix/store/fh65j490nikqss4qjkncc2f62rq8kf92-go-tools-2023.1.7" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/631sym9s1rkdqs19qn4iril3isprla4z-go-tools-2024.1.1", + "path": "/nix/store/i5rksvza7cdgdg47rq525kz0i9vsw40c-go-tools-2023.1.7", "default": true } ], - "store_path": "/nix/store/631sym9s1rkdqs19qn4iril3isprla4z-go-tools-2024.1.1" + "store_path": "/nix/store/i5rksvza7cdgdg47rq525kz0i9vsw40c-go-tools-2023.1.7" } } }, @@ -950,56 +950,56 @@ } }, "google-cloud-sdk@latest": { - "last_modified": "2024-09-16T10:17:16Z", - "resolved": "github:NixOS/nixpkgs/20f9370d5f588fb8c72e844c54511cab054b5f40#google-cloud-sdk", + "last_modified": "2024-08-14T11:41:26Z", + "resolved": "github:NixOS/nixpkgs/0cb2fd7c59fed0cd82ef858cbcbdb552b9a33465#google-cloud-sdk", "source": "devbox-search", - "version": "492.0.0", + "version": "485.0.0", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/j9baimnbg147szh8ws2hc1hmkb423riq-google-cloud-sdk-492.0.0", + "path": "/nix/store/gi2icsxy8mdwrc1lqfgfvwzz1k9wamdg-google-cloud-sdk-485.0.0", "default": true } ], - "store_path": "/nix/store/j9baimnbg147szh8ws2hc1hmkb423riq-google-cloud-sdk-492.0.0" + "store_path": "/nix/store/gi2icsxy8mdwrc1lqfgfvwzz1k9wamdg-google-cloud-sdk-485.0.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/r6dgpw8nqg3l8x78i527hb2slz7chk8g-google-cloud-sdk-492.0.0", + "path": "/nix/store/pdwridvvxk84rlgr8pfk07rxgbhji836-google-cloud-sdk-485.0.0", "default": true } ], - "store_path": "/nix/store/r6dgpw8nqg3l8x78i527hb2slz7chk8g-google-cloud-sdk-492.0.0" + "store_path": "/nix/store/pdwridvvxk84rlgr8pfk07rxgbhji836-google-cloud-sdk-485.0.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/5pywqc481xpqgxn89lzxa1lf0p2h13zv-google-cloud-sdk-492.0.0", + "path": "/nix/store/3lc1yqjbl60fi0zykj91byr5xlg155fc-google-cloud-sdk-485.0.0", "default": true } ], - "store_path": "/nix/store/5pywqc481xpqgxn89lzxa1lf0p2h13zv-google-cloud-sdk-492.0.0" + "store_path": "/nix/store/3lc1yqjbl60fi0zykj91byr5xlg155fc-google-cloud-sdk-485.0.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/dqpw1myxbj5zk0f8w0lsfzqq98g23hj9-google-cloud-sdk-492.0.0", + "path": "/nix/store/7s4q044dma8lvlwlj7v8vmf5s8wi949n-google-cloud-sdk-485.0.0", "default": true } ], - "store_path": "/nix/store/dqpw1myxbj5zk0f8w0lsfzqq98g23hj9-google-cloud-sdk-492.0.0" + "store_path": "/nix/store/7s4q044dma8lvlwlj7v8vmf5s8wi949n-google-cloud-sdk-485.0.0" } } }, "gopls@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#gopls", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#gopls", "source": "devbox-search", "version": "0.16.1", "systems": { @@ -1007,41 +1007,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/syc577acmyl666zd3fr7wnpdi8qvrkj5-gopls-0.16.1", + "path": "/nix/store/fjkm2f7gjlwsflrwbd1sz4d3c1wpyvb5-gopls-0.16.1", "default": true } ], - "store_path": "/nix/store/syc577acmyl666zd3fr7wnpdi8qvrkj5-gopls-0.16.1" + "store_path": "/nix/store/fjkm2f7gjlwsflrwbd1sz4d3c1wpyvb5-gopls-0.16.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/1xm5wipnq2qm73qyxm0r0wl90gxwchxd-gopls-0.16.1", + "path": "/nix/store/pw30jfhyvzn848w34glnnd5nmddy3kjp-gopls-0.16.1", "default": true } ], - "store_path": "/nix/store/1xm5wipnq2qm73qyxm0r0wl90gxwchxd-gopls-0.16.1" + "store_path": "/nix/store/pw30jfhyvzn848w34glnnd5nmddy3kjp-gopls-0.16.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/w7yx07za50ypm7x4bzjqzsqgfiib5vja-gopls-0.16.1", + "path": "/nix/store/h0mmsk9jg16g5i52p1g6k1xhdql3i6v2-gopls-0.16.1", "default": true } ], - "store_path": "/nix/store/w7yx07za50ypm7x4bzjqzsqgfiib5vja-gopls-0.16.1" + "store_path": "/nix/store/h0mmsk9jg16g5i52p1g6k1xhdql3i6v2-gopls-0.16.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/acj40fqrk398wvf2mk8y5mzy985dh9j8-gopls-0.16.1", + "path": "/nix/store/szci8xkslrfxzz68aa2kz6la8qamxvrf-gopls-0.16.1", "default": true } ], - "store_path": "/nix/store/acj40fqrk398wvf2mk8y5mzy985dh9j8-gopls-0.16.1" + "store_path": "/nix/store/szci8xkslrfxzz68aa2kz6la8qamxvrf-gopls-0.16.1" } } }, @@ -1094,8 +1094,8 @@ } }, "jq@latest": { - "last_modified": "2024-09-01T03:39:50Z", - "resolved": "github:NixOS/nixpkgs/4a9443e2a4e06cbaff89056b5cdf6777c1fe5755#jq", + "last_modified": "2024-08-14T02:42:29Z", + "resolved": "github:NixOS/nixpkgs/daf7bb95821b789db24fc1ac21f613db0c1bf2cb#jq", "source": "devbox-search", "version": "1.7.1", "systems": { @@ -1103,131 +1103,131 @@ "outputs": [ { "name": "bin", - "path": "/nix/store/a0qb3qz9nancghgi3z1734c5k79hzwgn-jq-1.7.1-bin", + "path": "/nix/store/d9wxqi1f75zarf9wbiwlli3n0nbqhjd7-jq-1.7.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/prp8b726pqh1ghw5idfdcyl0gnpry44q-jq-1.7.1-man", + "path": "/nix/store/p0cvcl9a2qxgvlbsmmx26blr9h6k9kwz-jq-1.7.1-man", "default": true }, - { - "name": "doc", - "path": "/nix/store/a3v44plz7dixbrn9d7s69xhf87g6zrn0-jq-1.7.1-doc" - }, { "name": "lib", - "path": "/nix/store/8n0r1ma1x5bvsr6n2kjzrhv8s7qyl9m1-jq-1.7.1-lib" + "path": "/nix/store/6cd405yg737s18blzgvqzgyw96fl1x0p-jq-1.7.1-lib" }, { "name": "out", - "path": "/nix/store/20x8m876m0a3nas08c4gg7n1gcvw2nd1-jq-1.7.1" + "path": "/nix/store/p08mdq0qx0l3yzpnh17ll9dc47bwnvsv-jq-1.7.1" }, { "name": "dev", - "path": "/nix/store/4rcpydzqwdck396pwhkcpz1r1brlgli4-jq-1.7.1-dev" + "path": "/nix/store/0hz6vhnzw7mfiw3w20cnzq36c2y27n2f-jq-1.7.1-dev" + }, + { + "name": "doc", + "path": "/nix/store/9br49dwjvgdgj0x31c4pv4rq1canwa60-jq-1.7.1-doc" } ], - "store_path": "/nix/store/a0qb3qz9nancghgi3z1734c5k79hzwgn-jq-1.7.1-bin" + "store_path": "/nix/store/d9wxqi1f75zarf9wbiwlli3n0nbqhjd7-jq-1.7.1-bin" }, "aarch64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/855185vhfhrx065c0ad0s1qms3p8sg4c-jq-1.7.1-bin", + "path": "/nix/store/hd0ngywgcjbn6jnmcnvm0d4lmg0s99l0-jq-1.7.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/9w36a5zl1wz9mnk22np556mfdicfzyfa-jq-1.7.1-man", + "path": "/nix/store/s4v2nw498ck1rwx2j90gks1ydix9ha3q-jq-1.7.1-man", "default": true }, { - "name": "dev", - "path": "/nix/store/zxyzp99sm3nkf387h9xrq8kf4pkp7jw8-jq-1.7.1-dev" + "name": "lib", + "path": "/nix/store/pp2063qjgqik7k5d6iwwpc8wn52jbick-jq-1.7.1-lib" }, { - "name": "doc", - "path": "/nix/store/h8iz5m7aq09an64n3xjdrpvnskgqg0j5-jq-1.7.1-doc" + "name": "out", + "path": "/nix/store/fz0dc5yykkk2lh8d9kndsywqwl68aiy2-jq-1.7.1" }, { - "name": "lib", - "path": "/nix/store/wkcx946q0kbpp0jb4rs3yq3ippiz5x83-jq-1.7.1-lib" + "name": "dev", + "path": "/nix/store/w9h3rnm5cgzj90xhbg4p8ki4ns8s0ikl-jq-1.7.1-dev" }, { - "name": "out", - "path": "/nix/store/x5p64yy46c4g5a6788av88fr8nxpfmvl-jq-1.7.1" + "name": "doc", + "path": "/nix/store/a0c7dharmysjskkgp0zfkqp3z175b5pg-jq-1.7.1-doc" } ], - "store_path": "/nix/store/855185vhfhrx065c0ad0s1qms3p8sg4c-jq-1.7.1-bin" + "store_path": "/nix/store/hd0ngywgcjbn6jnmcnvm0d4lmg0s99l0-jq-1.7.1-bin" }, "x86_64-darwin": { "outputs": [ { "name": "bin", - "path": "/nix/store/wlzv4fh056vg7i9zkcggqvfcnargj9cg-jq-1.7.1-bin", + "path": "/nix/store/kwkps100pvk6s8is34bqp1ycydbn2v7a-jq-1.7.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/5wsb8dxsvkhl3a4amaw3q237k31zm3xn-jq-1.7.1-man", + "path": "/nix/store/x46yqlsn1357pcpm7nvbrs863aak2pyb-jq-1.7.1-man", "default": true }, + { + "name": "out", + "path": "/nix/store/5pl3gvxdjpvig6cvv77fzg8asaalx07d-jq-1.7.1" + }, { "name": "dev", - "path": "/nix/store/1brda1qm783rz07f0a8y4mvs6m72v5ha-jq-1.7.1-dev" + "path": "/nix/store/69z4pdr45l1ixix3bjnfyfy1wqfshmby-jq-1.7.1-dev" }, { "name": "doc", - "path": "/nix/store/rnpf9ypnakmigb5wd9cbzzqs7919bqna-jq-1.7.1-doc" + "path": "/nix/store/6qlmlhii37fxxc36c54z5jb3xqjk2fp8-jq-1.7.1-doc" }, { "name": "lib", - "path": "/nix/store/3x3n94nvpfhb55n3039avj3cc13sqwmf-jq-1.7.1-lib" - }, - { - "name": "out", - "path": "/nix/store/5v08b5wxh9i1aqxr4j4v731phms6q4hj-jq-1.7.1" + "path": "/nix/store/61l4nqvpfpx7ilk9xdjbn2y8knk72kv4-jq-1.7.1-lib" } ], - "store_path": "/nix/store/wlzv4fh056vg7i9zkcggqvfcnargj9cg-jq-1.7.1-bin" + "store_path": "/nix/store/kwkps100pvk6s8is34bqp1ycydbn2v7a-jq-1.7.1-bin" }, "x86_64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/kc1c2x2a14zsjwxlscajmc59nsz0wd1s-jq-1.7.1-bin", + "path": "/nix/store/yw7dn51dwbmw2pkx5fqhgadpzyv8f724-jq-1.7.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/vdc00mcaj7xlk2f0sq7lyjgf0l3v9gvr-jq-1.7.1-man", + "path": "/nix/store/kvi43jy0kzsbkq4kmfgmrk6yw596bnb4-jq-1.7.1-man", "default": true }, + { + "name": "out", + "path": "/nix/store/h2lkpgx68cisqrka1x0arskiv39ngkm6-jq-1.7.1" + }, { "name": "dev", - "path": "/nix/store/rwypdgzzxk9740bd01irnq708p62axah-jq-1.7.1-dev" + "path": "/nix/store/wvcfmwz1hjs1fx4i182yfwwcvf01j1x8-jq-1.7.1-dev" }, { "name": "doc", - "path": "/nix/store/sifr3zf0rxisxs6ysvp3r2jds5pd22lj-jq-1.7.1-doc" + "path": "/nix/store/6gfjfw1akzpkhh3rfqbyyshpxaj9d1hw-jq-1.7.1-doc" }, { "name": "lib", - "path": "/nix/store/ssl0zcvizmyncqn3lwr3dmkb8j0x9fkn-jq-1.7.1-lib" - }, - { - "name": "out", - "path": "/nix/store/6mi0s34rv17dxd8bpv3vis709n230mfi-jq-1.7.1" + "path": "/nix/store/jn7ahwhssnkkg1m669x78qq163y3zyz3-jq-1.7.1-lib" } ], - "store_path": "/nix/store/kc1c2x2a14zsjwxlscajmc59nsz0wd1s-jq-1.7.1-bin" + "store_path": "/nix/store/yw7dn51dwbmw2pkx5fqhgadpzyv8f724-jq-1.7.1-bin" } } }, "markdownlint-cli2@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#markdownlint-cli2", + "last_modified": "2024-09-10T15:01:03Z", + "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#markdownlint-cli2", "source": "devbox-search", "version": "0.13.0", "systems": { @@ -1235,41 +1235,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/3c6jijgaj6sgiqgjc6k4mbjqjhadiiyi-markdownlint-cli2-0.13.0", + "path": "/nix/store/21sfamr4pjsa0ardwy3rj3rycr5fmzhj-markdownlint-cli2-0.13.0", "default": true } ], - "store_path": "/nix/store/3c6jijgaj6sgiqgjc6k4mbjqjhadiiyi-markdownlint-cli2-0.13.0" + "store_path": "/nix/store/21sfamr4pjsa0ardwy3rj3rycr5fmzhj-markdownlint-cli2-0.13.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/yxa8n8fzz331fzwn0hp3n8ngj3329b8v-markdownlint-cli2-0.13.0", + "path": "/nix/store/2qcya6yhm461yiyd2553nkr5sdq4za4f-markdownlint-cli2-0.13.0", "default": true } ], - "store_path": "/nix/store/yxa8n8fzz331fzwn0hp3n8ngj3329b8v-markdownlint-cli2-0.13.0" + "store_path": "/nix/store/2qcya6yhm461yiyd2553nkr5sdq4za4f-markdownlint-cli2-0.13.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/nlfjx2b5gh1src39b7kc9x75rgxy27z2-markdownlint-cli2-0.13.0", + "path": "/nix/store/yk1cifxgvlv6cbrwilw7sm4dnlfkavsi-markdownlint-cli2-0.13.0", "default": true } ], - "store_path": "/nix/store/nlfjx2b5gh1src39b7kc9x75rgxy27z2-markdownlint-cli2-0.13.0" + "store_path": "/nix/store/yk1cifxgvlv6cbrwilw7sm4dnlfkavsi-markdownlint-cli2-0.13.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/1h33xxbc66amkss9v126ai79gd4sm03l-markdownlint-cli2-0.13.0", + "path": "/nix/store/qyg3lr1xvfk1sfsbc78kvh3lv813gcv2-markdownlint-cli2-0.13.0", "default": true } ], - "store_path": "/nix/store/1h33xxbc66amkss9v126ai79gd4sm03l-markdownlint-cli2-0.13.0" + "store_path": "/nix/store/qyg3lr1xvfk1sfsbc78kvh3lv813gcv2-markdownlint-cli2-0.13.0" } } }, @@ -1370,230 +1370,230 @@ } }, "nodejs@latest": { - "last_modified": "2024-08-31T10:12:23Z", + "last_modified": "2024-07-31T08:48:38Z", "plugin_version": "0.0.2", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#nodejs_22", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#nodejs_22", "source": "devbox-search", - "version": "22.6.0", + "version": "22.4.1", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/h0lgf92wq4ip40d15mdah4548s5ck3lm-nodejs-22.6.0", + "path": "/nix/store/z5whjsr6cnqw0ycnylwxmibfr7cdwxrb-nodejs-22.4.1", "default": true }, { "name": "libv8", - "path": "/nix/store/cvyxkafalvlc8nymm9qghmd23lcsdq73-nodejs-22.6.0-libv8" + "path": "/nix/store/vzhng0wwbm0r1p9rdq1kspm0j5b9x0va-nodejs-22.4.1-libv8" } ], - "store_path": "/nix/store/h0lgf92wq4ip40d15mdah4548s5ck3lm-nodejs-22.6.0" + "store_path": "/nix/store/z5whjsr6cnqw0ycnylwxmibfr7cdwxrb-nodejs-22.4.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/i43skcppvpbprz429nlf6xh6x6gjk1w0-nodejs-22.6.0", + "path": "/nix/store/2c0qimdjslxal42s4m10zj95f9r11l4l-nodejs-22.4.1", "default": true }, { "name": "libv8", - "path": "/nix/store/1k0qb1hlhxzd45yirswq9ryzi8h2lzj7-nodejs-22.6.0-libv8" + "path": "/nix/store/aq9wvafy8svx7fgkm2w0yig3aqmkbqc8-nodejs-22.4.1-libv8" } ], - "store_path": "/nix/store/i43skcppvpbprz429nlf6xh6x6gjk1w0-nodejs-22.6.0" + "store_path": "/nix/store/2c0qimdjslxal42s4m10zj95f9r11l4l-nodejs-22.4.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/vpz3z6kvx63xiwgippddkp62vi4h8g3z-nodejs-22.6.0", + "path": "/nix/store/5l6flz4fpax64nzivg2r815pvllj2dml-nodejs-22.4.1", "default": true }, { "name": "libv8", - "path": "/nix/store/k9gallbn1nij77ryg2dix2iwrrky8c4g-nodejs-22.6.0-libv8" + "path": "/nix/store/5wwl7dh3xmnrah4f98713i93m43d02a5-nodejs-22.4.1-libv8" } ], - "store_path": "/nix/store/vpz3z6kvx63xiwgippddkp62vi4h8g3z-nodejs-22.6.0" + "store_path": "/nix/store/5l6flz4fpax64nzivg2r815pvllj2dml-nodejs-22.4.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/gb9rkc33y0qxk8ps1wyflry1gmz2q9w8-nodejs-22.6.0", + "path": "/nix/store/w8rva81863ls2ic3qc72mdzpzm0skyrm-nodejs-22.4.1", "default": true }, { "name": "libv8", - "path": "/nix/store/6v7dxcn0azh24swdv3745pfprhxcg4jr-nodejs-22.6.0-libv8" + "path": "/nix/store/93cvy69p4g2mrbjjkj5dxh8zin047nl5-nodejs-22.4.1-libv8" } ], - "store_path": "/nix/store/gb9rkc33y0qxk8ps1wyflry1gmz2q9w8-nodejs-22.6.0" + "store_path": "/nix/store/w8rva81863ls2ic3qc72mdzpzm0skyrm-nodejs-22.4.1" } } }, "oha@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#oha", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#oha", "source": "devbox-search", - "version": "1.4.6", + "version": "1.4.5", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/z1b280imwfkx7l7p0qasviaqjfi9xapy-oha-1.4.6", + "path": "/nix/store/6zy9zgmv4fxwmpcjqlabj7rdixgkg69n-oha-1.4.5", "default": true } ], - "store_path": "/nix/store/z1b280imwfkx7l7p0qasviaqjfi9xapy-oha-1.4.6" + "store_path": "/nix/store/6zy9zgmv4fxwmpcjqlabj7rdixgkg69n-oha-1.4.5" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/4bsvh357cq759vx7w5xwij52h5nxsiih-oha-1.4.6", + "path": "/nix/store/yfbcflzvnfyq0mvgdhnqvl35bp3balzw-oha-1.4.5", "default": true } ], - "store_path": "/nix/store/4bsvh357cq759vx7w5xwij52h5nxsiih-oha-1.4.6" + "store_path": "/nix/store/yfbcflzvnfyq0mvgdhnqvl35bp3balzw-oha-1.4.5" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/zai34w521i7axqirkbpwzixzw37zf0vm-oha-1.4.6", + "path": "/nix/store/dhgdzc110fx081rhiq0xi7axysz601x9-oha-1.4.5", "default": true } ], - "store_path": "/nix/store/zai34w521i7axqirkbpwzixzw37zf0vm-oha-1.4.6" + "store_path": "/nix/store/dhgdzc110fx081rhiq0xi7axysz601x9-oha-1.4.5" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ph6amnlf1q6wr37iq6gw8ajqxxzgk2vc-oha-1.4.6", + "path": "/nix/store/i4l0nw9cwhv1lj391yj5mjhm0x34ynmd-oha-1.4.5", "default": true } ], - "store_path": "/nix/store/ph6amnlf1q6wr37iq6gw8ajqxxzgk2vc-oha-1.4.6" + "store_path": "/nix/store/i4l0nw9cwhv1lj391yj5mjhm0x34ynmd-oha-1.4.5" } } }, "postgresql@latest": { - "last_modified": "2024-09-01T03:39:50Z", + "last_modified": "2024-08-07T17:07:38Z", "plugin_version": "0.0.2", - "resolved": "github:NixOS/nixpkgs/4a9443e2a4e06cbaff89056b5cdf6777c1fe5755#postgresql", + "resolved": "github:NixOS/nixpkgs/61448af9e290aec3be42741abaef6a52ae31f6a4#postgresql", "source": "devbox-search", - "version": "16.4", + "version": "15.7", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/4xs8m764256kn9hr2m9nfv4nhzcs876i-postgresql-16.4", + "path": "/nix/store/j063gkab0kj312p0r5wlwh8hhs3ivmmv-postgresql-15.7", "default": true }, { "name": "man", - "path": "/nix/store/sga8v7kf4r50lp5pmylsavvrb2jn9b1i-postgresql-16.4-man", + "path": "/nix/store/83zyb6qnvn85ilfb4g03yr8zjnc4kw5c-postgresql-15.7-man", "default": true }, { - "name": "doc", - "path": "/nix/store/vvq5zc10jg57cjky6xdc4z53bzzvv2k7-postgresql-16.4-doc" + "name": "lib", + "path": "/nix/store/2lqmjj3nwingqsajwgwym4jjl1plqrxd-postgresql-15.7-lib" }, { - "name": "lib", - "path": "/nix/store/cq2lfr6m71ppmx4v8vdjj2qbv5r7f8bv-postgresql-16.4-lib" + "name": "doc", + "path": "/nix/store/w98l40gkbw15cxjajs9wr9aaz1zqq8pv-postgresql-15.7-doc" } ], - "store_path": "/nix/store/4xs8m764256kn9hr2m9nfv4nhzcs876i-postgresql-16.4" + "store_path": "/nix/store/j063gkab0kj312p0r5wlwh8hhs3ivmmv-postgresql-15.7" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/aaphlis1flanaskw54vwzy0383dcinmm-postgresql-16.4", + "path": "/nix/store/bm08f8k2gyndfq1mszvdm07jnmwr6nlf-postgresql-15.7", "default": true }, { "name": "man", - "path": "/nix/store/wrscx3x20gv830ksy41xpyxpdab7g64q-postgresql-16.4-man", + "path": "/nix/store/yd6qvs38zm55271230hfn4j0rd4029ca-postgresql-15.7-man", "default": true }, { "name": "debug", - "path": "/nix/store/a2v8ji7rsmc7bhnw09wz4zbprfxd1bsh-postgresql-16.4-debug" + "path": "/nix/store/m8kdahlx418v1x8pvbjja5zbl8ix4hff-postgresql-15.7-debug" }, { "name": "doc", - "path": "/nix/store/zvhdzrpdx0bdhj59bhcnr17csq4sm5ad-postgresql-16.4-doc" + "path": "/nix/store/nw2ng4sh1vzih1rrfzlivd2c7ifh9zm2-postgresql-15.7-doc" }, { "name": "lib", - "path": "/nix/store/w0m8i8x3gviq0jf986a298z9jx03alix-postgresql-16.4-lib" + "path": "/nix/store/fvs07mhcygpwy41jhw34kq7ghlcnv4nf-postgresql-15.7-lib" } ], - "store_path": "/nix/store/aaphlis1flanaskw54vwzy0383dcinmm-postgresql-16.4" + "store_path": "/nix/store/bm08f8k2gyndfq1mszvdm07jnmwr6nlf-postgresql-15.7" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/qcgnc3vfiq94fsn07y6hjf00rx6g7hx0-postgresql-16.4", + "path": "/nix/store/q4h9xjc0804xkcb0l7kxmp8rbqadlpg9-postgresql-15.7", "default": true }, { "name": "man", - "path": "/nix/store/xn3wqkgs1n2fxghczxasam7gc6ig0q42-postgresql-16.4-man", + "path": "/nix/store/22xrk7r5c4bh12wnmh4xssd1xk06b96l-postgresql-15.7-man", "default": true }, { "name": "doc", - "path": "/nix/store/h9gr53mxkd9dsabw2migk25bbd2bh6aw-postgresql-16.4-doc" + "path": "/nix/store/4ynkh5cclgi0jcg42868z764irl32dx3-postgresql-15.7-doc" }, { "name": "lib", - "path": "/nix/store/6rgrvr20s3q1cx6f5lamdqlxqvxd9263-postgresql-16.4-lib" + "path": "/nix/store/4bqzgkyvzc7c7i955raibmaqabffsi4y-postgresql-15.7-lib" } ], - "store_path": "/nix/store/qcgnc3vfiq94fsn07y6hjf00rx6g7hx0-postgresql-16.4" + "store_path": "/nix/store/q4h9xjc0804xkcb0l7kxmp8rbqadlpg9-postgresql-15.7" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/w11g7nvm4h1ph7kjq6c4cxna3yppsn2v-postgresql-16.4", + "path": "/nix/store/8gr5ybhmdkafii5idcg57p66nk1qd6sf-postgresql-15.7", "default": true }, { "name": "man", - "path": "/nix/store/dzm8il4j20xpcvqwf8z0rgnqhsq3wgdz-postgresql-16.4-man", + "path": "/nix/store/0j6lskwq3imd8gdwy5rz9sjmn3c41qbc-postgresql-15.7-man", "default": true }, { "name": "debug", - "path": "/nix/store/6f8bx4qwnlyk8f6x4551kkpzmcb8kbky-postgresql-16.4-debug" + "path": "/nix/store/xw1fhj72fzrlkvapaf1spx19ixqm7394-postgresql-15.7-debug" }, { "name": "doc", - "path": "/nix/store/zfx8r6g70bxm08vyz4px3xvhz6i2q1x1-postgresql-16.4-doc" + "path": "/nix/store/01snq9n6ka7zkb4dp7k639mbb5p0v5qi-postgresql-15.7-doc" }, { "name": "lib", - "path": "/nix/store/ffdpyjcm8ax76fa5bxhqvjb4kyjj3i37-postgresql-16.4-lib" + "path": "/nix/store/9xj29q1wf5wazv63hn5dxlwsp8k3h5lc-postgresql-15.7-lib" } ], - "store_path": "/nix/store/w11g7nvm4h1ph7kjq6c4cxna3yppsn2v-postgresql-16.4" + "store_path": "/nix/store/8gr5ybhmdkafii5idcg57p66nk1qd6sf-postgresql-15.7" } } }, "pre-commit@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#pre-commit", + "last_modified": "2024-08-01T02:09:53Z", + "resolved": "github:NixOS/nixpkgs/799bc8d7b16e6779f0105713e6794899133c4a38#pre-commit", "source": "devbox-search", "version": "3.7.1", "systems": { @@ -1601,169 +1601,169 @@ "outputs": [ { "name": "out", - "path": "/nix/store/5g55f7d31vlac7c93chh86vqr3c5c3ri-pre-commit-3.7.1", + "path": "/nix/store/dwn5fkjvzz2lld6qyk3schl67l3in214-pre-commit-3.7.1", "default": true }, { "name": "dist", - "path": "/nix/store/qy583afkz2dpkm002bwifhgd8x4858hj-pre-commit-3.7.1-dist" + "path": "/nix/store/91m7xjkl860fd1jgpycf331p9h7id458-pre-commit-3.7.1-dist" } ], - "store_path": "/nix/store/5g55f7d31vlac7c93chh86vqr3c5c3ri-pre-commit-3.7.1" + "store_path": "/nix/store/dwn5fkjvzz2lld6qyk3schl67l3in214-pre-commit-3.7.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/b1v22x5qiwrw9pvkcz5wn5wimgpwa33i-pre-commit-3.7.1", + "path": "/nix/store/pknbsn6pxca776n1szqipgx133dwh0kh-pre-commit-3.7.1", "default": true }, { "name": "dist", - "path": "/nix/store/x0gcrvjnj27alqh4ac1x3gkz71r5pjh3-pre-commit-3.7.1-dist" + "path": "/nix/store/z2p5svdhfckxga5sl1iyw4p0ldy2ngsq-pre-commit-3.7.1-dist" } ], - "store_path": "/nix/store/b1v22x5qiwrw9pvkcz5wn5wimgpwa33i-pre-commit-3.7.1" + "store_path": "/nix/store/pknbsn6pxca776n1szqipgx133dwh0kh-pre-commit-3.7.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/1zic7nfa8cc952516r5aspsisp09bls8-pre-commit-3.7.1", + "path": "/nix/store/q54xmykk8h13b8gc9rq0s1cndnkcf54w-pre-commit-3.7.1", "default": true }, { "name": "dist", - "path": "/nix/store/jx0vz9353fi7k5nsmx1hbzbx45v4w1ag-pre-commit-3.7.1-dist" + "path": "/nix/store/8wcznvmxcfamh2d5nr49lxqy3ydadxc6-pre-commit-3.7.1-dist" } ], - "store_path": "/nix/store/1zic7nfa8cc952516r5aspsisp09bls8-pre-commit-3.7.1" + "store_path": "/nix/store/q54xmykk8h13b8gc9rq0s1cndnkcf54w-pre-commit-3.7.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/iqh91d6ca4vmf7xkdjf1bm19vx951d6x-pre-commit-3.7.1", + "path": "/nix/store/pya1ln69fdz8x5hp2qz2sdg50xr5680x-pre-commit-3.7.1", "default": true }, { "name": "dist", - "path": "/nix/store/ayz1q36zrplb8qnl41swbcz4ni0f5jb4-pre-commit-3.7.1-dist" + "path": "/nix/store/r2xjg3kwiin09yqb953jzn0jplikzphx-pre-commit-3.7.1-dist" } ], - "store_path": "/nix/store/iqh91d6ca4vmf7xkdjf1bm19vx951d6x-pre-commit-3.7.1" + "store_path": "/nix/store/pya1ln69fdz8x5hp2qz2sdg50xr5680x-pre-commit-3.7.1" } } }, "python@latest": { - "last_modified": "2024-08-31T10:12:23Z", + "last_modified": "2024-07-31T08:48:38Z", "plugin_version": "0.0.4", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#python3", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#python3", "source": "devbox-search", - "version": "3.12.5", + "version": "3.12.4", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/75j38g8ii1nqkmpf6sdlj3s5dyah3gas-python3-3.12.5", + "path": "/nix/store/1sgajx2r3bkriyxzwsahhva63p08pmac-python3-3.12.4", "default": true } ], - "store_path": "/nix/store/75j38g8ii1nqkmpf6sdlj3s5dyah3gas-python3-3.12.5" + "store_path": "/nix/store/1sgajx2r3bkriyxzwsahhva63p08pmac-python3-3.12.4" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ajjwc8k8sk3ksrl3dq4fsg83m1j8n8s3-python3-3.12.5", + "path": "/nix/store/jms4z7lzzwnv6gv3y0795365haicmh8m-python3-3.12.4", "default": true }, { "name": "debug", - "path": "/nix/store/9qvs74485a1v5255w2ps0xf4rxww6w89-python3-3.12.5-debug" + "path": "/nix/store/rshmxwibmxqvf94w3ld05yqnsijrmk4m-python3-3.12.4-debug" } ], - "store_path": "/nix/store/ajjwc8k8sk3ksrl3dq4fsg83m1j8n8s3-python3-3.12.5" + "store_path": "/nix/store/jms4z7lzzwnv6gv3y0795365haicmh8m-python3-3.12.4" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/rv3rj95fxv57c7qwgl43qa7n0fabdy0a-python3-3.12.5", + "path": "/nix/store/dh1i1387ibdzw0ala5rkl3s3ylf8i8pa-python3-3.12.4", "default": true } ], - "store_path": "/nix/store/rv3rj95fxv57c7qwgl43qa7n0fabdy0a-python3-3.12.5" + "store_path": "/nix/store/dh1i1387ibdzw0ala5rkl3s3ylf8i8pa-python3-3.12.4" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/pgb120fb7srbh418v4i2a70aq1w9dawd-python3-3.12.5", + "path": "/nix/store/l014xp1qxdl6gim3zc0jv3mpxhbp346s-python3-3.12.4", "default": true }, { "name": "debug", - "path": "/nix/store/4ws5lqhgsxdpfb924n49ma6ll7i8x0hf-python3-3.12.5-debug" + "path": "/nix/store/myg0p2vf2cj2jsb663qswnygvgn54kbc-python3-3.12.4-debug" } ], - "store_path": "/nix/store/pgb120fb7srbh418v4i2a70aq1w9dawd-python3-3.12.5" + "store_path": "/nix/store/l014xp1qxdl6gim3zc0jv3mpxhbp346s-python3-3.12.4" } } }, "rubocop@latest": { - "last_modified": "2024-08-31T19:22:30Z", - "resolved": "github:NixOS/nixpkgs/282e35e0762d64800d78e33ff225704baf9e5216#rubocop", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#rubocop", "source": "devbox-search", - "version": "1.65.1", + "version": "1.62.1", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/f7mjcpgy4kac87ygnwrc8zss4ps67x1w-ruby3.3-rubocop-1.65.1", + "path": "/nix/store/3f2d4498dwyja9zalgkx59i6xrd7m16i-ruby3.1-rubocop-1.62.1", "default": true } ], - "store_path": "/nix/store/f7mjcpgy4kac87ygnwrc8zss4ps67x1w-ruby3.3-rubocop-1.65.1" + "store_path": "/nix/store/3f2d4498dwyja9zalgkx59i6xrd7m16i-ruby3.1-rubocop-1.62.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ls69blqq10hqadgn99v02qhdzfs99jkk-ruby3.3-rubocop-1.65.1", + "path": "/nix/store/3df504w1r5g3zk51hq83699wc52qvz2k-ruby3.1-rubocop-1.62.1", "default": true } ], - "store_path": "/nix/store/ls69blqq10hqadgn99v02qhdzfs99jkk-ruby3.3-rubocop-1.65.1" + "store_path": "/nix/store/3df504w1r5g3zk51hq83699wc52qvz2k-ruby3.1-rubocop-1.62.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/xri9m3wj2zq4bwsszc9bkcn38664nl66-ruby3.3-rubocop-1.65.1", + "path": "/nix/store/d4j25l1szpf379pgg84c5bf18fzfrwkq-ruby3.1-rubocop-1.62.1", "default": true } ], - "store_path": "/nix/store/xri9m3wj2zq4bwsszc9bkcn38664nl66-ruby3.3-rubocop-1.65.1" + "store_path": "/nix/store/d4j25l1szpf379pgg84c5bf18fzfrwkq-ruby3.1-rubocop-1.62.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/358ycw2rdmyr26rxsxsszhsyfmxps29b-ruby3.3-rubocop-1.65.1", + "path": "/nix/store/mq6migs5hpjarlrayzhd4mx4vqr5rq8p-ruby3.1-rubocop-1.62.1", "default": true } ], - "store_path": "/nix/store/358ycw2rdmyr26rxsxsszhsyfmxps29b-ruby3.3-rubocop-1.65.1" + "store_path": "/nix/store/mq6migs5hpjarlrayzhd4mx4vqr5rq8p-ruby3.1-rubocop-1.62.1" } } }, "ruby@latest": { - "last_modified": "2024-09-10T15:01:03Z", + "last_modified": "2024-08-14T11:41:26Z", "plugin_version": "0.0.2", - "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#ruby", + "resolved": "github:NixOS/nixpkgs/0cb2fd7c59fed0cd82ef858cbcbdb552b9a33465#ruby_3_3", "source": "devbox-search", "version": "3.3.4", "systems": { @@ -1771,63 +1771,63 @@ "outputs": [ { "name": "out", - "path": "/nix/store/zsngi7cq7asxc6kb0lcmbyydasfrmai6-ruby-3.3.4", + "path": "/nix/store/9bxxr0h0fvgv6sf3w7qrbirv66psmrp0-ruby-3.3.4", "default": true }, { "name": "devdoc", - "path": "/nix/store/c0zmjmfw5pp00slwy842bd8zzy74pava-ruby-3.3.4-devdoc" + "path": "/nix/store/rw78r69d76fpjabhv0yqvq065i7hk7x8-ruby-3.3.4-devdoc" } ], - "store_path": "/nix/store/zsngi7cq7asxc6kb0lcmbyydasfrmai6-ruby-3.3.4" + "store_path": "/nix/store/9bxxr0h0fvgv6sf3w7qrbirv66psmrp0-ruby-3.3.4" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/8vri7d6b75gspvnfrdpqnagfi5bbvis9-ruby-3.3.4", + "path": "/nix/store/pznpsnxp0z3rymdz9sp7fgf330apsp4h-ruby-3.3.4", "default": true }, { "name": "devdoc", - "path": "/nix/store/bfrwr1n6pxd3hz31h589j317g290p0r5-ruby-3.3.4-devdoc" + "path": "/nix/store/whgzvyjfnaaxjlbhjaa02rm55794ilq7-ruby-3.3.4-devdoc" } ], - "store_path": "/nix/store/8vri7d6b75gspvnfrdpqnagfi5bbvis9-ruby-3.3.4" + "store_path": "/nix/store/pznpsnxp0z3rymdz9sp7fgf330apsp4h-ruby-3.3.4" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/hksac0axpx3savrgmy75rid3smv0d9nj-ruby-3.3.4", + "path": "/nix/store/xd977g6j6gbr3pwlix9030v3s48z22cv-ruby-3.3.4", "default": true }, { "name": "devdoc", - "path": "/nix/store/3564jxjgp9dk8vzjwb7i3fpzkpw74z3m-ruby-3.3.4-devdoc" + "path": "/nix/store/d1fywywndcqxiih82ysv3pm3mmd9n9fa-ruby-3.3.4-devdoc" } ], - "store_path": "/nix/store/hksac0axpx3savrgmy75rid3smv0d9nj-ruby-3.3.4" + "store_path": "/nix/store/xd977g6j6gbr3pwlix9030v3s48z22cv-ruby-3.3.4" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/2vg3asd3rmhng3wqvp781a1q5dhrr4yn-ruby-3.3.4", + "path": "/nix/store/bx1vnac35l0vd39390v5ddz73mjf20r7-ruby-3.3.4", "default": true }, { "name": "devdoc", - "path": "/nix/store/2vgrf69lcz8ipawxipza3w7gsqdnbbnk-ruby-3.3.4-devdoc" + "path": "/nix/store/kg19xgwfiz7yg329jsy85hs3mhmlii3b-ruby-3.3.4-devdoc" } ], - "store_path": "/nix/store/2vg3asd3rmhng3wqvp781a1q5dhrr4yn-ruby-3.3.4" + "store_path": "/nix/store/bx1vnac35l0vd39390v5ddz73mjf20r7-ruby-3.3.4" } } }, "rubyPackages.solargraph@latest": { - "last_modified": "2024-08-31T19:22:30Z", - "resolved": "github:NixOS/nixpkgs/282e35e0762d64800d78e33ff225704baf9e5216#rubyPackages.solargraph", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#rubyPackages.solargraph", "source": "devbox-search", "version": "0.50.0", "systems": { @@ -1835,47 +1835,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/b7smjfdy3wjfsfd8x2hl14kv1mbjxaxh-ruby3.3-solargraph-0.50.0", + "path": "/nix/store/7nyxvcs0hbchwnn74ymijw17kvg89nhv-ruby3.1-solargraph-0.50.0", "default": true } ], - "store_path": "/nix/store/b7smjfdy3wjfsfd8x2hl14kv1mbjxaxh-ruby3.3-solargraph-0.50.0" + "store_path": "/nix/store/7nyxvcs0hbchwnn74ymijw17kvg89nhv-ruby3.1-solargraph-0.50.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/1zjr5vd4r3mycf7yvgngd39kx7hn22f3-ruby3.3-solargraph-0.50.0", + "path": "/nix/store/hpxz314jqzz4ffn2xi4rbw59py4nz7cm-ruby3.1-solargraph-0.50.0", "default": true } ], - "store_path": "/nix/store/1zjr5vd4r3mycf7yvgngd39kx7hn22f3-ruby3.3-solargraph-0.50.0" + "store_path": "/nix/store/hpxz314jqzz4ffn2xi4rbw59py4nz7cm-ruby3.1-solargraph-0.50.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/lsgg4jbldhzizvzpw3wc78pqa7m2idp7-ruby3.3-solargraph-0.50.0", + "path": "/nix/store/2gvkxsz1wwacr4b4ciw6mw4cq4aas0jy-ruby3.1-solargraph-0.50.0", "default": true } ], - "store_path": "/nix/store/lsgg4jbldhzizvzpw3wc78pqa7m2idp7-ruby3.3-solargraph-0.50.0" + "store_path": "/nix/store/2gvkxsz1wwacr4b4ciw6mw4cq4aas0jy-ruby3.1-solargraph-0.50.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/3gswm57kl2ygzw6npw5nnzd5bgcgnnyq-ruby3.3-solargraph-0.50.0", + "path": "/nix/store/92g8hk03pq9k0i6mppb5s3pn2hs76anb-ruby3.1-solargraph-0.50.0", "default": true } ], - "store_path": "/nix/store/3gswm57kl2ygzw6npw5nnzd5bgcgnnyq-ruby3.3-solargraph-0.50.0" + "store_path": "/nix/store/92g8hk03pq9k0i6mppb5s3pn2hs76anb-ruby3.1-solargraph-0.50.0" } } }, "shellcheck@0.10.0": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#shellcheck", + "last_modified": "2024-08-01T02:09:53Z", + "resolved": "github:NixOS/nixpkgs/799bc8d7b16e6779f0105713e6794899133c4a38#shellcheck", "source": "devbox-search", "version": "0.10.0", "systems": { @@ -1883,103 +1883,103 @@ "outputs": [ { "name": "bin", - "path": "/nix/store/xzp9ys92l1d18brv2q183r3i12wjj61w-shellcheck-0.10.0-bin", + "path": "/nix/store/k2vfa0fkx8fjxqfha1q6mszxvgfnmdiy-shellcheck-0.10.0-bin", "default": true }, { "name": "man", - "path": "/nix/store/fylwfi4p0h8xx2w4pp37rwrx5di3hsbs-shellcheck-0.10.0-man", + "path": "/nix/store/iwzay0ybpvnbmgghd745gvkxzfjmfciy-shellcheck-0.10.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/f3j35ci3f1n6ic979g1zmf721iv99h8f-shellcheck-0.10.0-doc", + "path": "/nix/store/21zqgs561lzygmcjrs0n8yyannf6hils-shellcheck-0.10.0-doc", "default": true }, { "name": "out", - "path": "/nix/store/pxi0dvf9mgam5i7szl9scaydwcwaqpgh-shellcheck-0.10.0" + "path": "/nix/store/xrvyhw3szxbp2ay76jpc1qj7ngz6jpn6-shellcheck-0.10.0" } ], - "store_path": "/nix/store/xzp9ys92l1d18brv2q183r3i12wjj61w-shellcheck-0.10.0-bin" + "store_path": "/nix/store/k2vfa0fkx8fjxqfha1q6mszxvgfnmdiy-shellcheck-0.10.0-bin" }, "aarch64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/gs33kdch6p8icbphmi4jd5hfjpv57hxs-shellcheck-0.10.0-bin", + "path": "/nix/store/ywmf20zyjivrhw97v7s1fi51y76fs5jr-shellcheck-0.10.0-bin", "default": true }, { "name": "man", - "path": "/nix/store/hla6fpjvcmiwf3nzhpk0flgakhzbbcl9-shellcheck-0.10.0-man", + "path": "/nix/store/qrhgwpj48flw028gcx9sqf5afd5xqspa-shellcheck-0.10.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/sj0g5c0kpff988drn383rx2mrxnqh4zf-shellcheck-0.10.0-doc", + "path": "/nix/store/hvi4b8pvirrbzxam001bzgswpayvfvsy-shellcheck-0.10.0-doc", "default": true }, { "name": "out", - "path": "/nix/store/rbdfq1nz2f66l0h2mzs1zbk3ipm7j2mx-shellcheck-0.10.0" + "path": "/nix/store/6znz4zkzds0zb7dvi6n85amdhypb95zc-shellcheck-0.10.0" } ], - "store_path": "/nix/store/gs33kdch6p8icbphmi4jd5hfjpv57hxs-shellcheck-0.10.0-bin" + "store_path": "/nix/store/ywmf20zyjivrhw97v7s1fi51y76fs5jr-shellcheck-0.10.0-bin" }, "x86_64-darwin": { "outputs": [ { "name": "bin", - "path": "/nix/store/232fynanrdfwvmdbw6d4qfx9cjvjpnnr-shellcheck-0.10.0-bin", + "path": "/nix/store/slzar2ld6c9js9ffvr4v3yplw80r8qf5-shellcheck-0.10.0-bin", "default": true }, { "name": "man", - "path": "/nix/store/rghkf2jn301p14dm63m6vp529pm643n4-shellcheck-0.10.0-man", + "path": "/nix/store/jgj5m5dsvihrvbr4iwrc59zga22vgpg9-shellcheck-0.10.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/2955bf7b30q8h22pfrfkp5s9qrz20qaa-shellcheck-0.10.0-doc", + "path": "/nix/store/brs51ypipb98038aiqnjh48daxvhzdpk-shellcheck-0.10.0-doc", "default": true }, { "name": "out", - "path": "/nix/store/nfdbib0knf1zc3k3hynrlrypvqq0ibnz-shellcheck-0.10.0" + "path": "/nix/store/j327y2hh2wzgfb7bjsfwnr74sxrq9nma-shellcheck-0.10.0" } ], - "store_path": "/nix/store/232fynanrdfwvmdbw6d4qfx9cjvjpnnr-shellcheck-0.10.0-bin" + "store_path": "/nix/store/slzar2ld6c9js9ffvr4v3yplw80r8qf5-shellcheck-0.10.0-bin" }, "x86_64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/6k8wlg7702w31r892b6wxzbzk8hqr4kb-shellcheck-0.10.0-bin", + "path": "/nix/store/w1l0q93lwkzf64d61005qfi2l56aj7p5-shellcheck-0.10.0-bin", "default": true }, { "name": "man", - "path": "/nix/store/r1p28g8nw8aivf82sxd0pp28r4xxiama-shellcheck-0.10.0-man", + "path": "/nix/store/1sh87f3lgw4b1rwcsnnvrnampn4x4lph-shellcheck-0.10.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/6cl4n0dg1lfa8x18izk30v5bpqhf79z8-shellcheck-0.10.0-doc", + "path": "/nix/store/1x55vy8qhagqbq5rxp9hr8h8ynvixhhy-shellcheck-0.10.0-doc", "default": true }, { "name": "out", - "path": "/nix/store/775kf44w5x6yg67rgcpdf6f6b8sj840a-shellcheck-0.10.0" + "path": "/nix/store/wpl60jck2cryl0y1id2dbjnp18qjjg90-shellcheck-0.10.0" } ], - "store_path": "/nix/store/6k8wlg7702w31r892b6wxzbzk8hqr4kb-shellcheck-0.10.0-bin" + "store_path": "/nix/store/w1l0q93lwkzf64d61005qfi2l56aj7p5-shellcheck-0.10.0-bin" } } }, "swagger-cli@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#swagger-cli", + "last_modified": "2024-08-10T11:55:14Z", + "resolved": "github:NixOS/nixpkgs/8987be1fef03440514ebf3b0b60e0c44fc13eb6c#swagger-cli", "source": "devbox-search", "version": "4.0.4", "systems": { @@ -1987,47 +1987,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/l97bajrw465kq0ylnkhwkf22myx5wqi2-swagger-cli-4.0.4", + "path": "/nix/store/31gdx5s967g0xnsa5x3jqfrrb2zjdzhi-swagger-cli-4.0.4", "default": true } ], - "store_path": "/nix/store/l97bajrw465kq0ylnkhwkf22myx5wqi2-swagger-cli-4.0.4" + "store_path": "/nix/store/31gdx5s967g0xnsa5x3jqfrrb2zjdzhi-swagger-cli-4.0.4" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/4m3kq0l9xfj8qxh8qfwijf5a95jx0jgw-swagger-cli-4.0.4", + "path": "/nix/store/7nyggm836y6zsqwswvrnr2065rz8a3gq-swagger-cli-4.0.4", "default": true } ], - "store_path": "/nix/store/4m3kq0l9xfj8qxh8qfwijf5a95jx0jgw-swagger-cli-4.0.4" + "store_path": "/nix/store/7nyggm836y6zsqwswvrnr2065rz8a3gq-swagger-cli-4.0.4" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/ff5wx2b7dswhhsqqkza6xv5npzh927x4-swagger-cli-4.0.4", + "path": "/nix/store/3kxl503ragrwi3f0nfi83g9sf4pbkh6s-swagger-cli-4.0.4", "default": true } ], - "store_path": "/nix/store/ff5wx2b7dswhhsqqkza6xv5npzh927x4-swagger-cli-4.0.4" + "store_path": "/nix/store/3kxl503ragrwi3f0nfi83g9sf4pbkh6s-swagger-cli-4.0.4" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/db6z4kinn17pj73gybhxjq25lm7mhmsy-swagger-cli-4.0.4", + "path": "/nix/store/cr7d8vhysr4n13fks7q4bmhjz9nglddc-swagger-cli-4.0.4", "default": true } ], - "store_path": "/nix/store/db6z4kinn17pj73gybhxjq25lm7mhmsy-swagger-cli-4.0.4" + "store_path": "/nix/store/cr7d8vhysr4n13fks7q4bmhjz9nglddc-swagger-cli-4.0.4" } } }, "temurin-bin-21@latest": { - "last_modified": "2024-09-13T05:52:00Z", - "resolved": "github:NixOS/nixpkgs/673d99f1406cb09b8eb6feab4743ebdf70046557#temurin-bin-21", + "last_modified": "2024-08-14T11:41:26Z", + "resolved": "github:NixOS/nixpkgs/0cb2fd7c59fed0cd82ef858cbcbdb552b9a33465#temurin-bin-21", "source": "devbox-search", "version": "21.0.3", "systems": { @@ -2035,47 +2035,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/l06d8w0l9h27pxzhygixd478dz0098z0-temurin-bin-21.0.3", + "path": "/nix/store/ji3g2rn3vq0hiagafmfd68sy1753sjla-temurin-bin-21.0.3", "default": true } ], - "store_path": "/nix/store/l06d8w0l9h27pxzhygixd478dz0098z0-temurin-bin-21.0.3" + "store_path": "/nix/store/ji3g2rn3vq0hiagafmfd68sy1753sjla-temurin-bin-21.0.3" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/fzkfqws93k92sw66cfp36d7y8mcmlr22-temurin-bin-21.0.3", + "path": "/nix/store/alax27n8mir195mkjy2cv5q9hlvhvv04-temurin-bin-21.0.3", "default": true } ], - "store_path": "/nix/store/fzkfqws93k92sw66cfp36d7y8mcmlr22-temurin-bin-21.0.3" + "store_path": "/nix/store/alax27n8mir195mkjy2cv5q9hlvhvv04-temurin-bin-21.0.3" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/wd6swl5iax4im490lip7n1f0wk0zii7s-temurin-bin-21.0.3", + "path": "/nix/store/mfj5d8wdk8mk42r886s1ayvm9j953xhl-temurin-bin-21.0.3", "default": true } ], - "store_path": "/nix/store/wd6swl5iax4im490lip7n1f0wk0zii7s-temurin-bin-21.0.3" + "store_path": "/nix/store/mfj5d8wdk8mk42r886s1ayvm9j953xhl-temurin-bin-21.0.3" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/phhr8nfj0c6wnh43g0b35z7awflkg39z-temurin-bin-21.0.3", + "path": "/nix/store/f8h1clx76p6na7fv9j6625fsxbdz05rw-temurin-bin-21.0.3", "default": true } ], - "store_path": "/nix/store/phhr8nfj0c6wnh43g0b35z7awflkg39z-temurin-bin-21.0.3" + "store_path": "/nix/store/f8h1clx76p6na7fv9j6625fsxbdz05rw-temurin-bin-21.0.3" } } }, "which@latest": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#which", + "last_modified": "2024-07-31T08:48:38Z", + "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#which", "source": "devbox-search", "version": "2.21", "systems": { @@ -2083,47 +2083,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/v4ajfj4wpgj153zwbzzv8q2cc1qq8ck6-which-2.21", + "path": "/nix/store/ghsv4i7adj4bys0a232c41amjl0n5a7n-which-2.21", "default": true } ], - "store_path": "/nix/store/v4ajfj4wpgj153zwbzzv8q2cc1qq8ck6-which-2.21" + "store_path": "/nix/store/ghsv4i7adj4bys0a232c41amjl0n5a7n-which-2.21" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/yqbky0d614vmyilw8ilkxi11dzdzh4vg-which-2.21", + "path": "/nix/store/1x0yih053iq1m4ckn62nnim366n50m3f-which-2.21", "default": true } ], - "store_path": "/nix/store/yqbky0d614vmyilw8ilkxi11dzdzh4vg-which-2.21" + "store_path": "/nix/store/1x0yih053iq1m4ckn62nnim366n50m3f-which-2.21" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/8yh1x2cj5f0pi6hf60ym9h0z0vq9yhnj-which-2.21", + "path": "/nix/store/wlmysyfiw68qgrqvbq5n0g5zm0cm915f-which-2.21", "default": true } ], - "store_path": "/nix/store/8yh1x2cj5f0pi6hf60ym9h0z0vq9yhnj-which-2.21" + "store_path": "/nix/store/wlmysyfiw68qgrqvbq5n0g5zm0cm915f-which-2.21" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/flaw4yljz1856ygjkkd344y81ykjbzr8-which-2.21", + "path": "/nix/store/w40ik54r0nv4fjqwiqs81ixpmdf2xdfw-which-2.21", "default": true } ], - "store_path": "/nix/store/flaw4yljz1856ygjkkd344y81ykjbzr8-which-2.21" + "store_path": "/nix/store/w40ik54r0nv4fjqwiqs81ixpmdf2xdfw-which-2.21" } } }, "yq-go@4.44.3": { - "last_modified": "2024-08-31T10:12:23Z", - "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#yq-go", + "last_modified": "2024-09-10T15:01:03Z", + "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#yq-go", "source": "devbox-search", "version": "4.44.3", "systems": { @@ -2131,41 +2131,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/jppigm7xxrz14z5qbp70qwk63rqkzlr7-yq-go-4.44.3", + "path": "/nix/store/l4yd5ml8bq3k7kgx09v11q735ifafc2z-yq-go-4.44.3", "default": true } ], - "store_path": "/nix/store/jppigm7xxrz14z5qbp70qwk63rqkzlr7-yq-go-4.44.3" + "store_path": "/nix/store/l4yd5ml8bq3k7kgx09v11q735ifafc2z-yq-go-4.44.3" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/h1p18ha0hkgf0rwc87cc51nz2m69004s-yq-go-4.44.3", + "path": "/nix/store/v560a3n62j1vxmcajhmjm0m3g706gj1r-yq-go-4.44.3", "default": true } ], - "store_path": "/nix/store/h1p18ha0hkgf0rwc87cc51nz2m69004s-yq-go-4.44.3" + "store_path": "/nix/store/v560a3n62j1vxmcajhmjm0m3g706gj1r-yq-go-4.44.3" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/rfymy667lx1032cxdwyzslxia511dxz7-yq-go-4.44.3", + "path": "/nix/store/51y79dql9azy10mr82dbvp30y29lp3pn-yq-go-4.44.3", "default": true } ], - "store_path": "/nix/store/rfymy667lx1032cxdwyzslxia511dxz7-yq-go-4.44.3" + "store_path": "/nix/store/51y79dql9azy10mr82dbvp30y29lp3pn-yq-go-4.44.3" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/kb2z2ih2yvlpy9p4mx4sqdicjj4p2isw-yq-go-4.44.3", + "path": "/nix/store/3c1gvwg7nkvwvidnvng8fj31pprkhhva-yq-go-4.44.3", "default": true } ], - "store_path": "/nix/store/kb2z2ih2yvlpy9p4mx4sqdicjj4p2isw-yq-go-4.44.3" + "store_path": "/nix/store/3c1gvwg7nkvwvidnvng8fj31pprkhhva-yq-go-4.44.3" } } } From f0bb44a57c3b023aafc9bf9fdccf0e972e3e3be2 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 10 Oct 2024 12:00:13 +0200 Subject: [PATCH 14/39] Remove API credential routes and associated middleware from public API server --- src/autoscaler/api/publicapiserver/public_api_server.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/autoscaler/api/publicapiserver/public_api_server.go b/src/autoscaler/api/publicapiserver/public_api_server.go index 74e52fd730..62f9f8bbdf 100644 --- a/src/autoscaler/api/publicapiserver/public_api_server.go +++ b/src/autoscaler/api/publicapiserver/public_api_server.go @@ -92,13 +92,6 @@ func (s *PublicApiServer) GetMtlsServer() (ifrit.Runner, error) { rpolicy.Get(routes.PublicApiAttachPolicyRouteName).Handler(VarsFunc(pah.AttachScalingPolicy)) rpolicy.Get(routes.PublicApiDetachPolicyRouteName).Handler(VarsFunc(pah.DetachScalingPolicy)) - rcredential := routes.ApiCredentialRoutes() - rcredential.Use(rateLimiterMiddleware.CheckRateLimit) - - rcredential.Use(httpStatusCollectMiddleware.Collect) - rcredential.Use(mw.HasClientToken) - rcredential.Use(mw.Oauth) - healthRouter, err := createHealthRouter(s.logger, s.conf, s.policyDB, s.bindingDB, s.httpStatusCollector) if err != nil { return nil, fmt.Errorf("failed to create health router: %w", err) From f38547525453c1e9d744e833fe5b87185b34ee11 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 10 Oct 2024 12:00:49 +0200 Subject: [PATCH 15/39] Remove unused net/http import and apiHttpClient from api_suite_test in autoscaler API --- src/autoscaler/api/cmd/api/api_suite_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/autoscaler/api/cmd/api/api_suite_test.go b/src/autoscaler/api/cmd/api/api_suite_test.go index 822ef04f0c..29f85c0834 100644 --- a/src/autoscaler/api/cmd/api/api_suite_test.go +++ b/src/autoscaler/api/cmd/api/api_suite_test.go @@ -3,7 +3,6 @@ package main_test import ( "database/sql" "encoding/json" - "net/http" "os" "os/exec" "path/filepath" @@ -39,7 +38,6 @@ var ( apPath string cfg config.Config configFile *os.File - apiHttpClient *http.Client schedulerServer *ghttp.Server catalogBytes string brokerPort int From 817c1de875efff334b81b118f487887cb1db9d54 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 10 Oct 2024 12:03:29 +0200 Subject: [PATCH 16/39] Fix lint --- src/autoscaler/api/cmd/api/api_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/autoscaler/api/cmd/api/api_test.go b/src/autoscaler/api/cmd/api/api_test.go index 467422ad88..cced2671b1 100644 --- a/src/autoscaler/api/cmd/api/api_test.go +++ b/src/autoscaler/api/cmd/api/api_test.go @@ -7,12 +7,11 @@ import ( "net/url" "os" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" - . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" + . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -41,7 +40,7 @@ var _ = Describe("Api", func() { brokerHttpClient = NewServiceBrokerClient() healthHttpClient = &http.Client{} - apiHttpClient = testhelpers.NewPublicApiClient() + apiHttpClient = NewPublicApiClient() serverURL, err = url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfg.PublicApiServer.Port)) Expect(err).NotTo(HaveOccurred()) From c5f813810b4d746860d36bf27914ef101c9e9389 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 10 Oct 2024 14:39:02 +0200 Subject: [PATCH 17/39] Small fix --- src/autoscaler/api/cmd/api/api_suite_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/autoscaler/api/cmd/api/api_suite_test.go b/src/autoscaler/api/cmd/api/api_suite_test.go index 29f85c0834..2cc648c415 100644 --- a/src/autoscaler/api/cmd/api/api_suite_test.go +++ b/src/autoscaler/api/cmd/api/api_suite_test.go @@ -10,7 +10,6 @@ import ( "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cf/mocks" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" @@ -22,6 +21,8 @@ import ( "github.com/onsi/gomega/gexec" "gopkg.in/yaml.v3" + . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -60,7 +61,7 @@ type testdata struct { var _ = SynchronizedBeforeSuite(func() []byte { info := testdata{} - dbUrl := testhelpers.GetDbUrl() + dbUrl := GetDbUrl() database, e := db.GetConnection(dbUrl) if e != nil { @@ -131,7 +132,7 @@ var _ = SynchronizedBeforeSuite(func() []byte { } cfg.Logging.Level = "info" cfg.DB = make(map[string]db.DatabaseConfig) - dbUrl := testhelpers.GetDbUrl() + dbUrl := GetDbUrl() cfg.DB[db.BindingDb] = db.DatabaseConfig{ URL: dbUrl, MaxOpenConnections: 10, @@ -276,6 +277,6 @@ func (ap *ApiRunner) Interrupt() { func readFile(filename string) string { contents, err := os.ReadFile(filename) - testhelpers.FailOnError("Failed to read file:"+filename+" ", err) + FailOnError("Failed to read file:"+filename+" ", err) return string(contents) } From 4b10025779bac6cbe2f86e8385ac69fb710c1bce Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 10 Oct 2024 16:08:19 +0200 Subject: [PATCH 18/39] Remove Health Port configuration from metricsforwarder tests --- src/autoscaler/metricsforwarder/config/config_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/autoscaler/metricsforwarder/config/config_test.go b/src/autoscaler/metricsforwarder/config/config_test.go index f61b8d9d82..803473cf4c 100644 --- a/src/autoscaler/metricsforwarder/config/config_test.go +++ b/src/autoscaler/metricsforwarder/config/config_test.go @@ -250,7 +250,6 @@ cred_helper_impl: default It("returns the config", func() { Expect(conf.Server.Port).To(Equal(8081)) Expect(conf.Logging.Level).To(Equal("debug")) - Expect(conf.Health.Port).To(Equal(9999)) Expect(conf.LoggregatorConfig.MetronAddress).To(Equal("127.0.0.1:3457")) Expect(conf.Db[db.PolicyDb]).To(Equal( db.DatabaseConfig{ @@ -289,7 +288,6 @@ health: Expect(conf.LoggregatorConfig.MetronAddress).To(Equal(DefaultMetronAddress)) Expect(conf.CacheTTL).To(Equal(DefaultCacheTTL)) Expect(conf.CacheCleanupInterval).To(Equal(DefaultCacheCleanupInterval)) - Expect(conf.Health.Port).To(Equal(8081)) }) }) @@ -302,7 +300,6 @@ health: conf = &Config{} conf.Server.Port = 8081 conf.Logging.Level = "debug" - conf.Health.Port = 8081 conf.LoggregatorConfig.MetronAddress = "127.0.0.1:3458" conf.LoggregatorConfig.TLS.CACertFile = "../testcerts/ca.crt" conf.LoggregatorConfig.TLS.CertFile = "../testcerts/client.crt" From b8bdaedb800ae73cc1281eef2fd0e2f642334cd0 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 10 Oct 2024 16:10:37 +0200 Subject: [PATCH 19/39] Fix lint --- src/autoscaler/api/publicapiserver/scaling_history_handler.go | 1 - src/autoscaler/eventgenerator/server/server.go | 2 -- src/autoscaler/helpers/auth/xfcc_auth.go | 2 -- 3 files changed, 5 deletions(-) diff --git a/src/autoscaler/api/publicapiserver/scaling_history_handler.go b/src/autoscaler/api/publicapiserver/scaling_history_handler.go index 85be1d557b..3ba6d60e86 100644 --- a/src/autoscaler/api/publicapiserver/scaling_history_handler.go +++ b/src/autoscaler/api/publicapiserver/scaling_history_handler.go @@ -39,7 +39,6 @@ type ScalingHistoryHandler struct { } func NewScalingHistoryHandler(logger lager.Logger, conf *config.Config) (*ScalingHistoryHandler, error) { - newHandler := &ScalingHistoryHandler{ logger: logger.Session("scaling-history-handler"), conf: conf, diff --git a/src/autoscaler/eventgenerator/server/server.go b/src/autoscaler/eventgenerator/server/server.go index 60144e49ea..4bd5873345 100644 --- a/src/autoscaler/eventgenerator/server/server.go +++ b/src/autoscaler/eventgenerator/server/server.go @@ -46,7 +46,6 @@ type Server struct { } func (s *Server) GetMtlsServer() (ifrit.Runner, error) { - eventGeneratorRouter, err := createEventGeneratorRouter(s.logger, s.queryAppMetric, s.httpStatusCollector, s.conf.Server) if err != nil { return nil, fmt.Errorf("failed to create event generator router: %w", err) @@ -74,7 +73,6 @@ func serverConfigFrom(conf *config.Config) helpers.ServerConfig { } func (s *Server) GetHealthServer() (ifrit.Runner, error) { - healthRouter, err := createHealthRouter(s.appMetricDB, s.policyDb, s.logger, s.conf, s.httpStatusCollector) if err != nil { return nil, fmt.Errorf("failed to create health router: %w", err) diff --git a/src/autoscaler/helpers/auth/xfcc_auth.go b/src/autoscaler/helpers/auth/xfcc_auth.go index 2728cb7b5d..7f9857278a 100644 --- a/src/autoscaler/helpers/auth/xfcc_auth.go +++ b/src/autoscaler/helpers/auth/xfcc_auth.go @@ -47,7 +47,6 @@ func (m *XFCCAuthMiddleware) checkAuth(r *http.Request) error { } return nil - } func (m *XFCCAuthMiddleware) XFCCAuthenticationMiddleware(next http.Handler) http.Handler { @@ -75,7 +74,6 @@ func NewXfccAuthMiddleware(logger lager.Logger, orgGuid, spaceGuid string) *XFCC func getSpaceGuid(cert *x509.Certificate) string { var certSpaceGuid string for _, ou := range cert.Subject.OrganizationalUnit { - if strings.Contains(ou, "space:") { kv := mapFrom(ou) certSpaceGuid = kv["space"] From 288606f10ff44bf9987157dfe8931bb097b603ba Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 10 Oct 2024 16:47:56 +0200 Subject: [PATCH 20/39] Comment out setupMainRouter functions in eventgenerator and scalingengine servers --- src/autoscaler/eventgenerator/server/server.go | 14 +++++++------- src/autoscaler/scalingengine/server/server.go | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/autoscaler/eventgenerator/server/server.go b/src/autoscaler/eventgenerator/server/server.go index 4bd5873345..f47f6c4f99 100644 --- a/src/autoscaler/eventgenerator/server/server.go +++ b/src/autoscaler/eventgenerator/server/server.go @@ -101,10 +101,10 @@ func CreatePrometheusRegistry(appMetricDB db.AppMetricDB, policyDb db.PolicyDB, return promRegistry } -func setupMainRouter(egRouter, healthRouter *mux.Router) *mux.Router { - mainRouter := mux.NewRouter() - mainRouter.PathPrefix("/v1").Handler(egRouter) - //mainRouter.PathPrefix("/health").Handler(healthRouter) - //mainRouter.PathPrefix("/").Handler(healthRouter) - return mainRouter -} +// func setupMainRouter(egRouter, healthRouter *mux.Router) *mux.Router { +// mainRouter := mux.NewRouter() +// mainRouter.PathPrefix("/v1").Handler(egRouter) +// //mainRouter.PathPrefix("/health").Handler(healthRouter) +// //mainRouter.PathPrefix("/").Handler(healthRouter) +// return mainRouter +// } diff --git a/src/autoscaler/scalingengine/server/server.go b/src/autoscaler/scalingengine/server/server.go index a6325e8667..d14f2171db 100644 --- a/src/autoscaler/scalingengine/server/server.go +++ b/src/autoscaler/scalingengine/server/server.go @@ -127,13 +127,13 @@ func createScalingEngineRouter(logger lager.Logger, scalingEngineDB db.ScalingEn return r, nil } -func setupMainRouter(r *mux.Router, healthRouter *mux.Router) *mux.Router { - mainRouter := mux.NewRouter() - mainRouter.PathPrefix("/v1").Handler(r) - mainRouter.PathPrefix("/health").Handler(healthRouter) - mainRouter.PathPrefix("/").Handler(healthRouter) - return mainRouter -} +// func setupMainRouter(r *mux.Router, healthRouter *mux.Router) *mux.Router { +// mainRouter := mux.NewRouter() +// mainRouter.PathPrefix("/v1").Handler(r) +// mainRouter.PathPrefix("/health").Handler(healthRouter) +// mainRouter.PathPrefix("/").Handler(healthRouter) +// return mainRouter +// } func newScalingHistoryHandler(logger lager.Logger, scalingEngineDB db.ScalingEngineDB) (http.Handler, error) { scalingHistoryHandler, err := NewScalingHistoryHandler(logger, scalingEngineDB) From 167ec9c74ae9a14e0c83bc34df54fe58b0d483a2 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 11 Oct 2024 14:00:18 +0200 Subject: [PATCH 21/39] Fix test Improve error logging in metric_poller_test and update health port configuration structure configuration in both config_test.go and exampleconfig/config.yml. --- .../eventgenerator/aggregator/metric_poller_test.go | 3 ++- src/autoscaler/metricsforwarder/config/config_test.go | 6 ++++-- src/autoscaler/metricsforwarder/exampleconfig/config.yml | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/autoscaler/eventgenerator/aggregator/metric_poller_test.go b/src/autoscaler/eventgenerator/aggregator/metric_poller_test.go index 0becab0103..6d9229af76 100644 --- a/src/autoscaler/eventgenerator/aggregator/metric_poller_test.go +++ b/src/autoscaler/eventgenerator/aggregator/metric_poller_test.go @@ -70,7 +70,8 @@ var _ = Describe("MetricPoller", func() { It("logs an error", func() { //TODO this should be a prometheus counter not a log statement check - Eventually(logger.Buffer).Should(Say("retrieveMetric Failed")) + Eventually(logger.Buffer, 2*time.Second).Should(Say("retrieveMetric Failed")) + }) It("does not save any metrics", func() { diff --git a/src/autoscaler/metricsforwarder/config/config_test.go b/src/autoscaler/metricsforwarder/config/config_test.go index 803473cf4c..7d3797a092 100644 --- a/src/autoscaler/metricsforwarder/config/config_test.go +++ b/src/autoscaler/metricsforwarder/config/config_test.go @@ -242,7 +242,8 @@ db: max_idle_connections: 5 connection_max_lifetime: 60s health: - port: 9999 + server_config: + port: 9999 cred_helper_impl: default `) }) @@ -277,7 +278,8 @@ db: max_idle_connections: 5 connection_max_lifetime: 60s health: - port: 8081 + server_config: + port: 8081 `) }) diff --git a/src/autoscaler/metricsforwarder/exampleconfig/config.yml b/src/autoscaler/metricsforwarder/exampleconfig/config.yml index 5549af3251..6ae2ee4b6f 100644 --- a/src/autoscaler/metricsforwarder/exampleconfig/config.yml +++ b/src/autoscaler/metricsforwarder/exampleconfig/config.yml @@ -15,7 +15,8 @@ db: max_idle_connections: 5 connection_max_lifetime: 60s health: - port: 8081 + server_config: + port: 8081 rate_limit: max_amount: 10 valid_duration: 1s From f5eb30112c149885c683cd7d6ff8f79497705759 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 10:55:08 +0200 Subject: [PATCH 22/39] Add new API paths and relocate scaling history API in golangapiserver and scalingengine packages --- packages/golangapiserver/spec | 4 +++- packages/scalingengine/spec | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/golangapiserver/spec b/packages/golangapiserver/spec index 4b88477766..db12046d87 100644 --- a/packages/golangapiserver/spec +++ b/packages/golangapiserver/spec @@ -11,6 +11,8 @@ files: - autoscaler/api/policyvalidator/* - autoscaler/api/schemas/* - autoscaler/api/* # gosub +- autoscaler/api/apis/* # gosub +- autoscaler/api/apis/scalinghistory/* # gosub - autoscaler/api/broker/* # gosub - autoscaler/api/brokerserver/* # gosub - autoscaler/api/cmd/api/* # gosub @@ -26,12 +28,12 @@ files: - autoscaler/db/sqldb/* # gosub - autoscaler/healthendpoint/* # gosub - autoscaler/helpers/* # gosub -- autoscaler/helpers/apis/scalinghistory/* # gosub - autoscaler/helpers/handlers/* # gosub - autoscaler/metricsforwarder/server/common/* # gosub - autoscaler/models/* # gosub - autoscaler/ratelimiter/* # gosub - autoscaler/routes/* # gosub +- autoscaler/scalingengine/apis/scalinghistory/* # gosub - autoscaler/vendor/code.cloudfoundry.org/cfhttp/v2/* # gosub - autoscaler/vendor/code.cloudfoundry.org/clock/* # gosub - autoscaler/vendor/code.cloudfoundry.org/lager/v3/* # gosub diff --git a/packages/scalingengine/spec b/packages/scalingengine/spec index 3769e5739a..aeff80f066 100644 --- a/packages/scalingengine/spec +++ b/packages/scalingengine/spec @@ -14,12 +14,14 @@ files: - autoscaler/db/sqldb/* # gosub - autoscaler/healthendpoint/* # gosub - autoscaler/helpers/* # gosub -- autoscaler/helpers/apis/scalinghistory/* # gosub - autoscaler/helpers/handlers/* # gosub - autoscaler/metricsforwarder/server/common/* # gosub - autoscaler/models/* # gosub - autoscaler/routes/* # gosub - autoscaler/scalingengine/* # gosub +- autoscaler/scalingengine/apis/* # gosub +- autoscaler/scalingengine/apis/scalinghistory/* # gosub +- autoscaler/scalingengine/client/* # gosub - autoscaler/scalingengine/cmd/scalingengine/* # gosub - autoscaler/scalingengine/config/* # gosub - autoscaler/scalingengine/schedule/* # gosub From fc05d9ba64c5391b13b77945a1d1b325f79bb339 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 11:21:28 +0200 Subject: [PATCH 23/39] Refactor deployment script to use interpolated secrets from a template - Add a new template for autoscaler secrets (autoscaler-secrets.yml.tpl) - Update deploy-autoscaler.sh to interpolate secrets from the new template instead of fetching individually with credhub --- ci/autoscaler/scripts/autoscaler-secrets.yml.tpl | 13 +++++++++++++ ci/autoscaler/scripts/deploy-autoscaler.sh | 16 ++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 ci/autoscaler/scripts/autoscaler-secrets.yml.tpl diff --git a/ci/autoscaler/scripts/autoscaler-secrets.yml.tpl b/ci/autoscaler/scripts/autoscaler-secrets.yml.tpl new file mode 100644 index 0000000000..2c25984c38 --- /dev/null +++ b/ci/autoscaler/scripts/autoscaler-secrets.yml.tpl @@ -0,0 +1,13 @@ +--- +admin_password: ((/bosh-autoscaler/cf/cf_admin_password)) +routing_api_tls_client_cert: ((/bosh-autoscaler/cf/routing_api_tls_client.certificate)) +routing_api_ca_certs: ((/bosh-autoscaler/cf/router_ssl.ca)) +routing_api_client_secret: ((/bosh-autoscaler/cf/uaa_clients_routing_api_client_secret)) +routing_api_tls_client_private_key: ((/bosh-autoscaler/cf/routing_api_tls_client.private_key)) +routing_api_server_ca_cert: ((/bosh-autoscaler/cf/router_ssl.ca)) +log_cache_syslog_tls_ca: ((/bosh-autoscaler/cf/log_cache_syslog_tls.ca)) +syslog_agent_log_cache_tls_certificate: ((/bosh-autoscaler/cf/syslog_agent_log_cache_tls.certificate)) +syslog_agent_log_cache_tls_key: ((/bosh-autoscaler/cf/syslog_agent_log_cache_tls.private_key)) +metricscollector_ca_cert: ((/bosh-autoscaler/cf/log_cache.ca)) +metricscollector_client_cert: ((/bosh-autoscaler/cf/log_cache.certificate)) +metricscollector_client_key: ((/bosh-autoscaler/cf/log_cache.private_key)) diff --git a/ci/autoscaler/scripts/deploy-autoscaler.sh b/ci/autoscaler/scripts/deploy-autoscaler.sh index 7cdbc0c6c8..da16053d2e 100755 --- a/ci/autoscaler/scripts/deploy-autoscaler.sh +++ b/ci/autoscaler/scripts/deploy-autoscaler.sh @@ -89,6 +89,9 @@ function create_manifest(){ # on MacOS mktemp does not know the --tmpdir option tmp_manifest_file="$(mktemp "${tmp_dir}/${deployment_name}.bosh-manifest.yaml.XXX")" + + credhub interpolate -f "${autoscaler_dir}/ci/autoscaler/scripts/autoscaler-secrets.yml.tpl" > /tmp/autoscaler-secrets.yml + bosh -n -d "${deployment_name}" \ interpolate "${deployment_manifest}" \ ${OPS_FILES_TO_USE} \ @@ -96,22 +99,11 @@ function create_manifest(){ -v system_domain="${system_domain}" \ -v deployment_name="${deployment_name}" \ -v app_autoscaler_version="${bosh_release_version}" \ - -v admin_password="$(credhub get -n /bosh-autoscaler/cf/cf_admin_password -q)"\ - -v routing_api_ca_certs="$(credhub get -n /bosh-autoscaler/cf/router_ssl --key ca --quiet)"\ - -v routing_api_client_secret="$(credhub get -n /bosh-autoscaler/cf/uaa_clients_routing_api_client_secret --quiet)"\ - -v routing_api_tls_client_cert="$(credhub get -n /bosh-autoscaler/cf/routing_api_tls_client --key certificate --quiet)"\ - -v routing_api_tls_client_private_key="$(credhub get -n /bosh-autoscaler/cf/routing_api_tls_client --key private_key --quiet)"\ - -v routing_api_server_ca_cert="$(credhub get -n /bosh-autoscaler/cf/router_ssl --key ca --quiet)"\ -v cf_client_id=autoscaler_client_id \ -v cf_client_secret=autoscaler_client_secret \ - -v log_cache_syslog_tls_ca="$(credhub get -n /bosh-autoscaler/cf/log_cache_syslog_tls --key ca --quiet)"\ - -v syslog_agent_log_cache_tls_certificate="$(credhub get -n /bosh-autoscaler/cf/syslog_agent_log_cache_tls --key certificate --quiet)"\ - -v syslog_agent_log_cache_tls_key="$(credhub get -n /bosh-autoscaler/cf/syslog_agent_log_cache_tls --key private_key --quiet)"\ - -v metricscollector_ca_cert="$(credhub get -n /bosh-autoscaler/cf/log_cache --key ca --quiet)"\ - -v metricscollector_client_cert="$(credhub get -n /bosh-autoscaler/cf/log_cache --key certificate --quiet)"\ -v metricsforwarder_host="${metricsforwarder_host}"\ -v postgres_external_port="$(get_postgres_external_port)"\ - -v metricscollector_client_key="$(credhub get -n /bosh-autoscaler/cf/log_cache --key private_key --quiet)"\ + --vars-file=/tmp/autoscaler-secrets.yml \ -v skip_ssl_validation=true \ > "${tmp_manifest_file}" From 991291dee2bdcc277d38d0fa77fdb8b5fa471c50 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 12:04:42 +0200 Subject: [PATCH 24/39] Update klauspost/compress module from v1.17.10 to v1.17.11 in autoscaler component --- src/autoscaler/go.mod | 2 +- src/autoscaler/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/autoscaler/go.mod b/src/autoscaler/go.mod index 6f1c97664a..c6335405b2 100644 --- a/src/autoscaler/go.mod +++ b/src/autoscaler/go.mod @@ -77,7 +77,7 @@ require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/klauspost/compress v1.17.10 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect diff --git a/src/autoscaler/go.sum b/src/autoscaler/go.sum index 077891ae62..748caff466 100644 --- a/src/autoscaler/go.sum +++ b/src/autoscaler/go.sum @@ -651,8 +651,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0= -github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= From 51dc5ef70320b5d615c129b2ff1d7a221532db47 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 12:08:38 +0200 Subject: [PATCH 25/39] Add use-cf-services ops file to deploy-autoscaler script --- ci/autoscaler/scripts/deploy-autoscaler.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/autoscaler/scripts/deploy-autoscaler.sh b/ci/autoscaler/scripts/deploy-autoscaler.sh index da16053d2e..2019563b86 100755 --- a/ci/autoscaler/scripts/deploy-autoscaler.sh +++ b/ci/autoscaler/scripts/deploy-autoscaler.sh @@ -17,8 +17,8 @@ ops_files=${OPS_FILES:-"${autoscaler_dir}/operations/add-releases.yml\ ${autoscaler_dir}/operations/add-extra-plan.yml\ ${autoscaler_dir}/operations/set-release-version.yml\ ${autoscaler_dir}/operations/enable-metricsforwarder-via-syslog-agent.yml\ - ${autoscaler_dir}/operations/enable-scheduler-logging.yml"} - + ${autoscaler_dir}/operations/enable-scheduler-logging.yml\ + ${autoscaler_dir}/operations/use-cf-services.yml"} case "${cpu_upper_threshold}" in From 005450b7d38ad29b67287cc26faca43f05936ef3 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 14:19:01 +0200 Subject: [PATCH 26/39] does not run the generate of apis on binary build --- src/autoscaler/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autoscaler/Makefile b/src/autoscaler/Makefile index 02362488b2..3c2194810b 100644 --- a/src/autoscaler/Makefile +++ b/src/autoscaler/Makefile @@ -42,7 +42,6 @@ openapi-specs-list = $(wildcard ${openapi-spec-path}/*.yaml) openapi-generated-clients-and-servers-api-files = $(wildcard ${openapi-generated-clients-and-servers-api-dir}/*.go) openapi-generated-clients-and-servers-scalingengine-files = $(wildcard ${openapi-generated-clients-and-servers-scalingengine-dir}/*.go) - .PHONY: generate-openapi-generated-clients-and-servers generate-openapi-generated-clients-and-servers: ${openapi-generated-clients-and-servers-api-dir} ${openapi-generated-clients-and-servers-api-files} ${openapi-generated-clients-and-servers-scalingengine-dir} ${openapi-generated-clients-and-servers-scalingengine-files} ${openapi-generated-clients-and-servers-api-dir} ${openapi-generated-clients-and-servers-api-files} ${openapi-generated-clients-and-servers-scalingengine-dir} ${openapi-generated-clients-and-servers-scalingengine-files} &: $(wildcard ./scalingengine/apis/generate.go) $(wildcard ./api/apis/generate.go) ${openapi-specs-list} ./go.mod ./go.sum @@ -97,8 +96,9 @@ go-mod-vendor: ${go-vendoring-folder} ${go-vendored-files} ${go-vendoring-folder} ${go-vendored-files} &: ${app-fakes-dir} ${app-fakes-files} go mod vendor + # CGO_ENABLED := 1 is required to enforce dynamic linking which is a requirement of dynatrace. -build-%: ${openapi-generated-clients-and-servers-api-dir} ${openapi-generated-clients-and-servers-api-files} ${openapi-generated-clients-and-servers-scalingengine-dir} ${openapi-generated-clients-and-servers-scalingengine-files} +build-%: @echo "# building $*" @CGO_ENABLED=1 go build $(BUILDTAGS) $(BUILDFLAGS) -o build/$* $*/cmd/$*/main.go From 26f6c7a25be1799752b282ba1d63d223cb88dcb9 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 14:28:31 +0200 Subject: [PATCH 27/39] Generates openapi on build-all task --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 61598ce5c0..abb06d282c 100644 --- a/Makefile +++ b/Makefile @@ -94,7 +94,7 @@ clean-acceptance: build: $(all_modules) build-tests: build-test build-test: $(addprefix test_,$(go_modules)) -build-all: build build-test build-test-app mta-build ## Build all modules and tests +build-all: generate-openapi-generated-clients-and-servers build build-test build-test-app mta-build ## Build all modules and tests db: target/db target/db: @echo "# building $@" From 0c8153258746bb7788a134bd5f877991b3d23f47 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 14:29:05 +0200 Subject: [PATCH 28/39] Comment out Prometheus alert silencing commands in deploy-autoscaler.sh --- ci/autoscaler/scripts/deploy-autoscaler.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/autoscaler/scripts/deploy-autoscaler.sh b/ci/autoscaler/scripts/deploy-autoscaler.sh index 2019563b86..def1900957 100755 --- a/ci/autoscaler/scripts/deploy-autoscaler.sh +++ b/ci/autoscaler/scripts/deploy-autoscaler.sh @@ -126,9 +126,9 @@ function check_ops_files(){ function deploy() { # Try to silence Prometheus but do not fail deployment if there's an error - ${script_dir}/silence_prometheus_alert.sh "BOSHJobEphemeralDiskPredictWillFill" || true - ${script_dir}/silence_prometheus_alert.sh "BOSHJobProcessUnhealthy" || true - ${script_dir}/silence_prometheus_alert.sh "BOSHJobUnhealthy" || true +# ${script_dir}/silence_prometheus_alert.sh "BOSHJobEphemeralDiskPredictWillFill" || true +# ${script_dir}/silence_prometheus_alert.sh "BOSHJobProcessUnhealthy" || true +# ${script_dir}/silence_prometheus_alert.sh "BOSHJobUnhealthy" || true create_manifest From 8034c1a4ee6845f0fa776a87f39ea2ae2cfb48db Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 15:09:19 +0200 Subject: [PATCH 29/39] Refactor health endpoint configuration across multiple jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Consolidate health endpoint configuration into server_config and basic_auth sections for eventgenerator, golangapiserver, metricsforwarder, operator, and scalingengine jobs. • Enable TLS configuration within the server_config section when certificates are provided. • Update corresponding specs to reflect the new configuration structure and verify TLS settings and basic auth credentials. --- .../templates/eventgenerator.yml.erb | 20 ++++--- .../templates/apiserver.yml.erb | 15 ++--- .../templates/metricsforwarder.yml.erb | 20 ++++--- jobs/operator/templates/operator.yml.erb | 20 ++++--- .../templates/scalingengine.yml.erb | 20 ++++--- spec/jobs/common/health_endpoint_spec.rb | 58 +++++++++---------- .../eventgenerator/eventgenerator_spec.rb | 14 ++--- .../metricsforwarder/metricsforwarder_spec.rb | 14 ++--- spec/jobs/operator/operator_spec.rb | 14 ++--- spec/jobs/scalingengine/scalingengine_spec.rb | 14 ++--- src/autoscaler/build-extension-file.sh | 7 ++- src/autoscaler/mta.tpl.yaml | 31 +++++++++- 12 files changed, 129 insertions(+), 118 deletions(-) diff --git a/jobs/eventgenerator/templates/eventgenerator.yml.erb b/jobs/eventgenerator/templates/eventgenerator.yml.erb index 2b302a7115..46bcae0ef5 100644 --- a/jobs/eventgenerator/templates/eventgenerator.yml.erb +++ b/jobs/eventgenerator/templates/eventgenerator.yml.erb @@ -69,15 +69,17 @@ logging: level: <%= p("autoscaler.eventgenerator.logging.level") %> http_client_timeout: <%= p("autoscaler.eventgenerator.http_client_timeout") %> health: - port: <%= p("autoscaler.eventgenerator.health.port") %> - username: <%= p("autoscaler.eventgenerator.health.username") %> - password: <%= p("autoscaler.eventgenerator.health.password") %> - <% if_p("autoscaler.eventgenerator.health.ca_cert", "autoscaler.eventgenerator.health.server_cert", "autoscaler.eventgenerator.health.server_key") do %> - tls: - ca_file: /var/vcap/jobs/eventgenerator/config/certs/healthendpoint/ca.crt - cert_file: /var/vcap/jobs/eventgenerator/config/certs/healthendpoint/server.crt - key_file: /var/vcap/jobs/eventgenerator/config/certs/healthendpoint/server.key - <% end %> + server_config: + port: <%= p("autoscaler.eventgenerator.health.port") %> + <% if_p("autoscaler.eventgenerator.health.ca_cert", "autoscaler.eventgenerator.health.server_cert", "autoscaler.eventgenerator.health.server_key") do %> + tls: + ca_file: /var/vcap/jobs/eventgenerator/config/certs/healthendpoint/ca.crt + cert_file: /var/vcap/jobs/eventgenerator/config/certs/healthendpoint/server.crt + key_file: /var/vcap/jobs/eventgenerator/config/certs/healthendpoint/server.key + <% end %> + basic_auth: + username: <%= p("autoscaler.eventgenerator.health.username") %> + password: <%= p("autoscaler.eventgenerator.health.password") %> db: policy_db: diff --git a/jobs/golangapiserver/templates/apiserver.yml.erb b/jobs/golangapiserver/templates/apiserver.yml.erb index ece70ac31d..629bb34b15 100644 --- a/jobs/golangapiserver/templates/apiserver.yml.erb +++ b/jobs/golangapiserver/templates/apiserver.yml.erb @@ -91,13 +91,14 @@ dashboard_redirect_uri: <%= p("autoscaler.apiserver.broker.server.dashboard_redi default_credential_type: <%= p("autoscaler.apiserver.broker.default_credential_type") %> health: - port: <%= p("autoscaler.apiserver.health.port") %> - <% if_p("autoscaler.apiserver.health.ca_cert", "autoscaler.apiserver.health.server_cert", "autoscaler.apiserver.health.server_key") do %> - tls: - ca_file: /var/vcap/jobs/golangapiserver/config/certs/healthendpoint/ca.crt - cert_file: /var/vcap/jobs/golangapiserver/config/certs/healthendpoint/server.crt - key_file: /var/vcap/jobs/golangapiserver/config/certs/healthendpoint/server.key - <% end %> + server_config: + port: <%= p("autoscaler.apiserver.health.port") %> + <% if_p("autoscaler.apiserver.health.ca_cert", "autoscaler.apiserver.health.server_cert", "autoscaler.apiserver.health.server_key") do %> + tls: + ca_file: /var/vcap/jobs/golangapiserver/config/certs/healthendpoint/ca.crt + cert_file: /var/vcap/jobs/golangapiserver/config/certs/healthendpoint/server.crt + key_file: /var/vcap/jobs/golangapiserver/config/certs/healthendpoint/server.key + <% end %> db: policy_db: diff --git a/jobs/metricsforwarder/templates/metricsforwarder.yml.erb b/jobs/metricsforwarder/templates/metricsforwarder.yml.erb index e518676955..e6713c8643 100644 --- a/jobs/metricsforwarder/templates/metricsforwarder.yml.erb +++ b/jobs/metricsforwarder/templates/metricsforwarder.yml.erb @@ -91,15 +91,17 @@ cache_ttl: <%= p("autoscaler.metricsforwarder.cache_ttl") %> cache_cleanup_interval: <%= p("autoscaler.metricsforwarder.cache_cleanup_interval") %> policy_poller_interval: <%= p("autoscaler.metricsforwarder.policy_poller_interval") %> health: - port: <%= p("autoscaler.metricsforwarder.health.port") %> - username: <%= p("autoscaler.metricsforwarder.health.username") %> - password: <%= p("autoscaler.metricsforwarder.health.password") %> - <% if_p("autoscaler.metricsforwarder.health.ca_cert", "autoscaler.metricsforwarder.health.server_cert", "autoscaler.metricsforwarder.health.server_key") do %> - tls: - ca_file: /var/vcap/jobs/metricsforwarder/config/certs/healthendpoint/ca.crt - cert_file: /var/vcap/jobs/metricsforwarder/config/certs/healthendpoint/server.crt - key_file: /var/vcap/jobs/metricsforwarder/config/certs/healthendpoint/server.key - <% end %> + server_config: + port: <%= p("autoscaler.metricsforwarder.health.port") %> + <% if_p("autoscaler.metricsforwarder.health.ca_cert", "autoscaler.metricsforwarder.health.server_cert", "autoscaler.metricsforwarder.health.server_key") do %> + tls: + ca_file: /var/vcap/jobs/metricsforwarder/config/certs/healthendpoint/ca.crt + cert_file: /var/vcap/jobs/metricsforwarder/config/certs/healthendpoint/server.crt + key_file: /var/vcap/jobs/metricsforwarder/config/certs/healthendpoint/server.key + <% end %> + basic_auth: + username: <%= p("autoscaler.metricsforwarder.health.username") %> + password: <%= p("autoscaler.metricsforwarder.health.password") %> rate_limit: valid_duration: <%= p("autoscaler.metricsforwarder.rate_limit.valid_duration") %> diff --git a/jobs/operator/templates/operator.yml.erb b/jobs/operator/templates/operator.yml.erb index c96a6890a0..83b53699d9 100644 --- a/jobs/operator/templates/operator.yml.erb +++ b/jobs/operator/templates/operator.yml.erb @@ -58,15 +58,17 @@ cf: logging: level: <%= p("autoscaler.operator.logging.level") %> health: - port: <%= p("autoscaler.operator.health.port") %> - username: <%= p("autoscaler.operator.health.username") %> - password: <%= p("autoscaler.operator.health.password") %> - <% if_p("autoscaler.operator.health.ca_cert", "autoscaler.operator.health.server_cert", "autoscaler.operator.health.server_key") do %> - tls: - ca_file: /var/vcap/jobs/operator/config/certs/healthendpoint/ca.crt - cert_file: /var/vcap/jobs/operator/config/certs/healthendpoint/server.crt - key_file: /var/vcap/jobs/operator/config/certs/healthendpoint/server.key - <% end %> + server_config: + port: <%= p("autoscaler.operator.health.port") %> + <% if_p("autoscaler.operator.health.ca_cert", "autoscaler.operator.health.server_cert", "autoscaler.operator.health.server_key") do %> + tls: + ca_file: /var/vcap/jobs/operator/config/certs/healthendpoint/ca.crt + cert_file: /var/vcap/jobs/operator/config/certs/healthendpoint/server.crt + key_file: /var/vcap/jobs/operator/config/certs/healthendpoint/server.key + <% end %> + basic_auth: + username: <%= p("autoscaler.operator.health.username") %> + password: <%= p("autoscaler.operator.health.password") %> http_client_timeout: <%= p("autoscaler.operator.http_client_timeout") %> diff --git a/jobs/scalingengine/templates/scalingengine.yml.erb b/jobs/scalingengine/templates/scalingengine.yml.erb index 38c913d67a..562b65596e 100644 --- a/jobs/scalingengine/templates/scalingengine.yml.erb +++ b/jobs/scalingengine/templates/scalingengine.yml.erb @@ -63,15 +63,17 @@ logging: level: <%= p("autoscaler.scalingengine.logging.level") %> http_client_timeout: <%= p("autoscaler.scalingengine.http_client_timeout") %> health: - port: <%= p("autoscaler.scalingengine.health.port") %> - username: <%= p("autoscaler.scalingengine.health.username") %> - password: <%= p("autoscaler.scalingengine.health.password") %> - <% if_p("autoscaler.scalingengine.health.ca_cert", "autoscaler.scalingengine.health.server_cert", "autoscaler.scalingengine.health.server_key") do %> - tls: - ca_file: /var/vcap/jobs/scalingengine/config/certs/healthendpoint/ca.crt - cert_file: /var/vcap/jobs/scalingengine/config/certs/healthendpoint/server.crt - key_file: /var/vcap/jobs/scalingengine/config/certs/healthendpoint/server.key - <% end %> + basic_auth: + username: <%= p("autoscaler.scalingengine.health.username") %> + password: <%= p("autoscaler.scalingengine.health.password") %> + server_config: + port: <%= p("autoscaler.scalingengine.health.port") %> + <% if_p("autoscaler.scalingengine.health.ca_cert", "autoscaler.scalingengine.health.server_cert", "autoscaler.scalingengine.health.server_key") do %> + tls: + ca_file: /var/vcap/jobs/scalingengine/config/certs/healthendpoint/ca.crt + cert_file: /var/vcap/jobs/scalingengine/config/certs/healthendpoint/server.crt + key_file: /var/vcap/jobs/scalingengine/config/certs/healthendpoint/server.key + <% end %> db: diff --git a/spec/jobs/common/health_endpoint_spec.rb b/spec/jobs/common/health_endpoint_spec.rb index 98a96b2898..097e9e14b6 100644 --- a/spec/jobs/common/health_endpoint_spec.rb +++ b/spec/jobs/common/health_endpoint_spec.rb @@ -13,40 +13,38 @@ %w[operator operator config/operator.yml operator.yml], %w[scalingengine scalingengine config/scalingengine.yml scalingengine.yml] ].each do |service, release_job, config_file, properties_file| - context service do - context "health endpoint" do - before(:each) do - @properties = YAML.safe_load(fixture(properties_file).read) - @template = release.job(release_job).template(config_file) - @links = case service - when "eventgenerator" - [Bosh::Template::Test::Link.new(name: "eventgenerator")] - else - [] - end - @rendered_template = YAML.safe_load(@template.render(@properties, consumes: @links)) - end - it "by default TLS is not configured" do - expect(@rendered_template["health"]["tls"]).to be_nil + describe "service #{service} health endpoint" do + before(:each) do + @properties = YAML.safe_load(fixture(properties_file).read) + @template = release.job(release_job).template(config_file) + @links = case service + when "eventgenerator" + [Bosh::Template::Test::Link.new(name: "eventgenerator")] + else + [] end + @rendered_template = YAML.safe_load(@template.render(@properties, consumes: @links)) + end + it "by default TLS is not configured" do + expect(@rendered_template["health"]["server_config"]["tls"]).to be_nil + end - it "TLS can be enabled" do - service_config = (@properties["autoscaler"][service] ||= {}) - service_config["health"] = { - "ca_cert" => "SOME_CA", - "server_cert" => "SOME_CERT", - "server_key" => "SOME_KEY" - } + it "TLS can be enabled" do + service_config = (@properties["autoscaler"][service] ||= {}) + service_config["health"] = { + "ca_cert" => "SOME_CA", + "server_cert" => "SOME_CERT", + "server_key" => "SOME_KEY" + } - rendered_template = YAML.safe_load(@template.render(@properties, consumes: @links)) + rendered_template = YAML.safe_load(@template.render(@properties, consumes: @links)) - expect(rendered_template["health"]["tls"]).not_to be_nil - expect(rendered_template["health"]["tls"]).to include({ - "key_file" => "/var/vcap/jobs/#{release_job}/config/certs/healthendpoint/server.key", - "ca_file" => "/var/vcap/jobs/#{release_job}/config/certs/healthendpoint/ca.crt", - "cert_file" => "/var/vcap/jobs/#{release_job}/config/certs/healthendpoint/server.crt" - }) - end + expect(rendered_template["health"]["server_config"]["tls"]).not_to be_nil + expect(rendered_template["health"]["server_config"]["tls"]).to include({ + "key_file" => "/var/vcap/jobs/#{release_job}/config/certs/healthendpoint/server.key", + "ca_file" => "/var/vcap/jobs/#{release_job}/config/certs/healthendpoint/ca.crt", + "cert_file" => "/var/vcap/jobs/#{release_job}/config/certs/healthendpoint/server.crt" + }) end end end diff --git a/spec/jobs/eventgenerator/eventgenerator_spec.rb b/spec/jobs/eventgenerator/eventgenerator_spec.rb index e868cb2cd5..3fe85e2ff1 100644 --- a/spec/jobs/eventgenerator/eventgenerator_spec.rb +++ b/spec/jobs/eventgenerator/eventgenerator_spec.rb @@ -29,10 +29,7 @@ "port" => 1234 } } - expect(rendered_template["health"]) - .to include( - {"port" => 1234} - ) + expect(rendered_template["health"]["server_config"]["port"]).to eq(properties["autoscaler"]["eventgenerator"]["health"]["port"]) end it "check eventgenerator username and password" do @@ -44,12 +41,9 @@ } } - expect(rendered_template["health"]) - .to include( - {"port" => 1234, - "username" => "test-user", - "password" => "test-user-password"} - ) + expect(rendered_template["health"]["server_config"]["port"]).to eq(properties["autoscaler"]["eventgenerator"]["health"]["port"]) + expect(rendered_template["health"]["basic_auth"]["username"]).to eq(properties["autoscaler"]["eventgenerator"]["health"]["username"]) + expect(rendered_template["health"]["basic_auth"]["password"]).to eq(properties["autoscaler"]["eventgenerator"]["health"]["password"]) end describe "when using log-cache via https/uaa" do diff --git a/spec/jobs/metricsforwarder/metricsforwarder_spec.rb b/spec/jobs/metricsforwarder/metricsforwarder_spec.rb index 140ca46a92..0d66108e17 100644 --- a/spec/jobs/metricsforwarder/metricsforwarder_spec.rb +++ b/spec/jobs/metricsforwarder/metricsforwarder_spec.rb @@ -42,10 +42,7 @@ } } - expect(rendered_template["health"]) - .to include( - {"port" => 1234} - ) + expect(rendered_template["health"]["server_config"]["port"]).to eq(properties["autoscaler"]["metricsforwarder"]["health"]["port"]) end it "check metricsforwarder basic auth username and password" do @@ -57,12 +54,9 @@ } } - expect(rendered_template["health"]) - .to include( - {"port" => 1234, - "username" => "test-user", - "password" => "test-user-password"} - ) + expect(rendered_template["health"]["server_config"]["port"]).to eq(properties["autoscaler"]["metricsforwarder"]["health"]["port"]) + expect(rendered_template["health"]["basic_auth"]["username"]).to eq(properties["autoscaler"]["metricsforwarder"]["health"]["username"]) + expect(rendered_template["health"]["basic_auth"]["password"]).to eq(properties["autoscaler"]["metricsforwarder"]["health"]["password"]) end it "has a cred helper impl by default" do diff --git a/spec/jobs/operator/operator_spec.rb b/spec/jobs/operator/operator_spec.rb index fb7ebb2264..a3dc12f463 100644 --- a/spec/jobs/operator/operator_spec.rb +++ b/spec/jobs/operator/operator_spec.rb @@ -20,10 +20,7 @@ } } - expect(rendered_template["health"]) - .to include( - {"port" => 1234} - ) + expect(rendered_template["health"]["server_config"]["port"]).to eq(properties["autoscaler"]["operator"]["health"]["port"]) end it "check operator basic auth username and password" do @@ -35,12 +32,9 @@ } } - expect(rendered_template["health"]) - .to include( - {"port" => 1234, - "username" => "test-user", - "password" => "test-user-password"} - ) + expect(rendered_template["health"]["server_config"]["port"]).to eq(properties["autoscaler"]["operator"]["health"]["port"]) + expect(rendered_template["health"]["basic_auth"]["username"]).to eq(properties["autoscaler"]["operator"]["health"]["username"]) + expect(rendered_template["health"]["basic_auth"]["password"]).to eq(properties["autoscaler"]["operator"]["health"]["password"]) end context "uses tls" do diff --git a/spec/jobs/scalingengine/scalingengine_spec.rb b/spec/jobs/scalingengine/scalingengine_spec.rb index 1c40ad7d3b..39d4c13891 100644 --- a/spec/jobs/scalingengine/scalingengine_spec.rb +++ b/spec/jobs/scalingengine/scalingengine_spec.rb @@ -21,10 +21,7 @@ } } - expect(rendered_template["health"]) - .to include( - {"port" => 1234} - ) + expect(rendered_template["health"]["server_config"]["port"]).to eq(properties["autoscaler"]["scalingengine"]["health"]["port"]) end it "check scalingengine basic auth username and password" do @@ -36,12 +33,9 @@ } } - expect(rendered_template["health"]) - .to include( - {"port" => 1234, - "username" => "test-user", - "password" => "test-user-password"} - ) + expect(rendered_template["health"]["server_config"]["port"]).to eq(properties["autoscaler"]["scalingengine"]["health"]["port"]) + expect(rendered_template["health"]["basic_auth"]["username"]).to eq(properties["autoscaler"]["scalingengine"]["health"]["username"]) + expect(rendered_template["health"]["basic_auth"]["password"]).to eq(properties["autoscaler"]["scalingengine"]["health"]["password"]) end end diff --git a/src/autoscaler/build-extension-file.sh b/src/autoscaler/build-extension-file.sh index a1adb01176..90ec9f1644 100755 --- a/src/autoscaler/build-extension-file.sh +++ b/src/autoscaler/build-extension-file.sh @@ -40,15 +40,18 @@ _schema-version: 3.3.0 modules: - name: metricsforwarder requires: - - name: config + - name: metricsforwarder-config - name: policydb - name: syslog-client parameters: routes: - route: ${METRICSFORWARDER_APPNAME}.\${default-domain} + - name: publicapiserver + parameters: + instances: 0 resources: -- name: config +- name: metricsforwarder-config parameters: config: metricsforwarder: diff --git a/src/autoscaler/mta.tpl.yaml b/src/autoscaler/mta.tpl.yaml index 0c5f5b5122..1f96ed726b 100644 --- a/src/autoscaler/mta.tpl.yaml +++ b/src/autoscaler/mta.tpl.yaml @@ -12,7 +12,7 @@ modules: properties: GO_INSTALL_PACKAGE_SPEC: code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/cmd/metricsforwarder requires: - - name: config + - name: metricsforwarder-config - name: policydb - name: syslog-client - name: app-autoscaler-application-logs @@ -26,14 +26,39 @@ modules: builder: custom commands: - make vendor + - name: publicapiserver + type: go + path: . + properties: + GO_INSTALL_PACKAGE_SPEC: code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/cmd/publicapiserver + requires: + - name: publicapiserver-config + - name: policydb + - name: app-autoscaler-application-logs + parameters: + memory: 1G + disk-quota: 1G + instances: 2 + stack: cflinuxfs4 + routes: + build-parameters: + builder: custom + commands: + - make vendor resources: -- name: config +- name: metricsforwarder-config type: org.cloudfoundry.user-provided-service parameters: service-tags: - - config + - metricsforwarder-config path: metricsforwarder/default_config.json +- name: publicapiserver-config + type: org.cloudfoundry.user-provided-service + parameters: + service-tags: + - publicapiserver-config + path: api/default_config.json - name: policydb type: org.cloudfoundry.user-provided-service parameters: From ef9cbc27714c958a533608b777480122a31bf1e0 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Mon, 14 Oct 2024 18:14:58 +0200 Subject: [PATCH 30/39] Update devbox --- devbox.json | 6 +- devbox.lock | 784 ++++++++++++++++++++++++++-------------------------- 2 files changed, 394 insertions(+), 396 deletions(-) diff --git a/devbox.json b/devbox.json index 1dc861fecd..feb5ae44a5 100644 --- a/devbox.json +++ b/devbox.json @@ -9,6 +9,7 @@ "bundix": "latest", "coreutils": "latest", "delve": "latest", + "direnv": "2.34.0", "fly": "7.10.0", "gh": "latest", "gnumake": "4.4", @@ -35,12 +36,11 @@ "ginkgo": "2.20.2", "bundler": "latest", "golangci-lint": "1.61.0", - "actionlint": "1.7.3", - "go": "1.22.5", "act": "0.2.67", + "actionlint": "latest", "cloudfoundry-cli": "8.8.1", - "direnv": "2.35.0", "google-cloud-sdk": "latest", + "go": "1.22.3", "temurin-bin-21": "latest", "ruby": "latest" }, diff --git a/devbox.lock b/devbox.lock index 1f81d8a820..a0d42be71f 100644 --- a/devbox.lock +++ b/devbox.lock @@ -49,51 +49,51 @@ } } }, - "actionlint@1.7.3": { - "last_modified": "2024-10-06T12:21:13Z", - "resolved": "github:NixOS/nixpkgs/50b3bd3fed0442bcbf7f58355e990da84af1749d#actionlint", + "actionlint@latest": { + "last_modified": "2024-09-24T10:20:15Z", + "resolved": "github:NixOS/nixpkgs/965289e5e07243f1cde3212d8bcaf726d36c5c46#actionlint", "source": "devbox-search", - "version": "1.7.3", + "version": "1.7.2", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/33rbqx21gcp3imbrr2i0ykhz7x9ixsj5-actionlint-1.7.3", + "path": "/nix/store/qv1fnp2ifk37jlr1lbzf5r63vr3q6k41-actionlint-1.7.2", "default": true } ], - "store_path": "/nix/store/33rbqx21gcp3imbrr2i0ykhz7x9ixsj5-actionlint-1.7.3" + "store_path": "/nix/store/qv1fnp2ifk37jlr1lbzf5r63vr3q6k41-actionlint-1.7.2" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/xa1zzhm3szslprnknblyhvp4lbagk6nx-actionlint-1.7.3", + "path": "/nix/store/nw0xpacz21b9rszn4k6ay452pf8amf1p-actionlint-1.7.2", "default": true } ], - "store_path": "/nix/store/xa1zzhm3szslprnknblyhvp4lbagk6nx-actionlint-1.7.3" + "store_path": "/nix/store/nw0xpacz21b9rszn4k6ay452pf8amf1p-actionlint-1.7.2" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/z37rjxbvy5z9pk0vbimw2gw2rqdxjjc3-actionlint-1.7.3", + "path": "/nix/store/rh6f40c82jp5z3irxsq6lfapg6ckc9br-actionlint-1.7.2", "default": true } ], - "store_path": "/nix/store/z37rjxbvy5z9pk0vbimw2gw2rqdxjjc3-actionlint-1.7.3" + "store_path": "/nix/store/rh6f40c82jp5z3irxsq6lfapg6ckc9br-actionlint-1.7.2" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/48asng2d6lcma3rblhq56zwzbp7c7hbf-actionlint-1.7.3", + "path": "/nix/store/ggpghzgdd1cv04hh93cmqjb7mm3zqc7m-actionlint-1.7.2", "default": true } ], - "store_path": "/nix/store/48asng2d6lcma3rblhq56zwzbp7c7hbf-actionlint-1.7.3" + "store_path": "/nix/store/ggpghzgdd1cv04hh93cmqjb7mm3zqc7m-actionlint-1.7.2" } } }, @@ -146,8 +146,8 @@ } }, "bundix@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#bundix", + "last_modified": "2024-09-01T12:44:31Z", + "resolved": "github:NixOS/nixpkgs/b833ff01a0d694b910daca6e2ff4a3f26dee478c#bundix", "source": "devbox-search", "version": "2.5.2", "systems": { @@ -155,47 +155,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/pbrz49zxdd92h389cfyk5lnv98w0gc3b-bundix-2.5.2", + "path": "/nix/store/46yrpr6iqfzkas8b7mixjpi2qsdb4rss-bundix-2.5.2", "default": true } ], - "store_path": "/nix/store/pbrz49zxdd92h389cfyk5lnv98w0gc3b-bundix-2.5.2" + "store_path": "/nix/store/46yrpr6iqfzkas8b7mixjpi2qsdb4rss-bundix-2.5.2" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/scx6rk9b6h2nz8bnrdy47nkg7bskwsx4-bundix-2.5.2", + "path": "/nix/store/z2n8rwcis8dc45ids4apdaabj5f9z7qi-bundix-2.5.2", "default": true } ], - "store_path": "/nix/store/scx6rk9b6h2nz8bnrdy47nkg7bskwsx4-bundix-2.5.2" + "store_path": "/nix/store/z2n8rwcis8dc45ids4apdaabj5f9z7qi-bundix-2.5.2" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/b1kj0931c009gnjb2dxakndx4nhilw5p-bundix-2.5.2", + "path": "/nix/store/zykb79mvak4ip2r8x3dfmz9y5ic0x1zd-bundix-2.5.2", "default": true } ], - "store_path": "/nix/store/b1kj0931c009gnjb2dxakndx4nhilw5p-bundix-2.5.2" + "store_path": "/nix/store/zykb79mvak4ip2r8x3dfmz9y5ic0x1zd-bundix-2.5.2" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/r2ycwi3f81sagkjn8ask8d3kqqb9f6h1-bundix-2.5.2", + "path": "/nix/store/vyn43k2w72b02016v1dwaf396w8f1vmf-bundix-2.5.2", "default": true } ], - "store_path": "/nix/store/r2ycwi3f81sagkjn8ask8d3kqqb9f6h1-bundix-2.5.2" + "store_path": "/nix/store/vyn43k2w72b02016v1dwaf396w8f1vmf-bundix-2.5.2" } } }, "bundler@latest": { - "last_modified": "2024-09-10T15:01:03Z", - "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#bundler", + "last_modified": "2024-09-12T11:58:09Z", + "resolved": "github:NixOS/nixpkgs/280db3decab4cbeb22a4599bd472229ab74d25e1#bundler", "source": "devbox-search", "version": "2.5.16", "systems": { @@ -290,8 +290,8 @@ } }, "coreutils@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#coreutils", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#coreutils", "source": "devbox-search", "version": "9.5", "systems": { @@ -299,65 +299,65 @@ "outputs": [ { "name": "out", - "path": "/nix/store/7k0qi2r54imwjfs2bklg7fv0mn5jglil-coreutils-9.5", + "path": "/nix/store/21myf7jnm3j5myc9yp8nlhgp427si0cw-coreutils-9.5", "default": true }, { "name": "info", - "path": "/nix/store/x15d6rpi8icjcq7arr51ci0z2q7pyn2y-coreutils-9.5-info" + "path": "/nix/store/gngnz090cwq0iv6j8gf90i8ir23qlh8w-coreutils-9.5-info" } ], - "store_path": "/nix/store/7k0qi2r54imwjfs2bklg7fv0mn5jglil-coreutils-9.5" + "store_path": "/nix/store/21myf7jnm3j5myc9yp8nlhgp427si0cw-coreutils-9.5" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ykgg9l0s62brx3xmnhdyjms94ylgas1i-coreutils-9.5", + "path": "/nix/store/4wha9m6mh2k5a14srl4nmzpi9f7isp56-coreutils-9.5", "default": true }, { "name": "debug", - "path": "/nix/store/ml3apw95278grz137ssqkqh06k9kdkw5-coreutils-9.5-debug" + "path": "/nix/store/6hkfnznanfflydnwwlik54aizjqhqra9-coreutils-9.5-debug" }, { "name": "info", - "path": "/nix/store/3ncshm0nv5xwlv9j294577sy63xikfpp-coreutils-9.5-info" + "path": "/nix/store/lpcs05q46id2l5qzf5pmfjqqj0j5q4sw-coreutils-9.5-info" } ], - "store_path": "/nix/store/ykgg9l0s62brx3xmnhdyjms94ylgas1i-coreutils-9.5" + "store_path": "/nix/store/4wha9m6mh2k5a14srl4nmzpi9f7isp56-coreutils-9.5" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/n7gmn34vlgrirqhxr6rg9yy6lg8pm8id-coreutils-9.5", + "path": "/nix/store/gsk79pib6zrfm486m1g10m3z8z1lapyb-coreutils-9.5", "default": true }, { "name": "info", - "path": "/nix/store/yzhjqzsyjyka58dj33bfvxal4walzahs-coreutils-9.5-info" + "path": "/nix/store/91d0z5pj8740gblgj0ycksf234fhyq4w-coreutils-9.5-info" } ], - "store_path": "/nix/store/n7gmn34vlgrirqhxr6rg9yy6lg8pm8id-coreutils-9.5" + "store_path": "/nix/store/gsk79pib6zrfm486m1g10m3z8z1lapyb-coreutils-9.5" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/cnknp3yxfibxjhila0sjd1v3yglqssng-coreutils-9.5", + "path": "/nix/store/vb8mdklw65p9wikp97ybmnyay0xzipx3-coreutils-9.5", "default": true }, { "name": "debug", - "path": "/nix/store/4yz2r9nncispdfdsrrm3w881482i05ca-coreutils-9.5-debug" + "path": "/nix/store/9818diawz01qd1kjgfq071zyglsw35pg-coreutils-9.5-debug" }, { "name": "info", - "path": "/nix/store/bfxj13l4y87bd14z9wmjd20lgzk0rjwy-coreutils-9.5-info" + "path": "/nix/store/x2rwb8yxlpl4mh6qn85kfsr63v44zxnl-coreutils-9.5-info" } ], - "store_path": "/nix/store/cnknp3yxfibxjhila0sjd1v3yglqssng-coreutils-9.5" + "store_path": "/nix/store/vb8mdklw65p9wikp97ybmnyay0xzipx3-coreutils-9.5" } } }, @@ -410,8 +410,8 @@ } }, "delve@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#delve", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#delve", "source": "devbox-search", "version": "1.23.0", "systems": { @@ -419,91 +419,89 @@ "outputs": [ { "name": "out", - "path": "/nix/store/rx8vywp2nl9qvd5d2jlqnpjhjm83qmnj-delve-1.23.0", + "path": "/nix/store/hyk0a1p5bmxsgvyvmy4b5w7dyjp8wm7a-delve-1.23.0", "default": true } ], - "store_path": "/nix/store/rx8vywp2nl9qvd5d2jlqnpjhjm83qmnj-delve-1.23.0" + "store_path": "/nix/store/hyk0a1p5bmxsgvyvmy4b5w7dyjp8wm7a-delve-1.23.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/39djxclvi16c42xsjrg107g4xr01mndm-delve-1.23.0", + "path": "/nix/store/vkq8h2y846qap2laz1gx63f682f6yq6l-delve-1.23.0", "default": true } ], - "store_path": "/nix/store/39djxclvi16c42xsjrg107g4xr01mndm-delve-1.23.0" + "store_path": "/nix/store/vkq8h2y846qap2laz1gx63f682f6yq6l-delve-1.23.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/l1vkgm87ys2yc9hpgnk371hzs7kfn2xq-delve-1.23.0", + "path": "/nix/store/p9sscbm746fqghl065zc6k4z8w36s28j-delve-1.23.0", "default": true } ], - "store_path": "/nix/store/l1vkgm87ys2yc9hpgnk371hzs7kfn2xq-delve-1.23.0" + "store_path": "/nix/store/p9sscbm746fqghl065zc6k4z8w36s28j-delve-1.23.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/hyyl3pzqc3l569dfhkn5wbny355akaqf-delve-1.23.0", + "path": "/nix/store/2vpk03v85dpxjffvhh7akg3bnsqdciyq-delve-1.23.0", "default": true } ], - "store_path": "/nix/store/hyyl3pzqc3l569dfhkn5wbny355akaqf-delve-1.23.0" + "store_path": "/nix/store/2vpk03v85dpxjffvhh7akg3bnsqdciyq-delve-1.23.0" } } }, - "direnv@2.35.0": { - "last_modified": "2024-10-08T08:54:18Z", - "resolved": "github:NixOS/nixpkgs/a2eacc0c62c0537bd1a7a60c1f91d1f3a59fd013#direnv", + "direnv@2.34.0": { + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#direnv", "source": "devbox-search", - "version": "2.35.0", + "version": "2.34.0", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - - "path": "/nix/store/b862impi4vy6hx7irfhnp3y5padbw0jl-direnv-2.35.0", + "path": "/nix/store/439f5yi8i1akxl2669f5mam6iacisycv-direnv-2.34.0", "default": true } ], - "store_path": "/nix/store/b862impi4vy6hx7irfhnp3y5padbw0jl-direnv-2.35.0" + "store_path": "/nix/store/439f5yi8i1akxl2669f5mam6iacisycv-direnv-2.34.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/z2kgy6bcdn3h0rplawv2nj09hmffknhx-direnv-2.35.0", + "path": "/nix/store/1fj0mbpggzc91ykg1n5q86gd5ih7azws-direnv-2.34.0", "default": true } ], - "store_path": "/nix/store/z2kgy6bcdn3h0rplawv2nj09hmffknhx-direnv-2.35.0" + "store_path": "/nix/store/1fj0mbpggzc91ykg1n5q86gd5ih7azws-direnv-2.34.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/djl7k7rvngmjr8pbhgmzhccv82hlaxms-direnv-2.35.0", + "path": "/nix/store/v05bzbn98fq9qg6fpr5znpkqxgyx0xx4-direnv-2.34.0", "default": true } ], - "store_path": "/nix/store/djl7k7rvngmjr8pbhgmzhccv82hlaxms-direnv-2.35.0" + "store_path": "/nix/store/v05bzbn98fq9qg6fpr5znpkqxgyx0xx4-direnv-2.34.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - - "path": "/nix/store/0cc1cyiifsb6g6vr2271448va439q73b-direnv-2.35.0", + "path": "/nix/store/ml0yghnf9h5rb4dmv1brkfi60ils8gz6-direnv-2.34.0", "default": true } ], - "store_path": "/nix/store/0cc1cyiifsb6g6vr2271448va439q73b-direnv-2.35.0" + "store_path": "/nix/store/ml0yghnf9h5rb4dmv1brkfi60ils8gz6-direnv-2.34.0" } } }, @@ -556,56 +554,56 @@ } }, "gh@latest": { - "last_modified": "2024-08-02T23:16:43Z", - "resolved": "github:NixOS/nixpkgs/81610abc161d4021b29199aa464d6a1a521e0cc9#gh", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#gh", "source": "devbox-search", - "version": "2.54.0", + "version": "2.55.0", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/ffhz14j4v0wgr24li67n58l3r85khyv3-gh-2.54.0", + "path": "/nix/store/9p3lk5rxhvs69qdrh6cl870xmfnw7i9s-gh-2.55.0", "default": true } ], - "store_path": "/nix/store/ffhz14j4v0wgr24li67n58l3r85khyv3-gh-2.54.0" + "store_path": "/nix/store/9p3lk5rxhvs69qdrh6cl870xmfnw7i9s-gh-2.55.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/giqcnp1bkjmggzwylmzi221wwyzvwcj9-gh-2.54.0", + "path": "/nix/store/cpzrvzha3bhgipkfx0nzsg38aqr5p6c9-gh-2.55.0", "default": true } ], - "store_path": "/nix/store/giqcnp1bkjmggzwylmzi221wwyzvwcj9-gh-2.54.0" + "store_path": "/nix/store/cpzrvzha3bhgipkfx0nzsg38aqr5p6c9-gh-2.55.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/xl1kr0ah5vrh0mg2g21zlb6n73w8x4cj-gh-2.54.0", + "path": "/nix/store/bfnfq466pbqxxfzdwclfkh4wlwpgxlaj-gh-2.55.0", "default": true } ], - "store_path": "/nix/store/xl1kr0ah5vrh0mg2g21zlb6n73w8x4cj-gh-2.54.0" + "store_path": "/nix/store/bfnfq466pbqxxfzdwclfkh4wlwpgxlaj-gh-2.55.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/yz19xhnk02byz0h0qpaz18nggin56zqw-gh-2.54.0", + "path": "/nix/store/braa7pb3r5lhrv2cdgrrk5iazgy4x55c-gh-2.55.0", "default": true } ], - "store_path": "/nix/store/yz19xhnk02byz0h0qpaz18nggin56zqw-gh-2.54.0" + "store_path": "/nix/store/braa7pb3r5lhrv2cdgrrk5iazgy4x55c-gh-2.55.0" } } }, "ginkgo@2.20.2": { - "last_modified": "2024-09-10T15:01:03Z", - "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#ginkgo", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#ginkgo", "source": "devbox-search", "version": "2.20.2", "systems": { @@ -613,47 +611,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/nid0ha9rpkvbcp77q6vr5zy351n8yn9m-ginkgo-2.20.2", + "path": "/nix/store/bmaxiy62cl8pfv421m31d8yskbj7wzlh-ginkgo-2.20.2", "default": true } ], - "store_path": "/nix/store/nid0ha9rpkvbcp77q6vr5zy351n8yn9m-ginkgo-2.20.2" + "store_path": "/nix/store/bmaxiy62cl8pfv421m31d8yskbj7wzlh-ginkgo-2.20.2" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ak1481f4za57iaxr4kljslwyxnskfc93-ginkgo-2.20.2", + "path": "/nix/store/23ssidzz6ws4g5km5nlnlzpsk2hx0kxp-ginkgo-2.20.2", "default": true } ], - "store_path": "/nix/store/ak1481f4za57iaxr4kljslwyxnskfc93-ginkgo-2.20.2" + "store_path": "/nix/store/23ssidzz6ws4g5km5nlnlzpsk2hx0kxp-ginkgo-2.20.2" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/1k9ykqp9a0q6jrbg4zm5jygasl5jcdcy-ginkgo-2.20.2", + "path": "/nix/store/wgnx7y28bpss448qv8aqfryzpx0z9wjd-ginkgo-2.20.2", "default": true } ], - "store_path": "/nix/store/1k9ykqp9a0q6jrbg4zm5jygasl5jcdcy-ginkgo-2.20.2" + "store_path": "/nix/store/wgnx7y28bpss448qv8aqfryzpx0z9wjd-ginkgo-2.20.2" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/2waqj0vva63gjq24agj96qbf5gb3kicp-ginkgo-2.20.2", + "path": "/nix/store/lmsqp94ly9vrw8snzpncwjsvjnlj15sm-ginkgo-2.20.2", "default": true } ], - "store_path": "/nix/store/2waqj0vva63gjq24agj96qbf5gb3kicp-ginkgo-2.20.2" + "store_path": "/nix/store/lmsqp94ly9vrw8snzpncwjsvjnlj15sm-ginkgo-2.20.2" } } }, "gnumake@4.4": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#gnumake", + "last_modified": "2024-09-01T03:39:50Z", + "resolved": "github:NixOS/nixpkgs/4a9443e2a4e06cbaff89056b5cdf6777c1fe5755#gnumake", "source": "devbox-search", "version": "4.4.1", "systems": { @@ -661,91 +659,91 @@ "outputs": [ { "name": "out", - "path": "/nix/store/6gylp4vygmsm12rafhzvklrfkbhwwq40-gnumake-4.4.1", + "path": "/nix/store/92kvph7n3kva2snfj0hq3cl3cbmf8chc-gnumake-4.4.1", "default": true }, { "name": "man", - "path": "/nix/store/wb8icphy7jmgb6iiikn5djsk9rnlvm3d-gnumake-4.4.1-man", + "path": "/nix/store/8aa6zhw2lgxarahfgjlk3f7fg8x40k35-gnumake-4.4.1-man", "default": true }, { "name": "info", - "path": "/nix/store/537dz619lcxi30vr2nz7fdvkyykd4v18-gnumake-4.4.1-info" + "path": "/nix/store/zn8336d8r2sknny33k9l1v9vs86cpa4k-gnumake-4.4.1-info" } ], - "store_path": "/nix/store/6gylp4vygmsm12rafhzvklrfkbhwwq40-gnumake-4.4.1" + "store_path": "/nix/store/92kvph7n3kva2snfj0hq3cl3cbmf8chc-gnumake-4.4.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/zm3bz71h77594dnry2v5q6rd6wyb6j4y-gnumake-4.4.1", + "path": "/nix/store/89ifqwbc4335dkfx1fmlhfgjr9dyq42a-gnumake-4.4.1", "default": true }, { "name": "man", - "path": "/nix/store/ww1zz6d9lbypwxfg1187f9l2qj16abp9-gnumake-4.4.1-man", + "path": "/nix/store/xlijm05z007kshc6qs18mknv6j8vwqq2-gnumake-4.4.1-man", "default": true }, { "name": "debug", - "path": "/nix/store/2jmxd53sms6h9z95i5rl54s6899pb2ix-gnumake-4.4.1-debug" + "path": "/nix/store/6s14q5nxy67mwa0gpjjzsil7v8d46gdm-gnumake-4.4.1-debug" }, { "name": "info", - "path": "/nix/store/5aq5k0fl2w43iwlfqn4jy3hpbxl7sh9y-gnumake-4.4.1-info" + "path": "/nix/store/dy4gszlwxsfa73pgqq5skkdw1ffb4n4j-gnumake-4.4.1-info" } ], - "store_path": "/nix/store/zm3bz71h77594dnry2v5q6rd6wyb6j4y-gnumake-4.4.1" + "store_path": "/nix/store/89ifqwbc4335dkfx1fmlhfgjr9dyq42a-gnumake-4.4.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/ni2pnvdf4hydh6i72l762fqr5rrqzfcy-gnumake-4.4.1", + "path": "/nix/store/z75g6caslrjk4s8r797ivp8q5cx6f7bj-gnumake-4.4.1", "default": true }, { "name": "man", - "path": "/nix/store/wwl5zd4v5f0p2w97rz1b7whgy6xlqlf3-gnumake-4.4.1-man", + "path": "/nix/store/cgv4n8r330047gcz47ci52d5pw6k0qg8-gnumake-4.4.1-man", "default": true }, { "name": "info", - "path": "/nix/store/29kbczjj9j0vv1kxfxxv08jk6im2axq2-gnumake-4.4.1-info" + "path": "/nix/store/v9pch95kwwi7j18wgiw3mn1av89dg230-gnumake-4.4.1-info" } ], - "store_path": "/nix/store/ni2pnvdf4hydh6i72l762fqr5rrqzfcy-gnumake-4.4.1" + "store_path": "/nix/store/z75g6caslrjk4s8r797ivp8q5cx6f7bj-gnumake-4.4.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/3ssglpx5xilkrmkhyl4bg0501wshmsgv-gnumake-4.4.1", + "path": "/nix/store/i1ys5lhw9g9wcf6jz44n1m0k1p83zcr9-gnumake-4.4.1", "default": true }, { "name": "man", - "path": "/nix/store/kr3rxnzfskfzhwd10cx2wh0w84psn9hs-gnumake-4.4.1-man", + "path": "/nix/store/nhs9k7cw3kd4l4v0l22s1nwsxfydrc3m-gnumake-4.4.1-man", "default": true }, { "name": "debug", - "path": "/nix/store/25qnnb3mh78d5vz0bgj2ax8yv09g90jr-gnumake-4.4.1-debug" + "path": "/nix/store/pi5ls9kbadmxzj2r3xx8c5h65g5iddha-gnumake-4.4.1-debug" }, { "name": "info", - "path": "/nix/store/xmb5hll19li4k59mrrrh54zx7md6brsn-gnumake-4.4.1-info" + "path": "/nix/store/9qlbz0xav5rns8bmdn2gzajvbl4qw0kz-gnumake-4.4.1-info" } ], - "store_path": "/nix/store/3ssglpx5xilkrmkhyl4bg0501wshmsgv-gnumake-4.4.1" + "store_path": "/nix/store/i1ys5lhw9g9wcf6jz44n1m0k1p83zcr9-gnumake-4.4.1" } } }, "gnutar@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#gnutar", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#gnutar", "source": "devbox-search", "version": "1.35", "systems": { @@ -753,153 +751,153 @@ "outputs": [ { "name": "out", - "path": "/nix/store/lzndf97akrpmd3vjdq06xzwk8fnjc4j7-gnutar-1.35", + "path": "/nix/store/01091ljf4l5s1gdmqpmvdvxp38wp8zc6-gnutar-1.35", "default": true }, { "name": "info", - "path": "/nix/store/93ybjs28rh93c3gjxg17y5hyqzvj3l24-gnutar-1.35-info" + "path": "/nix/store/z2bl9ircb1nsq8a22l88lajm07hgbxfz-gnutar-1.35-info" } ], - "store_path": "/nix/store/lzndf97akrpmd3vjdq06xzwk8fnjc4j7-gnutar-1.35" + "store_path": "/nix/store/01091ljf4l5s1gdmqpmvdvxp38wp8zc6-gnutar-1.35" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/c309qwh4x5786ipcf30paci7mf20gl0c-gnutar-1.35", + "path": "/nix/store/r022jb6xrsvl7h3smwqcc88fdavz0gw4-gnutar-1.35", "default": true }, { "name": "info", - "path": "/nix/store/yg31hri62amv02a959rj6y0j9yjl25zx-gnutar-1.35-info" + "path": "/nix/store/b064hipy67xf7a5kw79f84zx3dcnfxn8-gnutar-1.35-info" } ], - "store_path": "/nix/store/c309qwh4x5786ipcf30paci7mf20gl0c-gnutar-1.35" + "store_path": "/nix/store/r022jb6xrsvl7h3smwqcc88fdavz0gw4-gnutar-1.35" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/4n04dldm6yfiijgk1p74i8jx4zlyvi9f-gnutar-1.35", + "path": "/nix/store/05pl6avyxhbqhgc78vvghg87f44p4i4g-gnutar-1.35", "default": true }, { "name": "info", - "path": "/nix/store/z5f9kspkbp32450bmdy14if52y0wpbxn-gnutar-1.35-info" + "path": "/nix/store/hffaa92zahkkzyssrbadmaqf13kw1df1-gnutar-1.35-info" } ], - "store_path": "/nix/store/4n04dldm6yfiijgk1p74i8jx4zlyvi9f-gnutar-1.35" + "store_path": "/nix/store/05pl6avyxhbqhgc78vvghg87f44p4i4g-gnutar-1.35" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/nzzl7dnay9jzgfv9fbwg1zza6ji7bjvr-gnutar-1.35", + "path": "/nix/store/gimzvyxqk4gq9rqbf8lc65gdhrkhj0iv-gnutar-1.35", "default": true }, { "name": "info", - "path": "/nix/store/kjrphh6wj48rb3xjdqiw81w2ad8lnrlc-gnutar-1.35-info" + "path": "/nix/store/6f46wh8c1dpdakrwlrnmkyzl3fjr4a1k-gnutar-1.35-info" } ], - "store_path": "/nix/store/nzzl7dnay9jzgfv9fbwg1zza6ji7bjvr-gnutar-1.35" + "store_path": "/nix/store/gimzvyxqk4gq9rqbf8lc65gdhrkhj0iv-gnutar-1.35" } } }, "go-tools@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#go-tools", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#go-tools", "source": "devbox-search", - "version": "2023.1.7", + "version": "2024.1.1", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/86zl4ynzkcn47lc0w1yqmwgqvj6icjs0-go-tools-2023.1.7", + "path": "/nix/store/d4lfrv2s9m0lk4pd13kv25z0pxw5x3x2-go-tools-2024.1.1", "default": true } ], - "store_path": "/nix/store/86zl4ynzkcn47lc0w1yqmwgqvj6icjs0-go-tools-2023.1.7" + "store_path": "/nix/store/d4lfrv2s9m0lk4pd13kv25z0pxw5x3x2-go-tools-2024.1.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ijpcpfyvnbbp9p3nqafc60scrraj6r6b-go-tools-2023.1.7", + "path": "/nix/store/h8pxdx739qijcimdi1y97pbc0vmm4nvf-go-tools-2024.1.1", "default": true } ], - "store_path": "/nix/store/ijpcpfyvnbbp9p3nqafc60scrraj6r6b-go-tools-2023.1.7" + "store_path": "/nix/store/h8pxdx739qijcimdi1y97pbc0vmm4nvf-go-tools-2024.1.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/fh65j490nikqss4qjkncc2f62rq8kf92-go-tools-2023.1.7", + "path": "/nix/store/g8vgngx6jhqq44a3568iay9kg6v2dv3x-go-tools-2024.1.1", "default": true } ], - "store_path": "/nix/store/fh65j490nikqss4qjkncc2f62rq8kf92-go-tools-2023.1.7" + "store_path": "/nix/store/g8vgngx6jhqq44a3568iay9kg6v2dv3x-go-tools-2024.1.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/i5rksvza7cdgdg47rq525kz0i9vsw40c-go-tools-2023.1.7", + "path": "/nix/store/631sym9s1rkdqs19qn4iril3isprla4z-go-tools-2024.1.1", "default": true } ], - "store_path": "/nix/store/i5rksvza7cdgdg47rq525kz0i9vsw40c-go-tools-2023.1.7" + "store_path": "/nix/store/631sym9s1rkdqs19qn4iril3isprla4z-go-tools-2024.1.1" } } }, - "go@1.22.5": { - "last_modified": "2024-08-14T11:41:26Z", - "resolved": "github:NixOS/nixpkgs/0cb2fd7c59fed0cd82ef858cbcbdb552b9a33465#go", + "go@1.22.3": { + "last_modified": "2024-06-12T20:55:33Z", + "resolved": "github:NixOS/nixpkgs/a9858885e197f984d92d7fe64e9fff6b2e488d40#go", "source": "devbox-search", - "version": "1.22.5", + "version": "1.22.3", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/p2i1kd6n12qj3s8kx65l3199mmjcffwz-go-1.22.5", + "path": "/nix/store/m4ip3yyqg4k2pilbyfczgiy8xqvh4554-go-1.22.3", "default": true } ], - "store_path": "/nix/store/p2i1kd6n12qj3s8kx65l3199mmjcffwz-go-1.22.5" + "store_path": "/nix/store/m4ip3yyqg4k2pilbyfczgiy8xqvh4554-go-1.22.3" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/xvwr7mp9yqafl8pclayhrhdcxkszaf6d-go-1.22.5", + "path": "/nix/store/iz2bp36nbgp2l7day14186nmw2v4v0qg-go-1.22.3", "default": true } ], - "store_path": "/nix/store/xvwr7mp9yqafl8pclayhrhdcxkszaf6d-go-1.22.5" + "store_path": "/nix/store/iz2bp36nbgp2l7day14186nmw2v4v0qg-go-1.22.3" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/vkrmfnargqkzg5amlfl4yh8vgxja7pli-go-1.22.5", + "path": "/nix/store/gl1vr9lznqg5zv2f0bvh0q469k82mjz0-go-1.22.3", "default": true } ], - "store_path": "/nix/store/vkrmfnargqkzg5amlfl4yh8vgxja7pli-go-1.22.5" + "store_path": "/nix/store/gl1vr9lznqg5zv2f0bvh0q469k82mjz0-go-1.22.3" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/4ay992wzksf59aapkkh5lflv4rkbmdjy-go-1.22.5", + "path": "/nix/store/vz8d6wmfcf38l3h3vymwqr6c5zxp5jmp-go-1.22.3", "default": true } ], - "store_path": "/nix/store/4ay992wzksf59aapkkh5lflv4rkbmdjy-go-1.22.5" + "store_path": "/nix/store/vz8d6wmfcf38l3h3vymwqr6c5zxp5jmp-go-1.22.3" } } }, @@ -952,56 +950,56 @@ } }, "google-cloud-sdk@latest": { - "last_modified": "2024-10-01T01:43:25Z", - "resolved": "github:NixOS/nixpkgs/9682b2197dabc185fcca802ac1ac21136e48fcc2#google-cloud-sdk", + "last_modified": "2024-09-16T10:17:16Z", + "resolved": "github:NixOS/nixpkgs/20f9370d5f588fb8c72e844c54511cab054b5f40#google-cloud-sdk", "source": "devbox-search", - "version": "494.0.0", + "version": "492.0.0", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/vj189b7g7a47m410n7i5218i339zrv1r-google-cloud-sdk-494.0.0", + "path": "/nix/store/j9baimnbg147szh8ws2hc1hmkb423riq-google-cloud-sdk-492.0.0", "default": true } ], - "store_path": "/nix/store/vj189b7g7a47m410n7i5218i339zrv1r-google-cloud-sdk-494.0.0" + "store_path": "/nix/store/j9baimnbg147szh8ws2hc1hmkb423riq-google-cloud-sdk-492.0.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/fcy6s62xsbgnzdmg42wsd84a9066hf8r-google-cloud-sdk-494.0.0", + "path": "/nix/store/r6dgpw8nqg3l8x78i527hb2slz7chk8g-google-cloud-sdk-492.0.0", "default": true } ], - "store_path": "/nix/store/fcy6s62xsbgnzdmg42wsd84a9066hf8r-google-cloud-sdk-494.0.0" + "store_path": "/nix/store/r6dgpw8nqg3l8x78i527hb2slz7chk8g-google-cloud-sdk-492.0.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/0k1wcz2h1pixyas5acpxpcvrlfr178m7-google-cloud-sdk-494.0.0", + "path": "/nix/store/5pywqc481xpqgxn89lzxa1lf0p2h13zv-google-cloud-sdk-492.0.0", "default": true } ], - "store_path": "/nix/store/0k1wcz2h1pixyas5acpxpcvrlfr178m7-google-cloud-sdk-494.0.0" + "store_path": "/nix/store/5pywqc481xpqgxn89lzxa1lf0p2h13zv-google-cloud-sdk-492.0.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/h5xqqp2y3cp3nkf2jy2ysc1cyapzq7mf-google-cloud-sdk-494.0.0", + "path": "/nix/store/dqpw1myxbj5zk0f8w0lsfzqq98g23hj9-google-cloud-sdk-492.0.0", "default": true } ], - "store_path": "/nix/store/h5xqqp2y3cp3nkf2jy2ysc1cyapzq7mf-google-cloud-sdk-494.0.0" + "store_path": "/nix/store/dqpw1myxbj5zk0f8w0lsfzqq98g23hj9-google-cloud-sdk-492.0.0" } } }, "gopls@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#gopls", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#gopls", "source": "devbox-search", "version": "0.16.1", "systems": { @@ -1009,41 +1007,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/fjkm2f7gjlwsflrwbd1sz4d3c1wpyvb5-gopls-0.16.1", + "path": "/nix/store/syc577acmyl666zd3fr7wnpdi8qvrkj5-gopls-0.16.1", "default": true } ], - "store_path": "/nix/store/fjkm2f7gjlwsflrwbd1sz4d3c1wpyvb5-gopls-0.16.1" + "store_path": "/nix/store/syc577acmyl666zd3fr7wnpdi8qvrkj5-gopls-0.16.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/pw30jfhyvzn848w34glnnd5nmddy3kjp-gopls-0.16.1", + "path": "/nix/store/1xm5wipnq2qm73qyxm0r0wl90gxwchxd-gopls-0.16.1", "default": true } ], - "store_path": "/nix/store/pw30jfhyvzn848w34glnnd5nmddy3kjp-gopls-0.16.1" + "store_path": "/nix/store/1xm5wipnq2qm73qyxm0r0wl90gxwchxd-gopls-0.16.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/h0mmsk9jg16g5i52p1g6k1xhdql3i6v2-gopls-0.16.1", + "path": "/nix/store/w7yx07za50ypm7x4bzjqzsqgfiib5vja-gopls-0.16.1", "default": true } ], - "store_path": "/nix/store/h0mmsk9jg16g5i52p1g6k1xhdql3i6v2-gopls-0.16.1" + "store_path": "/nix/store/w7yx07za50ypm7x4bzjqzsqgfiib5vja-gopls-0.16.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/szci8xkslrfxzz68aa2kz6la8qamxvrf-gopls-0.16.1", + "path": "/nix/store/acj40fqrk398wvf2mk8y5mzy985dh9j8-gopls-0.16.1", "default": true } ], - "store_path": "/nix/store/szci8xkslrfxzz68aa2kz6la8qamxvrf-gopls-0.16.1" + "store_path": "/nix/store/acj40fqrk398wvf2mk8y5mzy985dh9j8-gopls-0.16.1" } } }, @@ -1096,8 +1094,8 @@ } }, "jq@latest": { - "last_modified": "2024-08-14T02:42:29Z", - "resolved": "github:NixOS/nixpkgs/daf7bb95821b789db24fc1ac21f613db0c1bf2cb#jq", + "last_modified": "2024-09-01T03:39:50Z", + "resolved": "github:NixOS/nixpkgs/4a9443e2a4e06cbaff89056b5cdf6777c1fe5755#jq", "source": "devbox-search", "version": "1.7.1", "systems": { @@ -1105,131 +1103,131 @@ "outputs": [ { "name": "bin", - "path": "/nix/store/d9wxqi1f75zarf9wbiwlli3n0nbqhjd7-jq-1.7.1-bin", + "path": "/nix/store/a0qb3qz9nancghgi3z1734c5k79hzwgn-jq-1.7.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/p0cvcl9a2qxgvlbsmmx26blr9h6k9kwz-jq-1.7.1-man", + "path": "/nix/store/prp8b726pqh1ghw5idfdcyl0gnpry44q-jq-1.7.1-man", "default": true }, + { + "name": "doc", + "path": "/nix/store/a3v44plz7dixbrn9d7s69xhf87g6zrn0-jq-1.7.1-doc" + }, { "name": "lib", - "path": "/nix/store/6cd405yg737s18blzgvqzgyw96fl1x0p-jq-1.7.1-lib" + "path": "/nix/store/8n0r1ma1x5bvsr6n2kjzrhv8s7qyl9m1-jq-1.7.1-lib" }, { "name": "out", - "path": "/nix/store/p08mdq0qx0l3yzpnh17ll9dc47bwnvsv-jq-1.7.1" + "path": "/nix/store/20x8m876m0a3nas08c4gg7n1gcvw2nd1-jq-1.7.1" }, { "name": "dev", - "path": "/nix/store/0hz6vhnzw7mfiw3w20cnzq36c2y27n2f-jq-1.7.1-dev" - }, - { - "name": "doc", - "path": "/nix/store/9br49dwjvgdgj0x31c4pv4rq1canwa60-jq-1.7.1-doc" + "path": "/nix/store/4rcpydzqwdck396pwhkcpz1r1brlgli4-jq-1.7.1-dev" } ], - "store_path": "/nix/store/d9wxqi1f75zarf9wbiwlli3n0nbqhjd7-jq-1.7.1-bin" + "store_path": "/nix/store/a0qb3qz9nancghgi3z1734c5k79hzwgn-jq-1.7.1-bin" }, "aarch64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/hd0ngywgcjbn6jnmcnvm0d4lmg0s99l0-jq-1.7.1-bin", + "path": "/nix/store/855185vhfhrx065c0ad0s1qms3p8sg4c-jq-1.7.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/s4v2nw498ck1rwx2j90gks1ydix9ha3q-jq-1.7.1-man", + "path": "/nix/store/9w36a5zl1wz9mnk22np556mfdicfzyfa-jq-1.7.1-man", "default": true }, { - "name": "lib", - "path": "/nix/store/pp2063qjgqik7k5d6iwwpc8wn52jbick-jq-1.7.1-lib" + "name": "dev", + "path": "/nix/store/zxyzp99sm3nkf387h9xrq8kf4pkp7jw8-jq-1.7.1-dev" }, { - "name": "out", - "path": "/nix/store/fz0dc5yykkk2lh8d9kndsywqwl68aiy2-jq-1.7.1" + "name": "doc", + "path": "/nix/store/h8iz5m7aq09an64n3xjdrpvnskgqg0j5-jq-1.7.1-doc" }, { - "name": "dev", - "path": "/nix/store/w9h3rnm5cgzj90xhbg4p8ki4ns8s0ikl-jq-1.7.1-dev" + "name": "lib", + "path": "/nix/store/wkcx946q0kbpp0jb4rs3yq3ippiz5x83-jq-1.7.1-lib" }, { - "name": "doc", - "path": "/nix/store/a0c7dharmysjskkgp0zfkqp3z175b5pg-jq-1.7.1-doc" + "name": "out", + "path": "/nix/store/x5p64yy46c4g5a6788av88fr8nxpfmvl-jq-1.7.1" } ], - "store_path": "/nix/store/hd0ngywgcjbn6jnmcnvm0d4lmg0s99l0-jq-1.7.1-bin" + "store_path": "/nix/store/855185vhfhrx065c0ad0s1qms3p8sg4c-jq-1.7.1-bin" }, "x86_64-darwin": { "outputs": [ { "name": "bin", - "path": "/nix/store/kwkps100pvk6s8is34bqp1ycydbn2v7a-jq-1.7.1-bin", + "path": "/nix/store/wlzv4fh056vg7i9zkcggqvfcnargj9cg-jq-1.7.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/x46yqlsn1357pcpm7nvbrs863aak2pyb-jq-1.7.1-man", + "path": "/nix/store/5wsb8dxsvkhl3a4amaw3q237k31zm3xn-jq-1.7.1-man", "default": true }, - { - "name": "out", - "path": "/nix/store/5pl3gvxdjpvig6cvv77fzg8asaalx07d-jq-1.7.1" - }, { "name": "dev", - "path": "/nix/store/69z4pdr45l1ixix3bjnfyfy1wqfshmby-jq-1.7.1-dev" + "path": "/nix/store/1brda1qm783rz07f0a8y4mvs6m72v5ha-jq-1.7.1-dev" }, { "name": "doc", - "path": "/nix/store/6qlmlhii37fxxc36c54z5jb3xqjk2fp8-jq-1.7.1-doc" + "path": "/nix/store/rnpf9ypnakmigb5wd9cbzzqs7919bqna-jq-1.7.1-doc" }, { "name": "lib", - "path": "/nix/store/61l4nqvpfpx7ilk9xdjbn2y8knk72kv4-jq-1.7.1-lib" + "path": "/nix/store/3x3n94nvpfhb55n3039avj3cc13sqwmf-jq-1.7.1-lib" + }, + { + "name": "out", + "path": "/nix/store/5v08b5wxh9i1aqxr4j4v731phms6q4hj-jq-1.7.1" } ], - "store_path": "/nix/store/kwkps100pvk6s8is34bqp1ycydbn2v7a-jq-1.7.1-bin" + "store_path": "/nix/store/wlzv4fh056vg7i9zkcggqvfcnargj9cg-jq-1.7.1-bin" }, "x86_64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/yw7dn51dwbmw2pkx5fqhgadpzyv8f724-jq-1.7.1-bin", + "path": "/nix/store/kc1c2x2a14zsjwxlscajmc59nsz0wd1s-jq-1.7.1-bin", "default": true }, { "name": "man", - "path": "/nix/store/kvi43jy0kzsbkq4kmfgmrk6yw596bnb4-jq-1.7.1-man", + "path": "/nix/store/vdc00mcaj7xlk2f0sq7lyjgf0l3v9gvr-jq-1.7.1-man", "default": true }, - { - "name": "out", - "path": "/nix/store/h2lkpgx68cisqrka1x0arskiv39ngkm6-jq-1.7.1" - }, { "name": "dev", - "path": "/nix/store/wvcfmwz1hjs1fx4i182yfwwcvf01j1x8-jq-1.7.1-dev" + "path": "/nix/store/rwypdgzzxk9740bd01irnq708p62axah-jq-1.7.1-dev" }, { "name": "doc", - "path": "/nix/store/6gfjfw1akzpkhh3rfqbyyshpxaj9d1hw-jq-1.7.1-doc" + "path": "/nix/store/sifr3zf0rxisxs6ysvp3r2jds5pd22lj-jq-1.7.1-doc" }, { "name": "lib", - "path": "/nix/store/jn7ahwhssnkkg1m669x78qq163y3zyz3-jq-1.7.1-lib" + "path": "/nix/store/ssl0zcvizmyncqn3lwr3dmkb8j0x9fkn-jq-1.7.1-lib" + }, + { + "name": "out", + "path": "/nix/store/6mi0s34rv17dxd8bpv3vis709n230mfi-jq-1.7.1" } ], - "store_path": "/nix/store/yw7dn51dwbmw2pkx5fqhgadpzyv8f724-jq-1.7.1-bin" + "store_path": "/nix/store/kc1c2x2a14zsjwxlscajmc59nsz0wd1s-jq-1.7.1-bin" } } }, "markdownlint-cli2@latest": { - "last_modified": "2024-09-10T15:01:03Z", - "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#markdownlint-cli2", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#markdownlint-cli2", "source": "devbox-search", "version": "0.13.0", "systems": { @@ -1237,41 +1235,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/21sfamr4pjsa0ardwy3rj3rycr5fmzhj-markdownlint-cli2-0.13.0", + "path": "/nix/store/3c6jijgaj6sgiqgjc6k4mbjqjhadiiyi-markdownlint-cli2-0.13.0", "default": true } ], - "store_path": "/nix/store/21sfamr4pjsa0ardwy3rj3rycr5fmzhj-markdownlint-cli2-0.13.0" + "store_path": "/nix/store/3c6jijgaj6sgiqgjc6k4mbjqjhadiiyi-markdownlint-cli2-0.13.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/2qcya6yhm461yiyd2553nkr5sdq4za4f-markdownlint-cli2-0.13.0", + "path": "/nix/store/yxa8n8fzz331fzwn0hp3n8ngj3329b8v-markdownlint-cli2-0.13.0", "default": true } ], - "store_path": "/nix/store/2qcya6yhm461yiyd2553nkr5sdq4za4f-markdownlint-cli2-0.13.0" + "store_path": "/nix/store/yxa8n8fzz331fzwn0hp3n8ngj3329b8v-markdownlint-cli2-0.13.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/yk1cifxgvlv6cbrwilw7sm4dnlfkavsi-markdownlint-cli2-0.13.0", + "path": "/nix/store/nlfjx2b5gh1src39b7kc9x75rgxy27z2-markdownlint-cli2-0.13.0", "default": true } ], - "store_path": "/nix/store/yk1cifxgvlv6cbrwilw7sm4dnlfkavsi-markdownlint-cli2-0.13.0" + "store_path": "/nix/store/nlfjx2b5gh1src39b7kc9x75rgxy27z2-markdownlint-cli2-0.13.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/qyg3lr1xvfk1sfsbc78kvh3lv813gcv2-markdownlint-cli2-0.13.0", + "path": "/nix/store/1h33xxbc66amkss9v126ai79gd4sm03l-markdownlint-cli2-0.13.0", "default": true } ], - "store_path": "/nix/store/qyg3lr1xvfk1sfsbc78kvh3lv813gcv2-markdownlint-cli2-0.13.0" + "store_path": "/nix/store/1h33xxbc66amkss9v126ai79gd4sm03l-markdownlint-cli2-0.13.0" } } }, @@ -1372,230 +1370,230 @@ } }, "nodejs@latest": { - "last_modified": "2024-07-31T08:48:38Z", + "last_modified": "2024-08-31T10:12:23Z", "plugin_version": "0.0.2", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#nodejs_22", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#nodejs_22", "source": "devbox-search", - "version": "22.4.1", + "version": "22.6.0", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/z5whjsr6cnqw0ycnylwxmibfr7cdwxrb-nodejs-22.4.1", + "path": "/nix/store/h0lgf92wq4ip40d15mdah4548s5ck3lm-nodejs-22.6.0", "default": true }, { "name": "libv8", - "path": "/nix/store/vzhng0wwbm0r1p9rdq1kspm0j5b9x0va-nodejs-22.4.1-libv8" + "path": "/nix/store/cvyxkafalvlc8nymm9qghmd23lcsdq73-nodejs-22.6.0-libv8" } ], - "store_path": "/nix/store/z5whjsr6cnqw0ycnylwxmibfr7cdwxrb-nodejs-22.4.1" + "store_path": "/nix/store/h0lgf92wq4ip40d15mdah4548s5ck3lm-nodejs-22.6.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/2c0qimdjslxal42s4m10zj95f9r11l4l-nodejs-22.4.1", + "path": "/nix/store/i43skcppvpbprz429nlf6xh6x6gjk1w0-nodejs-22.6.0", "default": true }, { "name": "libv8", - "path": "/nix/store/aq9wvafy8svx7fgkm2w0yig3aqmkbqc8-nodejs-22.4.1-libv8" + "path": "/nix/store/1k0qb1hlhxzd45yirswq9ryzi8h2lzj7-nodejs-22.6.0-libv8" } ], - "store_path": "/nix/store/2c0qimdjslxal42s4m10zj95f9r11l4l-nodejs-22.4.1" + "store_path": "/nix/store/i43skcppvpbprz429nlf6xh6x6gjk1w0-nodejs-22.6.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/5l6flz4fpax64nzivg2r815pvllj2dml-nodejs-22.4.1", + "path": "/nix/store/vpz3z6kvx63xiwgippddkp62vi4h8g3z-nodejs-22.6.0", "default": true }, { "name": "libv8", - "path": "/nix/store/5wwl7dh3xmnrah4f98713i93m43d02a5-nodejs-22.4.1-libv8" + "path": "/nix/store/k9gallbn1nij77ryg2dix2iwrrky8c4g-nodejs-22.6.0-libv8" } ], - "store_path": "/nix/store/5l6flz4fpax64nzivg2r815pvllj2dml-nodejs-22.4.1" + "store_path": "/nix/store/vpz3z6kvx63xiwgippddkp62vi4h8g3z-nodejs-22.6.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/w8rva81863ls2ic3qc72mdzpzm0skyrm-nodejs-22.4.1", + "path": "/nix/store/gb9rkc33y0qxk8ps1wyflry1gmz2q9w8-nodejs-22.6.0", "default": true }, { "name": "libv8", - "path": "/nix/store/93cvy69p4g2mrbjjkj5dxh8zin047nl5-nodejs-22.4.1-libv8" + "path": "/nix/store/6v7dxcn0azh24swdv3745pfprhxcg4jr-nodejs-22.6.0-libv8" } ], - "store_path": "/nix/store/w8rva81863ls2ic3qc72mdzpzm0skyrm-nodejs-22.4.1" + "store_path": "/nix/store/gb9rkc33y0qxk8ps1wyflry1gmz2q9w8-nodejs-22.6.0" } } }, "oha@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#oha", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#oha", "source": "devbox-search", - "version": "1.4.5", + "version": "1.4.6", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/6zy9zgmv4fxwmpcjqlabj7rdixgkg69n-oha-1.4.5", + "path": "/nix/store/z1b280imwfkx7l7p0qasviaqjfi9xapy-oha-1.4.6", "default": true } ], - "store_path": "/nix/store/6zy9zgmv4fxwmpcjqlabj7rdixgkg69n-oha-1.4.5" + "store_path": "/nix/store/z1b280imwfkx7l7p0qasviaqjfi9xapy-oha-1.4.6" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/yfbcflzvnfyq0mvgdhnqvl35bp3balzw-oha-1.4.5", + "path": "/nix/store/4bsvh357cq759vx7w5xwij52h5nxsiih-oha-1.4.6", "default": true } ], - "store_path": "/nix/store/yfbcflzvnfyq0mvgdhnqvl35bp3balzw-oha-1.4.5" + "store_path": "/nix/store/4bsvh357cq759vx7w5xwij52h5nxsiih-oha-1.4.6" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/dhgdzc110fx081rhiq0xi7axysz601x9-oha-1.4.5", + "path": "/nix/store/zai34w521i7axqirkbpwzixzw37zf0vm-oha-1.4.6", "default": true } ], - "store_path": "/nix/store/dhgdzc110fx081rhiq0xi7axysz601x9-oha-1.4.5" + "store_path": "/nix/store/zai34w521i7axqirkbpwzixzw37zf0vm-oha-1.4.6" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/i4l0nw9cwhv1lj391yj5mjhm0x34ynmd-oha-1.4.5", + "path": "/nix/store/ph6amnlf1q6wr37iq6gw8ajqxxzgk2vc-oha-1.4.6", "default": true } ], - "store_path": "/nix/store/i4l0nw9cwhv1lj391yj5mjhm0x34ynmd-oha-1.4.5" + "store_path": "/nix/store/ph6amnlf1q6wr37iq6gw8ajqxxzgk2vc-oha-1.4.6" } } }, "postgresql@latest": { - "last_modified": "2024-08-07T17:07:38Z", + "last_modified": "2024-09-01T03:39:50Z", "plugin_version": "0.0.2", - "resolved": "github:NixOS/nixpkgs/61448af9e290aec3be42741abaef6a52ae31f6a4#postgresql", + "resolved": "github:NixOS/nixpkgs/4a9443e2a4e06cbaff89056b5cdf6777c1fe5755#postgresql", "source": "devbox-search", - "version": "15.7", + "version": "16.4", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/j063gkab0kj312p0r5wlwh8hhs3ivmmv-postgresql-15.7", + "path": "/nix/store/4xs8m764256kn9hr2m9nfv4nhzcs876i-postgresql-16.4", "default": true }, { "name": "man", - "path": "/nix/store/83zyb6qnvn85ilfb4g03yr8zjnc4kw5c-postgresql-15.7-man", + "path": "/nix/store/sga8v7kf4r50lp5pmylsavvrb2jn9b1i-postgresql-16.4-man", "default": true }, { - "name": "lib", - "path": "/nix/store/2lqmjj3nwingqsajwgwym4jjl1plqrxd-postgresql-15.7-lib" + "name": "doc", + "path": "/nix/store/vvq5zc10jg57cjky6xdc4z53bzzvv2k7-postgresql-16.4-doc" }, { - "name": "doc", - "path": "/nix/store/w98l40gkbw15cxjajs9wr9aaz1zqq8pv-postgresql-15.7-doc" + "name": "lib", + "path": "/nix/store/cq2lfr6m71ppmx4v8vdjj2qbv5r7f8bv-postgresql-16.4-lib" } ], - "store_path": "/nix/store/j063gkab0kj312p0r5wlwh8hhs3ivmmv-postgresql-15.7" + "store_path": "/nix/store/4xs8m764256kn9hr2m9nfv4nhzcs876i-postgresql-16.4" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/bm08f8k2gyndfq1mszvdm07jnmwr6nlf-postgresql-15.7", + "path": "/nix/store/aaphlis1flanaskw54vwzy0383dcinmm-postgresql-16.4", "default": true }, { "name": "man", - "path": "/nix/store/yd6qvs38zm55271230hfn4j0rd4029ca-postgresql-15.7-man", + "path": "/nix/store/wrscx3x20gv830ksy41xpyxpdab7g64q-postgresql-16.4-man", "default": true }, { "name": "debug", - "path": "/nix/store/m8kdahlx418v1x8pvbjja5zbl8ix4hff-postgresql-15.7-debug" + "path": "/nix/store/a2v8ji7rsmc7bhnw09wz4zbprfxd1bsh-postgresql-16.4-debug" }, { "name": "doc", - "path": "/nix/store/nw2ng4sh1vzih1rrfzlivd2c7ifh9zm2-postgresql-15.7-doc" + "path": "/nix/store/zvhdzrpdx0bdhj59bhcnr17csq4sm5ad-postgresql-16.4-doc" }, { "name": "lib", - "path": "/nix/store/fvs07mhcygpwy41jhw34kq7ghlcnv4nf-postgresql-15.7-lib" + "path": "/nix/store/w0m8i8x3gviq0jf986a298z9jx03alix-postgresql-16.4-lib" } ], - "store_path": "/nix/store/bm08f8k2gyndfq1mszvdm07jnmwr6nlf-postgresql-15.7" + "store_path": "/nix/store/aaphlis1flanaskw54vwzy0383dcinmm-postgresql-16.4" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/q4h9xjc0804xkcb0l7kxmp8rbqadlpg9-postgresql-15.7", + "path": "/nix/store/qcgnc3vfiq94fsn07y6hjf00rx6g7hx0-postgresql-16.4", "default": true }, { "name": "man", - "path": "/nix/store/22xrk7r5c4bh12wnmh4xssd1xk06b96l-postgresql-15.7-man", + "path": "/nix/store/xn3wqkgs1n2fxghczxasam7gc6ig0q42-postgresql-16.4-man", "default": true }, { "name": "doc", - "path": "/nix/store/4ynkh5cclgi0jcg42868z764irl32dx3-postgresql-15.7-doc" + "path": "/nix/store/h9gr53mxkd9dsabw2migk25bbd2bh6aw-postgresql-16.4-doc" }, { "name": "lib", - "path": "/nix/store/4bqzgkyvzc7c7i955raibmaqabffsi4y-postgresql-15.7-lib" + "path": "/nix/store/6rgrvr20s3q1cx6f5lamdqlxqvxd9263-postgresql-16.4-lib" } ], - "store_path": "/nix/store/q4h9xjc0804xkcb0l7kxmp8rbqadlpg9-postgresql-15.7" + "store_path": "/nix/store/qcgnc3vfiq94fsn07y6hjf00rx6g7hx0-postgresql-16.4" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/8gr5ybhmdkafii5idcg57p66nk1qd6sf-postgresql-15.7", + "path": "/nix/store/w11g7nvm4h1ph7kjq6c4cxna3yppsn2v-postgresql-16.4", "default": true }, { "name": "man", - "path": "/nix/store/0j6lskwq3imd8gdwy5rz9sjmn3c41qbc-postgresql-15.7-man", + "path": "/nix/store/dzm8il4j20xpcvqwf8z0rgnqhsq3wgdz-postgresql-16.4-man", "default": true }, { "name": "debug", - "path": "/nix/store/xw1fhj72fzrlkvapaf1spx19ixqm7394-postgresql-15.7-debug" + "path": "/nix/store/6f8bx4qwnlyk8f6x4551kkpzmcb8kbky-postgresql-16.4-debug" }, { "name": "doc", - "path": "/nix/store/01snq9n6ka7zkb4dp7k639mbb5p0v5qi-postgresql-15.7-doc" + "path": "/nix/store/zfx8r6g70bxm08vyz4px3xvhz6i2q1x1-postgresql-16.4-doc" }, { "name": "lib", - "path": "/nix/store/9xj29q1wf5wazv63hn5dxlwsp8k3h5lc-postgresql-15.7-lib" + "path": "/nix/store/ffdpyjcm8ax76fa5bxhqvjb4kyjj3i37-postgresql-16.4-lib" } ], - "store_path": "/nix/store/8gr5ybhmdkafii5idcg57p66nk1qd6sf-postgresql-15.7" + "store_path": "/nix/store/w11g7nvm4h1ph7kjq6c4cxna3yppsn2v-postgresql-16.4" } } }, "pre-commit@latest": { - "last_modified": "2024-08-01T02:09:53Z", - "resolved": "github:NixOS/nixpkgs/799bc8d7b16e6779f0105713e6794899133c4a38#pre-commit", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#pre-commit", "source": "devbox-search", "version": "3.7.1", "systems": { @@ -1603,169 +1601,169 @@ "outputs": [ { "name": "out", - "path": "/nix/store/dwn5fkjvzz2lld6qyk3schl67l3in214-pre-commit-3.7.1", + "path": "/nix/store/5g55f7d31vlac7c93chh86vqr3c5c3ri-pre-commit-3.7.1", "default": true }, { "name": "dist", - "path": "/nix/store/91m7xjkl860fd1jgpycf331p9h7id458-pre-commit-3.7.1-dist" + "path": "/nix/store/qy583afkz2dpkm002bwifhgd8x4858hj-pre-commit-3.7.1-dist" } ], - "store_path": "/nix/store/dwn5fkjvzz2lld6qyk3schl67l3in214-pre-commit-3.7.1" + "store_path": "/nix/store/5g55f7d31vlac7c93chh86vqr3c5c3ri-pre-commit-3.7.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/pknbsn6pxca776n1szqipgx133dwh0kh-pre-commit-3.7.1", + "path": "/nix/store/b1v22x5qiwrw9pvkcz5wn5wimgpwa33i-pre-commit-3.7.1", "default": true }, { "name": "dist", - "path": "/nix/store/z2p5svdhfckxga5sl1iyw4p0ldy2ngsq-pre-commit-3.7.1-dist" + "path": "/nix/store/x0gcrvjnj27alqh4ac1x3gkz71r5pjh3-pre-commit-3.7.1-dist" } ], - "store_path": "/nix/store/pknbsn6pxca776n1szqipgx133dwh0kh-pre-commit-3.7.1" + "store_path": "/nix/store/b1v22x5qiwrw9pvkcz5wn5wimgpwa33i-pre-commit-3.7.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/q54xmykk8h13b8gc9rq0s1cndnkcf54w-pre-commit-3.7.1", + "path": "/nix/store/1zic7nfa8cc952516r5aspsisp09bls8-pre-commit-3.7.1", "default": true }, { "name": "dist", - "path": "/nix/store/8wcznvmxcfamh2d5nr49lxqy3ydadxc6-pre-commit-3.7.1-dist" + "path": "/nix/store/jx0vz9353fi7k5nsmx1hbzbx45v4w1ag-pre-commit-3.7.1-dist" } ], - "store_path": "/nix/store/q54xmykk8h13b8gc9rq0s1cndnkcf54w-pre-commit-3.7.1" + "store_path": "/nix/store/1zic7nfa8cc952516r5aspsisp09bls8-pre-commit-3.7.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/pya1ln69fdz8x5hp2qz2sdg50xr5680x-pre-commit-3.7.1", + "path": "/nix/store/iqh91d6ca4vmf7xkdjf1bm19vx951d6x-pre-commit-3.7.1", "default": true }, { "name": "dist", - "path": "/nix/store/r2xjg3kwiin09yqb953jzn0jplikzphx-pre-commit-3.7.1-dist" + "path": "/nix/store/ayz1q36zrplb8qnl41swbcz4ni0f5jb4-pre-commit-3.7.1-dist" } ], - "store_path": "/nix/store/pya1ln69fdz8x5hp2qz2sdg50xr5680x-pre-commit-3.7.1" + "store_path": "/nix/store/iqh91d6ca4vmf7xkdjf1bm19vx951d6x-pre-commit-3.7.1" } } }, "python@latest": { - "last_modified": "2024-07-31T08:48:38Z", + "last_modified": "2024-08-31T10:12:23Z", "plugin_version": "0.0.4", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#python3", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#python3", "source": "devbox-search", - "version": "3.12.4", + "version": "3.12.5", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/1sgajx2r3bkriyxzwsahhva63p08pmac-python3-3.12.4", + "path": "/nix/store/75j38g8ii1nqkmpf6sdlj3s5dyah3gas-python3-3.12.5", "default": true } ], - "store_path": "/nix/store/1sgajx2r3bkriyxzwsahhva63p08pmac-python3-3.12.4" + "store_path": "/nix/store/75j38g8ii1nqkmpf6sdlj3s5dyah3gas-python3-3.12.5" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/jms4z7lzzwnv6gv3y0795365haicmh8m-python3-3.12.4", + "path": "/nix/store/ajjwc8k8sk3ksrl3dq4fsg83m1j8n8s3-python3-3.12.5", "default": true }, { "name": "debug", - "path": "/nix/store/rshmxwibmxqvf94w3ld05yqnsijrmk4m-python3-3.12.4-debug" + "path": "/nix/store/9qvs74485a1v5255w2ps0xf4rxww6w89-python3-3.12.5-debug" } ], - "store_path": "/nix/store/jms4z7lzzwnv6gv3y0795365haicmh8m-python3-3.12.4" + "store_path": "/nix/store/ajjwc8k8sk3ksrl3dq4fsg83m1j8n8s3-python3-3.12.5" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/dh1i1387ibdzw0ala5rkl3s3ylf8i8pa-python3-3.12.4", + "path": "/nix/store/rv3rj95fxv57c7qwgl43qa7n0fabdy0a-python3-3.12.5", "default": true } ], - "store_path": "/nix/store/dh1i1387ibdzw0ala5rkl3s3ylf8i8pa-python3-3.12.4" + "store_path": "/nix/store/rv3rj95fxv57c7qwgl43qa7n0fabdy0a-python3-3.12.5" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/l014xp1qxdl6gim3zc0jv3mpxhbp346s-python3-3.12.4", + "path": "/nix/store/pgb120fb7srbh418v4i2a70aq1w9dawd-python3-3.12.5", "default": true }, { "name": "debug", - "path": "/nix/store/myg0p2vf2cj2jsb663qswnygvgn54kbc-python3-3.12.4-debug" + "path": "/nix/store/4ws5lqhgsxdpfb924n49ma6ll7i8x0hf-python3-3.12.5-debug" } ], - "store_path": "/nix/store/l014xp1qxdl6gim3zc0jv3mpxhbp346s-python3-3.12.4" + "store_path": "/nix/store/pgb120fb7srbh418v4i2a70aq1w9dawd-python3-3.12.5" } } }, "rubocop@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#rubocop", + "last_modified": "2024-08-31T19:22:30Z", + "resolved": "github:NixOS/nixpkgs/282e35e0762d64800d78e33ff225704baf9e5216#rubocop", "source": "devbox-search", - "version": "1.62.1", + "version": "1.65.1", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/3f2d4498dwyja9zalgkx59i6xrd7m16i-ruby3.1-rubocop-1.62.1", + "path": "/nix/store/f7mjcpgy4kac87ygnwrc8zss4ps67x1w-ruby3.3-rubocop-1.65.1", "default": true } ], - "store_path": "/nix/store/3f2d4498dwyja9zalgkx59i6xrd7m16i-ruby3.1-rubocop-1.62.1" + "store_path": "/nix/store/f7mjcpgy4kac87ygnwrc8zss4ps67x1w-ruby3.3-rubocop-1.65.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/3df504w1r5g3zk51hq83699wc52qvz2k-ruby3.1-rubocop-1.62.1", + "path": "/nix/store/ls69blqq10hqadgn99v02qhdzfs99jkk-ruby3.3-rubocop-1.65.1", "default": true } ], - "store_path": "/nix/store/3df504w1r5g3zk51hq83699wc52qvz2k-ruby3.1-rubocop-1.62.1" + "store_path": "/nix/store/ls69blqq10hqadgn99v02qhdzfs99jkk-ruby3.3-rubocop-1.65.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/d4j25l1szpf379pgg84c5bf18fzfrwkq-ruby3.1-rubocop-1.62.1", + "path": "/nix/store/xri9m3wj2zq4bwsszc9bkcn38664nl66-ruby3.3-rubocop-1.65.1", "default": true } ], - "store_path": "/nix/store/d4j25l1szpf379pgg84c5bf18fzfrwkq-ruby3.1-rubocop-1.62.1" + "store_path": "/nix/store/xri9m3wj2zq4bwsszc9bkcn38664nl66-ruby3.3-rubocop-1.65.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/mq6migs5hpjarlrayzhd4mx4vqr5rq8p-ruby3.1-rubocop-1.62.1", + "path": "/nix/store/358ycw2rdmyr26rxsxsszhsyfmxps29b-ruby3.3-rubocop-1.65.1", "default": true } ], - "store_path": "/nix/store/mq6migs5hpjarlrayzhd4mx4vqr5rq8p-ruby3.1-rubocop-1.62.1" + "store_path": "/nix/store/358ycw2rdmyr26rxsxsszhsyfmxps29b-ruby3.3-rubocop-1.65.1" } } }, "ruby@latest": { - "last_modified": "2024-08-14T11:41:26Z", + "last_modified": "2024-09-10T15:01:03Z", "plugin_version": "0.0.2", - "resolved": "github:NixOS/nixpkgs/0cb2fd7c59fed0cd82ef858cbcbdb552b9a33465#ruby_3_3", + "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#ruby", "source": "devbox-search", "version": "3.3.4", "systems": { @@ -1773,63 +1771,63 @@ "outputs": [ { "name": "out", - "path": "/nix/store/9bxxr0h0fvgv6sf3w7qrbirv66psmrp0-ruby-3.3.4", + "path": "/nix/store/zsngi7cq7asxc6kb0lcmbyydasfrmai6-ruby-3.3.4", "default": true }, { "name": "devdoc", - "path": "/nix/store/rw78r69d76fpjabhv0yqvq065i7hk7x8-ruby-3.3.4-devdoc" + "path": "/nix/store/c0zmjmfw5pp00slwy842bd8zzy74pava-ruby-3.3.4-devdoc" } ], - "store_path": "/nix/store/9bxxr0h0fvgv6sf3w7qrbirv66psmrp0-ruby-3.3.4" + "store_path": "/nix/store/zsngi7cq7asxc6kb0lcmbyydasfrmai6-ruby-3.3.4" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/pznpsnxp0z3rymdz9sp7fgf330apsp4h-ruby-3.3.4", + "path": "/nix/store/8vri7d6b75gspvnfrdpqnagfi5bbvis9-ruby-3.3.4", "default": true }, { "name": "devdoc", - "path": "/nix/store/whgzvyjfnaaxjlbhjaa02rm55794ilq7-ruby-3.3.4-devdoc" + "path": "/nix/store/bfrwr1n6pxd3hz31h589j317g290p0r5-ruby-3.3.4-devdoc" } ], - "store_path": "/nix/store/pznpsnxp0z3rymdz9sp7fgf330apsp4h-ruby-3.3.4" + "store_path": "/nix/store/8vri7d6b75gspvnfrdpqnagfi5bbvis9-ruby-3.3.4" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/xd977g6j6gbr3pwlix9030v3s48z22cv-ruby-3.3.4", + "path": "/nix/store/hksac0axpx3savrgmy75rid3smv0d9nj-ruby-3.3.4", "default": true }, { "name": "devdoc", - "path": "/nix/store/d1fywywndcqxiih82ysv3pm3mmd9n9fa-ruby-3.3.4-devdoc" + "path": "/nix/store/3564jxjgp9dk8vzjwb7i3fpzkpw74z3m-ruby-3.3.4-devdoc" } ], - "store_path": "/nix/store/xd977g6j6gbr3pwlix9030v3s48z22cv-ruby-3.3.4" + "store_path": "/nix/store/hksac0axpx3savrgmy75rid3smv0d9nj-ruby-3.3.4" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/bx1vnac35l0vd39390v5ddz73mjf20r7-ruby-3.3.4", + "path": "/nix/store/2vg3asd3rmhng3wqvp781a1q5dhrr4yn-ruby-3.3.4", "default": true }, { "name": "devdoc", - "path": "/nix/store/kg19xgwfiz7yg329jsy85hs3mhmlii3b-ruby-3.3.4-devdoc" + "path": "/nix/store/2vgrf69lcz8ipawxipza3w7gsqdnbbnk-ruby-3.3.4-devdoc" } ], - "store_path": "/nix/store/bx1vnac35l0vd39390v5ddz73mjf20r7-ruby-3.3.4" + "store_path": "/nix/store/2vg3asd3rmhng3wqvp781a1q5dhrr4yn-ruby-3.3.4" } } }, "rubyPackages.solargraph@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#rubyPackages.solargraph", + "last_modified": "2024-08-31T19:22:30Z", + "resolved": "github:NixOS/nixpkgs/282e35e0762d64800d78e33ff225704baf9e5216#rubyPackages.solargraph", "source": "devbox-search", "version": "0.50.0", "systems": { @@ -1837,47 +1835,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/7nyxvcs0hbchwnn74ymijw17kvg89nhv-ruby3.1-solargraph-0.50.0", + "path": "/nix/store/b7smjfdy3wjfsfd8x2hl14kv1mbjxaxh-ruby3.3-solargraph-0.50.0", "default": true } ], - "store_path": "/nix/store/7nyxvcs0hbchwnn74ymijw17kvg89nhv-ruby3.1-solargraph-0.50.0" + "store_path": "/nix/store/b7smjfdy3wjfsfd8x2hl14kv1mbjxaxh-ruby3.3-solargraph-0.50.0" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/hpxz314jqzz4ffn2xi4rbw59py4nz7cm-ruby3.1-solargraph-0.50.0", + "path": "/nix/store/1zjr5vd4r3mycf7yvgngd39kx7hn22f3-ruby3.3-solargraph-0.50.0", "default": true } ], - "store_path": "/nix/store/hpxz314jqzz4ffn2xi4rbw59py4nz7cm-ruby3.1-solargraph-0.50.0" + "store_path": "/nix/store/1zjr5vd4r3mycf7yvgngd39kx7hn22f3-ruby3.3-solargraph-0.50.0" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/2gvkxsz1wwacr4b4ciw6mw4cq4aas0jy-ruby3.1-solargraph-0.50.0", + "path": "/nix/store/lsgg4jbldhzizvzpw3wc78pqa7m2idp7-ruby3.3-solargraph-0.50.0", "default": true } ], - "store_path": "/nix/store/2gvkxsz1wwacr4b4ciw6mw4cq4aas0jy-ruby3.1-solargraph-0.50.0" + "store_path": "/nix/store/lsgg4jbldhzizvzpw3wc78pqa7m2idp7-ruby3.3-solargraph-0.50.0" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/92g8hk03pq9k0i6mppb5s3pn2hs76anb-ruby3.1-solargraph-0.50.0", + "path": "/nix/store/3gswm57kl2ygzw6npw5nnzd5bgcgnnyq-ruby3.3-solargraph-0.50.0", "default": true } ], - "store_path": "/nix/store/92g8hk03pq9k0i6mppb5s3pn2hs76anb-ruby3.1-solargraph-0.50.0" + "store_path": "/nix/store/3gswm57kl2ygzw6npw5nnzd5bgcgnnyq-ruby3.3-solargraph-0.50.0" } } }, "shellcheck@0.10.0": { - "last_modified": "2024-08-01T02:09:53Z", - "resolved": "github:NixOS/nixpkgs/799bc8d7b16e6779f0105713e6794899133c4a38#shellcheck", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#shellcheck", "source": "devbox-search", "version": "0.10.0", "systems": { @@ -1885,103 +1883,103 @@ "outputs": [ { "name": "bin", - "path": "/nix/store/k2vfa0fkx8fjxqfha1q6mszxvgfnmdiy-shellcheck-0.10.0-bin", + "path": "/nix/store/xzp9ys92l1d18brv2q183r3i12wjj61w-shellcheck-0.10.0-bin", "default": true }, { "name": "man", - "path": "/nix/store/iwzay0ybpvnbmgghd745gvkxzfjmfciy-shellcheck-0.10.0-man", + "path": "/nix/store/fylwfi4p0h8xx2w4pp37rwrx5di3hsbs-shellcheck-0.10.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/21zqgs561lzygmcjrs0n8yyannf6hils-shellcheck-0.10.0-doc", + "path": "/nix/store/f3j35ci3f1n6ic979g1zmf721iv99h8f-shellcheck-0.10.0-doc", "default": true }, { "name": "out", - "path": "/nix/store/xrvyhw3szxbp2ay76jpc1qj7ngz6jpn6-shellcheck-0.10.0" + "path": "/nix/store/pxi0dvf9mgam5i7szl9scaydwcwaqpgh-shellcheck-0.10.0" } ], - "store_path": "/nix/store/k2vfa0fkx8fjxqfha1q6mszxvgfnmdiy-shellcheck-0.10.0-bin" + "store_path": "/nix/store/xzp9ys92l1d18brv2q183r3i12wjj61w-shellcheck-0.10.0-bin" }, "aarch64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/ywmf20zyjivrhw97v7s1fi51y76fs5jr-shellcheck-0.10.0-bin", + "path": "/nix/store/gs33kdch6p8icbphmi4jd5hfjpv57hxs-shellcheck-0.10.0-bin", "default": true }, { "name": "man", - "path": "/nix/store/qrhgwpj48flw028gcx9sqf5afd5xqspa-shellcheck-0.10.0-man", + "path": "/nix/store/hla6fpjvcmiwf3nzhpk0flgakhzbbcl9-shellcheck-0.10.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/hvi4b8pvirrbzxam001bzgswpayvfvsy-shellcheck-0.10.0-doc", + "path": "/nix/store/sj0g5c0kpff988drn383rx2mrxnqh4zf-shellcheck-0.10.0-doc", "default": true }, { "name": "out", - "path": "/nix/store/6znz4zkzds0zb7dvi6n85amdhypb95zc-shellcheck-0.10.0" + "path": "/nix/store/rbdfq1nz2f66l0h2mzs1zbk3ipm7j2mx-shellcheck-0.10.0" } ], - "store_path": "/nix/store/ywmf20zyjivrhw97v7s1fi51y76fs5jr-shellcheck-0.10.0-bin" + "store_path": "/nix/store/gs33kdch6p8icbphmi4jd5hfjpv57hxs-shellcheck-0.10.0-bin" }, "x86_64-darwin": { "outputs": [ { "name": "bin", - "path": "/nix/store/slzar2ld6c9js9ffvr4v3yplw80r8qf5-shellcheck-0.10.0-bin", + "path": "/nix/store/232fynanrdfwvmdbw6d4qfx9cjvjpnnr-shellcheck-0.10.0-bin", "default": true }, { "name": "man", - "path": "/nix/store/jgj5m5dsvihrvbr4iwrc59zga22vgpg9-shellcheck-0.10.0-man", + "path": "/nix/store/rghkf2jn301p14dm63m6vp529pm643n4-shellcheck-0.10.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/brs51ypipb98038aiqnjh48daxvhzdpk-shellcheck-0.10.0-doc", + "path": "/nix/store/2955bf7b30q8h22pfrfkp5s9qrz20qaa-shellcheck-0.10.0-doc", "default": true }, { "name": "out", - "path": "/nix/store/j327y2hh2wzgfb7bjsfwnr74sxrq9nma-shellcheck-0.10.0" + "path": "/nix/store/nfdbib0knf1zc3k3hynrlrypvqq0ibnz-shellcheck-0.10.0" } ], - "store_path": "/nix/store/slzar2ld6c9js9ffvr4v3yplw80r8qf5-shellcheck-0.10.0-bin" + "store_path": "/nix/store/232fynanrdfwvmdbw6d4qfx9cjvjpnnr-shellcheck-0.10.0-bin" }, "x86_64-linux": { "outputs": [ { "name": "bin", - "path": "/nix/store/w1l0q93lwkzf64d61005qfi2l56aj7p5-shellcheck-0.10.0-bin", + "path": "/nix/store/6k8wlg7702w31r892b6wxzbzk8hqr4kb-shellcheck-0.10.0-bin", "default": true }, { "name": "man", - "path": "/nix/store/1sh87f3lgw4b1rwcsnnvrnampn4x4lph-shellcheck-0.10.0-man", + "path": "/nix/store/r1p28g8nw8aivf82sxd0pp28r4xxiama-shellcheck-0.10.0-man", "default": true }, { "name": "doc", - "path": "/nix/store/1x55vy8qhagqbq5rxp9hr8h8ynvixhhy-shellcheck-0.10.0-doc", + "path": "/nix/store/6cl4n0dg1lfa8x18izk30v5bpqhf79z8-shellcheck-0.10.0-doc", "default": true }, { "name": "out", - "path": "/nix/store/wpl60jck2cryl0y1id2dbjnp18qjjg90-shellcheck-0.10.0" + "path": "/nix/store/775kf44w5x6yg67rgcpdf6f6b8sj840a-shellcheck-0.10.0" } ], - "store_path": "/nix/store/w1l0q93lwkzf64d61005qfi2l56aj7p5-shellcheck-0.10.0-bin" + "store_path": "/nix/store/6k8wlg7702w31r892b6wxzbzk8hqr4kb-shellcheck-0.10.0-bin" } } }, "swagger-cli@latest": { - "last_modified": "2024-08-10T11:55:14Z", - "resolved": "github:NixOS/nixpkgs/8987be1fef03440514ebf3b0b60e0c44fc13eb6c#swagger-cli", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#swagger-cli", "source": "devbox-search", "version": "4.0.4", "systems": { @@ -1989,47 +1987,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/31gdx5s967g0xnsa5x3jqfrrb2zjdzhi-swagger-cli-4.0.4", + "path": "/nix/store/l97bajrw465kq0ylnkhwkf22myx5wqi2-swagger-cli-4.0.4", "default": true } ], - "store_path": "/nix/store/31gdx5s967g0xnsa5x3jqfrrb2zjdzhi-swagger-cli-4.0.4" + "store_path": "/nix/store/l97bajrw465kq0ylnkhwkf22myx5wqi2-swagger-cli-4.0.4" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/7nyggm836y6zsqwswvrnr2065rz8a3gq-swagger-cli-4.0.4", + "path": "/nix/store/4m3kq0l9xfj8qxh8qfwijf5a95jx0jgw-swagger-cli-4.0.4", "default": true } ], - "store_path": "/nix/store/7nyggm836y6zsqwswvrnr2065rz8a3gq-swagger-cli-4.0.4" + "store_path": "/nix/store/4m3kq0l9xfj8qxh8qfwijf5a95jx0jgw-swagger-cli-4.0.4" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/3kxl503ragrwi3f0nfi83g9sf4pbkh6s-swagger-cli-4.0.4", + "path": "/nix/store/ff5wx2b7dswhhsqqkza6xv5npzh927x4-swagger-cli-4.0.4", "default": true } ], - "store_path": "/nix/store/3kxl503ragrwi3f0nfi83g9sf4pbkh6s-swagger-cli-4.0.4" + "store_path": "/nix/store/ff5wx2b7dswhhsqqkza6xv5npzh927x4-swagger-cli-4.0.4" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/cr7d8vhysr4n13fks7q4bmhjz9nglddc-swagger-cli-4.0.4", + "path": "/nix/store/db6z4kinn17pj73gybhxjq25lm7mhmsy-swagger-cli-4.0.4", "default": true } ], - "store_path": "/nix/store/cr7d8vhysr4n13fks7q4bmhjz9nglddc-swagger-cli-4.0.4" + "store_path": "/nix/store/db6z4kinn17pj73gybhxjq25lm7mhmsy-swagger-cli-4.0.4" } } }, "temurin-bin-21@latest": { - "last_modified": "2024-08-14T11:41:26Z", - "resolved": "github:NixOS/nixpkgs/0cb2fd7c59fed0cd82ef858cbcbdb552b9a33465#temurin-bin-21", + "last_modified": "2024-09-13T05:52:00Z", + "resolved": "github:NixOS/nixpkgs/673d99f1406cb09b8eb6feab4743ebdf70046557#temurin-bin-21", "source": "devbox-search", "version": "21.0.3", "systems": { @@ -2037,47 +2035,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/ji3g2rn3vq0hiagafmfd68sy1753sjla-temurin-bin-21.0.3", + "path": "/nix/store/l06d8w0l9h27pxzhygixd478dz0098z0-temurin-bin-21.0.3", "default": true } ], - "store_path": "/nix/store/ji3g2rn3vq0hiagafmfd68sy1753sjla-temurin-bin-21.0.3" + "store_path": "/nix/store/l06d8w0l9h27pxzhygixd478dz0098z0-temurin-bin-21.0.3" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/alax27n8mir195mkjy2cv5q9hlvhvv04-temurin-bin-21.0.3", + "path": "/nix/store/fzkfqws93k92sw66cfp36d7y8mcmlr22-temurin-bin-21.0.3", "default": true } ], - "store_path": "/nix/store/alax27n8mir195mkjy2cv5q9hlvhvv04-temurin-bin-21.0.3" + "store_path": "/nix/store/fzkfqws93k92sw66cfp36d7y8mcmlr22-temurin-bin-21.0.3" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/mfj5d8wdk8mk42r886s1ayvm9j953xhl-temurin-bin-21.0.3", + "path": "/nix/store/wd6swl5iax4im490lip7n1f0wk0zii7s-temurin-bin-21.0.3", "default": true } ], - "store_path": "/nix/store/mfj5d8wdk8mk42r886s1ayvm9j953xhl-temurin-bin-21.0.3" + "store_path": "/nix/store/wd6swl5iax4im490lip7n1f0wk0zii7s-temurin-bin-21.0.3" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/f8h1clx76p6na7fv9j6625fsxbdz05rw-temurin-bin-21.0.3", + "path": "/nix/store/phhr8nfj0c6wnh43g0b35z7awflkg39z-temurin-bin-21.0.3", "default": true } ], - "store_path": "/nix/store/f8h1clx76p6na7fv9j6625fsxbdz05rw-temurin-bin-21.0.3" + "store_path": "/nix/store/phhr8nfj0c6wnh43g0b35z7awflkg39z-temurin-bin-21.0.3" } } }, "which@latest": { - "last_modified": "2024-07-31T08:48:38Z", - "resolved": "github:NixOS/nixpkgs/c3392ad349a5227f4a3464dce87bcc5046692fce#which", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#which", "source": "devbox-search", "version": "2.21", "systems": { @@ -2085,47 +2083,47 @@ "outputs": [ { "name": "out", - "path": "/nix/store/ghsv4i7adj4bys0a232c41amjl0n5a7n-which-2.21", + "path": "/nix/store/v4ajfj4wpgj153zwbzzv8q2cc1qq8ck6-which-2.21", "default": true } ], - "store_path": "/nix/store/ghsv4i7adj4bys0a232c41amjl0n5a7n-which-2.21" + "store_path": "/nix/store/v4ajfj4wpgj153zwbzzv8q2cc1qq8ck6-which-2.21" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/1x0yih053iq1m4ckn62nnim366n50m3f-which-2.21", + "path": "/nix/store/yqbky0d614vmyilw8ilkxi11dzdzh4vg-which-2.21", "default": true } ], - "store_path": "/nix/store/1x0yih053iq1m4ckn62nnim366n50m3f-which-2.21" + "store_path": "/nix/store/yqbky0d614vmyilw8ilkxi11dzdzh4vg-which-2.21" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/wlmysyfiw68qgrqvbq5n0g5zm0cm915f-which-2.21", + "path": "/nix/store/8yh1x2cj5f0pi6hf60ym9h0z0vq9yhnj-which-2.21", "default": true } ], - "store_path": "/nix/store/wlmysyfiw68qgrqvbq5n0g5zm0cm915f-which-2.21" + "store_path": "/nix/store/8yh1x2cj5f0pi6hf60ym9h0z0vq9yhnj-which-2.21" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/w40ik54r0nv4fjqwiqs81ixpmdf2xdfw-which-2.21", + "path": "/nix/store/flaw4yljz1856ygjkkd344y81ykjbzr8-which-2.21", "default": true } ], - "store_path": "/nix/store/w40ik54r0nv4fjqwiqs81ixpmdf2xdfw-which-2.21" + "store_path": "/nix/store/flaw4yljz1856ygjkkd344y81ykjbzr8-which-2.21" } } }, "yq-go@4.44.3": { - "last_modified": "2024-09-10T15:01:03Z", - "resolved": "github:NixOS/nixpkgs/5ed627539ac84809c78b2dd6d26a5cebeb5ae269#yq-go", + "last_modified": "2024-08-31T10:12:23Z", + "resolved": "github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe#yq-go", "source": "devbox-search", "version": "4.44.3", "systems": { @@ -2133,41 +2131,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/l4yd5ml8bq3k7kgx09v11q735ifafc2z-yq-go-4.44.3", + "path": "/nix/store/jppigm7xxrz14z5qbp70qwk63rqkzlr7-yq-go-4.44.3", "default": true } ], - "store_path": "/nix/store/l4yd5ml8bq3k7kgx09v11q735ifafc2z-yq-go-4.44.3" + "store_path": "/nix/store/jppigm7xxrz14z5qbp70qwk63rqkzlr7-yq-go-4.44.3" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/v560a3n62j1vxmcajhmjm0m3g706gj1r-yq-go-4.44.3", + "path": "/nix/store/h1p18ha0hkgf0rwc87cc51nz2m69004s-yq-go-4.44.3", "default": true } ], - "store_path": "/nix/store/v560a3n62j1vxmcajhmjm0m3g706gj1r-yq-go-4.44.3" + "store_path": "/nix/store/h1p18ha0hkgf0rwc87cc51nz2m69004s-yq-go-4.44.3" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/51y79dql9azy10mr82dbvp30y29lp3pn-yq-go-4.44.3", + "path": "/nix/store/rfymy667lx1032cxdwyzslxia511dxz7-yq-go-4.44.3", "default": true } ], - "store_path": "/nix/store/51y79dql9azy10mr82dbvp30y29lp3pn-yq-go-4.44.3" + "store_path": "/nix/store/rfymy667lx1032cxdwyzslxia511dxz7-yq-go-4.44.3" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/3c1gvwg7nkvwvidnvng8fj31pprkhhva-yq-go-4.44.3", + "path": "/nix/store/kb2z2ih2yvlpy9p4mx4sqdicjj4p2isw-yq-go-4.44.3", "default": true } ], - "store_path": "/nix/store/3c1gvwg7nkvwvidnvng8fj31pprkhhva-yq-go-4.44.3" + "store_path": "/nix/store/kb2z2ih2yvlpy9p4mx4sqdicjj4p2isw-yq-go-4.44.3" } } } From fc6a724e3e443c0b7891aa67920d8bc75d88d005 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 17 Oct 2024 14:30:44 +0200 Subject: [PATCH 31/39] Update publicapi routes --- ci/autoscaler/scripts/vars.source.sh | 11 ----------- src/autoscaler/Makefile | 2 +- src/autoscaler/build-extension-file.sh | 11 +++++++++-- src/autoscaler/mta.tpl.yaml | 4 ++-- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/ci/autoscaler/scripts/vars.source.sh b/ci/autoscaler/scripts/vars.source.sh index b7b925a2ac..b1f58cfeb7 100644 --- a/ci/autoscaler/scripts/vars.source.sh +++ b/ci/autoscaler/scripts/vars.source.sh @@ -67,17 +67,6 @@ export SYSTEM_DOMAIN="${SYSTEM_DOMAIN:-"autoscaler.app-runtime-interfaces.ci.clo debug "SYSTEM_DOMAIN: ${SYSTEM_DOMAIN}" system_domain="${SYSTEM_DOMAIN}" -# Configure cloudfoundry app variables -export METRICSFORWARDER_APPNAME="${METRICSFORWARDER_APPNAME:-"${DEPLOYMENT_NAME}-metricsforwarder"}" -debug "METRICSFORWARDER_APPNAME: ${METRICSFORWARDER_APPNAME}" -log "set up vars: METRICSFORWRDER_APPNAME=${METRICSFORWARDER_APPNAME}" -metricsforwarder_appname="${METRICSFORWARDER_APPNAME}" - -export METRICSFORWARDER_HOST="${METRICSFORWARDER_HOST:-"${METRICSFORWARDER_APPNAME}.${SYSTEM_DOMAIN}"}" -debug "METRICSFORWARDER_HOST: ${METRICSFORWARDER_HOST}" -log "set up vars: METRICSFORWARDER_HOST=${METRICSFORWARDER_HOST}" -metricsforwarder_host="${METRICSFORWARDER_HOST}" - BBL_STATE_PATH="${BBL_STATE_PATH:-$( realpath -e "${root_dir}/../app-autoscaler-env-bbl-state/bbl-state" 2> /dev/null || echo "${root_dir}/../bbl-state/bbl-state" )}" BBL_STATE_PATH="$(realpath -e "${BBL_STATE_PATH}" || echo "ERR_invalid_state_path" )" export BBL_STATE_PATH diff --git a/src/autoscaler/Makefile b/src/autoscaler/Makefile index 3c2194810b..6bd8864afc 100644 --- a/src/autoscaler/Makefile +++ b/src/autoscaler/Makefile @@ -177,7 +177,7 @@ mta-build: mta-build-clean sed -i 's/VERSION/$(VERSION)/g' mta.yaml mkdir -p $(DEST) mbt build - @mv mta_archives/com.github.cloudfoundry.app-autoscaler-release_$(VERSION).mtar $(DEST)/app-autoscaler-release-v$(VERSION).mtar + @mv ${PWD}/mta_archives/com.github.cloudfoundry.app-autoscaler-release_$(VERSION).mtar $(DEST)/app-autoscaler-release-v$(VERSION).mtar mta-build-clean: rm -rf mta_archives diff --git a/src/autoscaler/build-extension-file.sh b/src/autoscaler/build-extension-file.sh index 90ec9f1644..7d13b98124 100755 --- a/src/autoscaler/build-extension-file.sh +++ b/src/autoscaler/build-extension-file.sh @@ -20,7 +20,10 @@ export POSTGRES_ADDRESS="${DEPLOYMENT_NAME}-postgres.tcp.${SYSTEM_DOMAIN}" export POSTGRES_EXTERNAL_PORT="${PR_NUMBER:-5432}" export METRICSFORWARDER_HEALTH_PASSWORD="$(credhub get -n /bosh-autoscaler/${DEPLOYMENT_NAME}/autoscaler_metricsforwarder_health_password --quiet)" -export METRICSFORWARDER_APPNAME="${METRICSFORWARDER_APPNAME:-"${DEPLOYMENT_NAME}-metricsforwarder"}" + +export METRICSFORWARDER_HOST="${METRICSFORWARDER_HOST:-"${DEPLOYMENT_NAME}-metricsforwarder"}" +export PUBLICAPISERVER_HOST="${PUBLICAPISERVER_HOST:-"${DEPLOYMENT_NAME}"}" +export SERVICEBROKER_HOST="${SERVICEBROKER_HOST:-"${DEPLOYMENT_NAME}servicebroker"}" export POLICY_DB_PASSWORD="$(credhub get -n /bosh-autoscaler/${DEPLOYMENT_NAME}/database_password --quiet)" export POLICY_DB_SERVER_CA="$(credhub get -n /bosh-autoscaler/${DEPLOYMENT_NAME}/postgres_server --key ca --quiet )" @@ -45,10 +48,14 @@ modules: - name: syslog-client parameters: routes: - - route: ${METRICSFORWARDER_APPNAME}.\${default-domain} + - route: ${METRICSFORWARDER_HOST}.\${default-domain} + - name: publicapiserver parameters: instances: 0 + routes: + - route: ${PUBLICAPISERVER_HOST}.\${default-domain} + - route: ${SERVICEBROKER_HOST}.\${default-domain} resources: - name: metricsforwarder-config diff --git a/src/autoscaler/mta.tpl.yaml b/src/autoscaler/mta.tpl.yaml index 608f4e72ac..9f7f46dd74 100644 --- a/src/autoscaler/mta.tpl.yaml +++ b/src/autoscaler/mta.tpl.yaml @@ -27,7 +27,7 @@ modules: build-parameters: builder: custom commands: - - make vendor + - make clean vendor - name: publicapiserver type: go path: . @@ -46,7 +46,7 @@ modules: build-parameters: builder: custom commands: - - make vendor + - make clean vendor resources: - name: metricsforwarder-config From 38e76270cb43c6e6377527529762a4867574f85b Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Thu, 17 Oct 2024 17:36:24 +0200 Subject: [PATCH 32/39] Update service credential key in metricsforwarder config loading --- src/autoscaler/metricsforwarder/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autoscaler/metricsforwarder/config/config.go b/src/autoscaler/metricsforwarder/config/config.go index 6536d0b95e..35b5c8e9c0 100644 --- a/src/autoscaler/metricsforwarder/config/config.go +++ b/src/autoscaler/metricsforwarder/config/config.go @@ -139,7 +139,7 @@ func loadVcapConfig(conf *Config, vcapReader configutil.VCAPConfigurationReader) } func loadMetricsforwarderConfig(conf *Config, vcapReader configutil.VCAPConfigurationReader) error { - data, err := vcapReader.GetServiceCredentialContent("config", "metricsforwarder") + data, err := vcapReader.GetServiceCredentialContent("metricsforwarder-config", "metricsforwarder") if err != nil { return fmt.Errorf("%w: %v", ErrMetricsforwarderConfigNotFound, err) } From 26afce891e6757aa7aa0fc6d12e4ca3be003cf9d Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 18 Oct 2024 13:20:05 +0200 Subject: [PATCH 33/39] Remove metrics forwarder host configuration and update deployment scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Eliminate metrics forwarder host variable from deployment manifest and Makefile • Adjust default METRICSFORWARDER_HOST and add METRICSFORWARDER_MTLS_HOST in build-extension-file.sh • Update GO_INSTALL_PACKAGE_SPEC in mta.tpl.yaml to point to new publicapiserver path • Add publicapiserver-config resource with metrics forwarder URLs in build-extension-file.sh --- ci/autoscaler/scripts/deploy-autoscaler.sh | 1 - operations/use-cf-services.yml | 6 ------ src/autoscaler/Makefile | 9 ++++----- src/autoscaler/build-extension-file.sh | 12 +++++++++++- src/autoscaler/mta.tpl.yaml | 2 +- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/ci/autoscaler/scripts/deploy-autoscaler.sh b/ci/autoscaler/scripts/deploy-autoscaler.sh index def1900957..8c087616f6 100755 --- a/ci/autoscaler/scripts/deploy-autoscaler.sh +++ b/ci/autoscaler/scripts/deploy-autoscaler.sh @@ -101,7 +101,6 @@ function create_manifest(){ -v app_autoscaler_version="${bosh_release_version}" \ -v cf_client_id=autoscaler_client_id \ -v cf_client_secret=autoscaler_client_secret \ - -v metricsforwarder_host="${metricsforwarder_host}"\ -v postgres_external_port="$(get_postgres_external_port)"\ --vars-file=/tmp/autoscaler-secrets.yml \ -v skip_ssl_validation=true \ diff --git a/operations/use-cf-services.yml b/operations/use-cf-services.yml index b12de16443..33c0a7268a 100644 --- a/operations/use-cf-services.yml +++ b/operations/use-cf-services.yml @@ -1,9 +1,3 @@ -- type: replace - path: /instance_groups/name=apiserver/jobs/name=golangapiserver/properties/autoscaler/apiserver/metrics_forwarder - value: - host: ((metricsforwarder_host)) - mtls_host: ((metricsforwarder_host)) - ## add router tcp route for postgres - type: replace path: /instance_groups/name=postgres/jobs/- diff --git a/src/autoscaler/Makefile b/src/autoscaler/Makefile index 6bd8864afc..d74ddc228a 100644 --- a/src/autoscaler/Makefile +++ b/src/autoscaler/Makefile @@ -173,11 +173,10 @@ mta-logs: .PHONY: mta-build mta-build: mta-build-clean @echo "bulding mtar file for version: $(VERSION)" - cp mta.tpl.yaml mta.yaml - sed -i 's/VERSION/$(VERSION)/g' mta.yaml - mkdir -p $(DEST) - mbt build - @mv ${PWD}/mta_archives/com.github.cloudfoundry.app-autoscaler-release_$(VERSION).mtar $(DEST)/app-autoscaler-release-v$(VERSION).mtar + @cp mta.tpl.yaml mta.yaml + @sed -i 's/VERSION/$(VERSION)/g' mta.yaml + @mbt build + @mkdir -p "${PWD}/$(DEST)" && mv ${PWD}/mta_archives/com.github.cloudfoundry.app-autoscaler-release_$(VERSION).mtar ${PWD}/$(DEST)/app-autoscaler-release-v$(VERSION).mtar mta-build-clean: rm -rf mta_archives diff --git a/src/autoscaler/build-extension-file.sh b/src/autoscaler/build-extension-file.sh index 7d13b98124..0726ab57e1 100755 --- a/src/autoscaler/build-extension-file.sh +++ b/src/autoscaler/build-extension-file.sh @@ -21,7 +21,8 @@ export POSTGRES_EXTERNAL_PORT="${PR_NUMBER:-5432}" export METRICSFORWARDER_HEALTH_PASSWORD="$(credhub get -n /bosh-autoscaler/${DEPLOYMENT_NAME}/autoscaler_metricsforwarder_health_password --quiet)" -export METRICSFORWARDER_HOST="${METRICSFORWARDER_HOST:-"${DEPLOYMENT_NAME}-metricsforwarder"}" +export METRICSFORWARDER_HOST="${METRICSFORWARDER_HOST:-"${DEPLOYMENT_NAME}metrics"}" +export METRICSFORWARDER_MTLS_HOST="${METRICSFORWARDER_MTLS_HOST:-"${DEPLOYMENT_NAME}-metricsforwarder-mtls"}" export PUBLICAPISERVER_HOST="${PUBLICAPISERVER_HOST:-"${DEPLOYMENT_NAME}"}" export SERVICEBROKER_HOST="${SERVICEBROKER_HOST:-"${DEPLOYMENT_NAME}servicebroker"}" @@ -49,6 +50,8 @@ modules: parameters: routes: - route: ${METRICSFORWARDER_HOST}.\${default-domain} + - route: ${METRICSFORWARDER_MTLS_HOST}.\${default-domain} + - name: publicapiserver parameters: @@ -77,4 +80,11 @@ resources: client_cert: "${SYSLOG_CLIENT_CERT//$'\n'/\\n}" client_key: "${SYSLOG_CLIENT_KEY//$'\n'/\\n}" server_ca: "${SYSLOG_CLIENT_CA//$'\n'/\\n}" +- name: publicapiserver-config + parameters: + config: + publicapiserver: + metrics_forwarder: + metrics_forwarder_url: ${METRICSFORWARDER_HOST}.\${default-domain} + metrics_forwarder_mtls_url: ${METRICSFORWARDER_MTLS_HOST}.\${default-domain} EOF diff --git a/src/autoscaler/mta.tpl.yaml b/src/autoscaler/mta.tpl.yaml index 9f7f46dd74..4079cc8418 100644 --- a/src/autoscaler/mta.tpl.yaml +++ b/src/autoscaler/mta.tpl.yaml @@ -32,7 +32,7 @@ modules: type: go path: . properties: - GO_INSTALL_PACKAGE_SPEC: code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/cmd/publicapiserver + GO_INSTALL_PACKAGE_SPEC: code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/cmd/api requires: - name: publicapiserver-config - name: policydb From e871698f84a465a29f1e79fea12ab8b458d4b0e3 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 18 Oct 2024 14:46:20 +0200 Subject: [PATCH 34/39] Refactor config loading and add error handling in autoscaler API - Replace `io.Reader` with `os` package for file handling - Introduce error variables for config file reading and missing server config - Change `PublicApiServer` struct field to `Server` - Add `defaultConfig` function for initializing default config values - Implement `loadYamlFile` and `loadPublicApiServerConfig` for config loading - Modify `LoadConfig` to use new helper functions and handle VCAP configuration - Update tests to reflect changes and use `testhelpers.BytesToFile` for file operations - Add new default JSON files for catalog, config, and info - Update build-extension-file.sh to include `skip_ssl_validation` in parameters --- src/autoscaler/api/config/config.go | 75 +- src/autoscaler/api/config/config_test.go | 926 +++++++++--------- src/autoscaler/api/default_catalog.json | 29 + src/autoscaler/api/default_config.json | 99 ++ src/autoscaler/api/default_info.json | 8 + src/autoscaler/build-extension-file.sh | 2 + .../metricsforwarder/config/config_test.go | 15 +- src/autoscaler/testhelpers/files.go | 12 + 8 files changed, 679 insertions(+), 487 deletions(-) create mode 100644 src/autoscaler/api/default_catalog.json create mode 100644 src/autoscaler/api/default_config.json create mode 100644 src/autoscaler/api/default_info.json diff --git a/src/autoscaler/api/config/config.go b/src/autoscaler/api/config/config.go index 5d8439bc0b..b2b1f721fc 100644 --- a/src/autoscaler/api/config/config.go +++ b/src/autoscaler/api/config/config.go @@ -3,11 +3,12 @@ package config import ( "errors" "fmt" - "io" + "os" "strings" "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cf" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/configutil" "golang.org/x/crypto/bcrypt" @@ -33,6 +34,11 @@ const ( DefaultDiskUpperThreshold = 2 * 1024 ) +var ( + ErrReadYaml = errors.New("failed to read config file") + ErrPublicApiServerConfigNotFound = errors.New("metricsforwarder config service not found") +) + var defaultBrokerServerConfig = helpers.ServerConfig{ Port: 8080, } @@ -92,7 +98,7 @@ type LowerUpperThresholdConfig struct { type Config struct { Logging helpers.LoggingConfig `yaml:"logging"` BrokerServer helpers.ServerConfig `yaml:"broker_server"` - PublicApiServer helpers.ServerConfig `yaml:"public_api_server"` + Server helpers.ServerConfig `yaml:"public_api_server"` DB map[string]db.DatabaseConfig `yaml:"db"` BrokerCredentials []BrokerCredentialsConfig `yaml:"broker_credentials"` APIClientId string `yaml:"api_client_id"` @@ -119,13 +125,15 @@ type PlanCheckConfig struct { PlanDefinitions map[string]PlanDefinition `yaml:"plan_definitions"` } -func LoadConfig(reader io.Reader) (*Config, error) { - conf := &Config{ - Logging: defaultLoggingConfig, - BrokerServer: defaultBrokerServerConfig, - PublicApiServer: defaultPublicApiServerConfig, +func defaultConfig() Config { + return Config{ + Logging: defaultLoggingConfig, + BrokerServer: defaultBrokerServerConfig, + Server: defaultPublicApiServerConfig, CF: cf.Config{ - ClientConfig: cf.ClientConfig{SkipSSLValidation: false}, + ClientConfig: cf.ClientConfig{ + SkipSSLValidation: false, + }, }, RateLimit: models.RateLimitConfig{ MaxAmount: DefaultMaxAmount, @@ -151,17 +159,60 @@ func LoadConfig(reader io.Reader) (*Config, error) { }, } - dec := yaml.NewDecoder(reader) - dec.KnownFields(true) - err := dec.Decode(conf) +} +func loadYamlFile(filepath string, conf *Config) error { + if filepath == "" { + return nil + } + file, err := os.Open(filepath) + if err != nil { + fmt.Fprintf(os.Stdout, "failed to open config file '%s': %s\n", filepath, err) + return ErrReadYaml + } + defer file.Close() + dec := yaml.NewDecoder(file) + dec.KnownFields(true) + if err := dec.Decode(conf); err != nil { + return fmt.Errorf("%w: %v", ErrReadYaml, err) + } + return nil +} +func loadPublicApiServerConfig(conf *Config, vcapReader configutil.VCAPConfigurationReader) error { + data, err := vcapReader.GetServiceCredentialContent("metricsforwarder-config", "metricsforwarder") if err != nil { + return fmt.Errorf("%w: %v", ErrPublicApiServerConfigNotFound, err) + } + return yaml.Unmarshal(data, conf) +} + +func loadVcapConfig(conf *Config, vcapReader configutil.VCAPConfigurationReader) error { + if !vcapReader.IsRunningOnCF() { + return nil + } + + conf.Server.Port = vcapReader.GetPort() + if err := loadPublicApiServerConfig(conf, vcapReader); err != nil { + return err + } + + return nil +} + +func LoadConfig(filepath string, vcapReader configutil.VCAPConfigurationReader) (*Config, error) { + conf := defaultConfig() + + if err := loadYamlFile(filepath, &conf); err != nil { + return nil, err + } + + if err := loadVcapConfig(&conf, vcapReader); err != nil { return nil, err } conf.Logging.Level = strings.ToLower(conf.Logging.Level) - return conf, nil + return &conf, nil } func (c *Config) Validate() error { diff --git a/src/autoscaler/api/config/config_test.go b/src/autoscaler/api/config/config_test.go index 265d2b396f..c3fc183cff 100644 --- a/src/autoscaler/api/config/config_test.go +++ b/src/autoscaler/api/config/config_test.go @@ -1,10 +1,11 @@ package config_test import ( - "bytes" + "fmt" "time" - . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cf" @@ -13,511 +14,512 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "gopkg.in/yaml.v3" ) var _ = Describe("Config", func() { var ( - conf *Config - err error - configBytes string + conf *Config + err error + configBytes []byte + configFile string + mockVCAPConfigurationReader *fakes.FakeVCAPConfigurationReader ) + BeforeEach(func() { + mockVCAPConfigurationReader = &fakes.FakeVCAPConfigurationReader{} + }) + Describe("Load Config", func() { - JustBeforeEach(func() { - conf, err = LoadConfig(bytes.NewReader([]byte(configBytes))) - }) - Context("with invalid yaml", func() { - BeforeEach(func() { - configBytes = LoadFile("invalid_config.yml") - }) + When("config is read from env", func() { + var expectedDbUrl string - It("returns an error", func() { - Expect(err).To(MatchError(MatchRegexp("yaml: .*"))) - }) - }) - Context("with valid yaml", func() { - BeforeEach(func() { - configBytes = LoadFile("valid_config.yml") - }) - - It("It returns the config", func() { - Expect(err).NotTo(HaveOccurred()) - - Expect(conf.Logging.Level).To(Equal("debug")) - Expect(conf.BrokerServer.Port).To(Equal(8080)) - Expect(conf.BrokerServer.TLS).To(Equal( - models.TLSCerts{ - KeyFile: "/var/vcap/jobs/autoscaler/config/certs/broker.key", - CACertFile: "/var/vcap/jobs/autoscaler/config/certs/autoscaler-ca.crt", - CertFile: "/var/vcap/jobs/autoscaler/config/certs/broker.crt", - }, - )) - Expect(conf.PublicApiServer.Port).To(Equal(8081)) - Expect(conf.PublicApiServer.TLS).To(Equal( - models.TLSCerts{ - KeyFile: "/var/vcap/jobs/autoscaler/config/certs/api.key", - CACertFile: "/var/vcap/jobs/autoscaler/config/certs/autoscaler-ca.crt", - CertFile: "/var/vcap/jobs/autoscaler/config/certs/api.crt", - }, - )) - Expect(conf.DB[db.BindingDb]).To(Equal( - db.DatabaseConfig{ - URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", - MaxOpenConnections: 10, - MaxIdleConnections: 5, - ConnectionMaxLifetime: 60 * time.Second, - })) - Expect(conf.DB[db.PolicyDb]).To(Equal( - db.DatabaseConfig{ - URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", - MaxOpenConnections: 10, - MaxIdleConnections: 5, - ConnectionMaxLifetime: 60 * time.Second, - })) - Expect(conf.BrokerCredentials[0].BrokerUsername).To(Equal("broker_username")) - Expect(conf.BrokerCredentials[0].BrokerPassword).To(Equal("broker_password")) - Expect(conf.CatalogPath).To(Equal("../exampleconfig/catalog-example.json")) - Expect(conf.CatalogSchemaPath).To(Equal("../schemas/catalog.schema.json")) - Expect(conf.PolicySchemaPath).To(Equal("../exampleconfig/policy.schema.json")) - Expect(conf.Scheduler).To(Equal( - SchedulerConfig{ - SchedulerURL: "https://localhost:8083", - TLSClientCerts: models.TLSCerts{ - KeyFile: "/var/vcap/jobs/autoscaler/config/certs/sc.key", - CACertFile: "/var/vcap/jobs/autoscaler/config/certs/autoscaler-ca.crt", - CertFile: "/var/vcap/jobs/autoscaler/config/certs/sc.crt", - }, - }, - )) - Expect(conf.MetricsForwarder).To(Equal( - MetricsForwarderConfig{ - MetricsForwarderUrl: "https://localhost:8088", - MetricsForwarderMtlsUrl: "https://mtlssdsdds:8084", - }, - )) - Expect(conf.InfoFilePath).To(Equal("/var/vcap/jobs/autoscaer/config/info-file.json")) - Expect(conf.CF).To(Equal( - cf.Config{ - API: "https://api.example.com", - ClientID: "client-id", - Secret: "client-secret", - ClientConfig: cf.ClientConfig{ - SkipSSLValidation: false, - MaxRetries: 3, - MaxRetryWaitMs: 27, - }, - }, - )) - Expect(conf.CredHelperImpl).To(Equal("default")) - Expect(conf.ScalingRules.CPU.LowerThreshold).To(Equal(22)) - Expect(conf.ScalingRules.CPU.UpperThreshold).To(Equal(33)) - Expect(conf.ScalingRules.CPUUtil.LowerThreshold).To(Equal(22)) - Expect(conf.ScalingRules.CPUUtil.UpperThreshold).To(Equal(33)) - Expect(conf.ScalingRules.DiskUtil.LowerThreshold).To(Equal(22)) - Expect(conf.ScalingRules.DiskUtil.UpperThreshold).To(Equal(33)) - Expect(conf.ScalingRules.Disk.LowerThreshold).To(Equal(22)) - Expect(conf.ScalingRules.Disk.UpperThreshold).To(Equal(33)) + JustBeforeEach(func() { + mockVCAPConfigurationReader.IsRunningOnCFReturns(true) + mockVCAPConfigurationReader.MaterializeDBFromServiceReturns(expectedDbUrl, nil) + conf, err = LoadConfig("", mockVCAPConfigurationReader) }) - }) - Context("with partial config", func() { - BeforeEach(func() { - configBytes = LoadFile("partial_config.yml") - }) - It("It returns the default values", func() { - Expect(err).NotTo(HaveOccurred()) - - Expect(conf.Logging.Level).To(Equal("info")) - Expect(conf.BrokerServer.Port).To(Equal(8080)) - Expect(conf.PublicApiServer.Port).To(Equal(8081)) - Expect(conf.DB[db.BindingDb]).To(Equal( - db.DatabaseConfig{ - URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", - MaxOpenConnections: 0, - MaxIdleConnections: 0, - ConnectionMaxLifetime: 0 * time.Second, - })) - Expect(conf.DB[db.PolicyDb]).To(Equal( - db.DatabaseConfig{ - URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", - MaxOpenConnections: 0, - MaxIdleConnections: 0, - ConnectionMaxLifetime: 0 * time.Second, - })) - Expect(conf.ScalingRules.CPU.LowerThreshold).To(Equal(1)) - Expect(conf.ScalingRules.CPU.UpperThreshold).To(Equal(100)) - Expect(conf.ScalingRules.CPUUtil.LowerThreshold).To(Equal(1)) - Expect(conf.ScalingRules.CPUUtil.UpperThreshold).To(Equal(100)) - Expect(conf.ScalingRules.DiskUtil.LowerThreshold).To(Equal(1)) - Expect(conf.ScalingRules.DiskUtil.UpperThreshold).To(Equal(100)) - Expect(conf.ScalingRules.Disk.LowerThreshold).To(Equal(1)) - Expect(conf.ScalingRules.Disk.UpperThreshold).To(Equal(2 * 1024)) - }) - }) + When("vcap PORT is set to a number", func() { + BeforeEach(func() { + mockVCAPConfigurationReader.GetPortReturns(3333) + }) - Context("when it gives a non integer broker_server port", func() { - BeforeEach(func() { - configBytes = ` -broker_server: - port: port -` + It("sets env variable over config file", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(conf.Server.Port).To(Equal(3333)) + }) }) - It("should error", func() { - Expect(err).To(BeAssignableToTypeOf(&yaml.TypeError{})) - Expect(err).To(MatchError(MatchRegexp("cannot unmarshal.*into int"))) - }) - }) - Context("when it gives a non integer public_api_server port", func() { - BeforeEach(func() { - configBytes = ` -public_api_server: - port: port -` - }) + When("service is empty", func() { + var expectedErr error + BeforeEach(func() { + expectedErr = fmt.Errorf("publicapiserver config service not found") + mockVCAPConfigurationReader.GetServiceCredentialContentReturns([]byte(""), expectedErr) + }) - It("should error", func() { - Expect(err).To(BeAssignableToTypeOf(&yaml.TypeError{})) - Expect(err).To(MatchError(MatchRegexp("cannot unmarshal.*into int"))) + It("should error with config service not found", func() { + Expect(err).To(MatchError(MatchRegexp("publicapiserver config service not found"))) + }) }) }) - Context("when it gives a non integer health server port", func() { - BeforeEach(func() { - configBytes = ` -health: - server_config: - port: port -` + + When("config is read from file", func() { + JustBeforeEach(func() { + configFile = testhelpers.BytesToFile(configBytes) + conf, err = LoadConfig(configFile, mockVCAPConfigurationReader) }) - It("should error", func() { - Expect(err).To(BeAssignableToTypeOf(&yaml.TypeError{})) - Expect(err).To(MatchError(MatchRegexp("cannot unmarshal.*into int"))) + Context("with invalid yaml", func() { + BeforeEach(func() { + configBytes = []byte(testhelpers.LoadFile("invalid_config.yml")) + }) + + It("returns an error", func() { + Expect(err).To(MatchError(MatchRegexp("yaml: .*"))) + }) }) - }) + Context("with valid yaml", func() { + BeforeEach(func() { + configBytes = []byte(testhelpers.LoadFile("valid_config.yml")) + }) - Context("when max_amount of rate_limit is not an integer", func() { - BeforeEach(func() { - configBytes = ` + It("It returns the config", func() { + Expect(err).NotTo(HaveOccurred()) + + Expect(conf.Logging.Level).To(Equal("debug")) + Expect(conf.BrokerServer.Port).To(Equal(8080)) + Expect(conf.BrokerServer.TLS).To(Equal( + models.TLSCerts{ + KeyFile: "/var/vcap/jobs/autoscaler/config/certs/broker.key", + CACertFile: "/var/vcap/jobs/autoscaler/config/certs/autoscaler-ca.crt", + CertFile: "/var/vcap/jobs/autoscaler/config/certs/broker.crt", + }, + )) + Expect(conf.Server.Port).To(Equal(8081)) + Expect(conf.Server.TLS).To(Equal( + models.TLSCerts{ + KeyFile: "/var/vcap/jobs/autoscaler/config/certs/api.key", + CACertFile: "/var/vcap/jobs/autoscaler/config/certs/autoscaler-ca.crt", + CertFile: "/var/vcap/jobs/autoscaler/config/certs/api.crt", + }, + )) + Expect(conf.DB[db.BindingDb]).To(Equal( + db.DatabaseConfig{ + URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", + MaxOpenConnections: 10, + MaxIdleConnections: 5, + ConnectionMaxLifetime: 60 * time.Second, + })) + Expect(conf.DB[db.PolicyDb]).To(Equal( + db.DatabaseConfig{ + URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", + MaxOpenConnections: 10, + MaxIdleConnections: 5, + ConnectionMaxLifetime: 60 * time.Second, + })) + Expect(conf.BrokerCredentials[0].BrokerUsername).To(Equal("broker_username")) + Expect(conf.BrokerCredentials[0].BrokerPassword).To(Equal("broker_password")) + Expect(conf.CatalogPath).To(Equal("../exampleconfig/catalog-example.json")) + Expect(conf.CatalogSchemaPath).To(Equal("../schemas/catalog.schema.json")) + Expect(conf.PolicySchemaPath).To(Equal("../exampleconfig/policy.schema.json")) + Expect(conf.Scheduler).To(Equal( + SchedulerConfig{ + SchedulerURL: "https://localhost:8083", + TLSClientCerts: models.TLSCerts{ + KeyFile: "/var/vcap/jobs/autoscaler/config/certs/sc.key", + CACertFile: "/var/vcap/jobs/autoscaler/config/certs/autoscaler-ca.crt", + CertFile: "/var/vcap/jobs/autoscaler/config/certs/sc.crt", + }, + }, + )) + Expect(conf.MetricsForwarder).To(Equal( + MetricsForwarderConfig{ + MetricsForwarderUrl: "https://localhost:8088", + MetricsForwarderMtlsUrl: "https://mtlssdsdds:8084", + }, + )) + Expect(conf.InfoFilePath).To(Equal("/var/vcap/jobs/autoscaer/config/info-file.json")) + Expect(conf.CF).To(Equal( + cf.Config{ + API: "https://api.example.com", + ClientID: "client-id", + Secret: "client-secret", + ClientConfig: cf.ClientConfig{ + SkipSSLValidation: false, + MaxRetries: 3, + MaxRetryWaitMs: 27, + }, + }, + )) + Expect(conf.CredHelperImpl).To(Equal("default")) + Expect(conf.ScalingRules.CPU.LowerThreshold).To(Equal(22)) + Expect(conf.ScalingRules.CPU.UpperThreshold).To(Equal(33)) + Expect(conf.ScalingRules.CPUUtil.LowerThreshold).To(Equal(22)) + Expect(conf.ScalingRules.CPUUtil.UpperThreshold).To(Equal(33)) + Expect(conf.ScalingRules.DiskUtil.LowerThreshold).To(Equal(22)) + Expect(conf.ScalingRules.DiskUtil.UpperThreshold).To(Equal(33)) + Expect(conf.ScalingRules.Disk.LowerThreshold).To(Equal(22)) + Expect(conf.ScalingRules.Disk.UpperThreshold).To(Equal(33)) + }) + }) + + Context("with partial config", func() { + BeforeEach(func() { + configBytes = []byte(testhelpers.LoadFile("partial_config.yml")) + }) + It("It returns the default values", func() { + Expect(err).NotTo(HaveOccurred()) + + Expect(conf.Logging.Level).To(Equal("info")) + Expect(conf.BrokerServer.Port).To(Equal(8080)) + Expect(conf.Server.Port).To(Equal(8081)) + Expect(conf.DB[db.BindingDb]).To(Equal( + db.DatabaseConfig{ + URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", + MaxOpenConnections: 0, + MaxIdleConnections: 0, + ConnectionMaxLifetime: 0 * time.Second, + })) + Expect(conf.DB[db.PolicyDb]).To(Equal( + db.DatabaseConfig{ + URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", + MaxOpenConnections: 0, + MaxIdleConnections: 0, + ConnectionMaxLifetime: 0 * time.Second, + })) + Expect(conf.ScalingRules.CPU.LowerThreshold).To(Equal(1)) + Expect(conf.ScalingRules.CPU.UpperThreshold).To(Equal(100)) + Expect(conf.ScalingRules.CPUUtil.LowerThreshold).To(Equal(1)) + Expect(conf.ScalingRules.CPUUtil.UpperThreshold).To(Equal(100)) + Expect(conf.ScalingRules.DiskUtil.LowerThreshold).To(Equal(1)) + Expect(conf.ScalingRules.DiskUtil.UpperThreshold).To(Equal(100)) + Expect(conf.ScalingRules.Disk.LowerThreshold).To(Equal(1)) + Expect(conf.ScalingRules.Disk.UpperThreshold).To(Equal(2 * 1024)) + }) + }) + + Context("when max_amount of rate_limit is not an integer", func() { + BeforeEach(func() { + configBytes = []byte(` rate_limit: max_amount: NOT-INTEGER -` - }) - It("should error", func() { - Expect(err).To(BeAssignableToTypeOf(&yaml.TypeError{})) - Expect(err).To(MatchError(MatchRegexp("cannot unmarshal.*into int"))) +`) + }) + It("should error", func() { + Expect(err).To(MatchError(MatchRegexp("failed to read config file"))) + }) }) - }) - Context("when valid_duration of rate_limit is not a time duration", func() { - BeforeEach(func() { - configBytes = ` + + Context("when valid_duration of rate_limit is not a time duration", func() { + BeforeEach(func() { + configBytes = []byte(` rate_limit: valid_duration: NOT-TIME-DURATION -` - }) - It("should error", func() { - Expect(err).To(BeAssignableToTypeOf(&yaml.TypeError{})) - Expect(err).To(MatchError(MatchRegexp("cannot unmarshal.*into time.Duration"))) - }) - }) - - }) +`) + }) - Describe("Validate", func() { - BeforeEach(func() { - conf = &Config{} - conf.DB = make(map[string]db.DatabaseConfig) - conf.DB[db.BindingDb] = db.DatabaseConfig{ - URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", - MaxOpenConnections: 10, - MaxIdleConnections: 5, - ConnectionMaxLifetime: 60 * time.Second, - } - conf.DB[db.PolicyDb] = db.DatabaseConfig{ - URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", - MaxOpenConnections: 10, - MaxIdleConnections: 5, - ConnectionMaxLifetime: 60 * time.Second, - } - - brokerCred1 := BrokerCredentialsConfig{ - BrokerUsernameHash: []byte("$2a$10$WNO1cPko4iDAT6MkhaDojeJMU8ZdNH6gt.SapsFOsC0OF4cQ9qQwu"), // ruby -r bcrypt -e 'puts BCrypt::Password.create("broker_username")' - BrokerPasswordHash: []byte("$2a$10$evLviRLcIPKnWQqlBl3DJOvBZir9vJ4gdEeyoGgvnK/CGBnxIAFRu"), // ruby -r bcrypt -e 'puts BCrypt::Password.create("broker_password")' - } - var brokerCreds []BrokerCredentialsConfig - brokerCreds = append(brokerCreds, brokerCred1) - conf.BrokerCredentials = brokerCreds - - conf.CatalogSchemaPath = "../schemas/catalog.schema.json" - conf.CatalogPath = "../exampleconfig/catalog-example.json" - conf.PolicySchemaPath = "../exampleconfig/policy.schema.json" - - conf.Scheduler.SchedulerURL = "https://localhost:8083" - - conf.ScalingEngine.ScalingEngineUrl = "https://localhost:8084" - conf.EventGenerator.EventGeneratorUrl = "https://localhost:8085" - conf.MetricsForwarder.MetricsForwarderUrl = "https://localhost:8088" - - conf.CF.API = "https://api.bosh-lite.com" - conf.CF.ClientID = "client-id" - conf.CF.Secret = "secret" - - conf.InfoFilePath = "../exampleconfig/info-file.json" - - conf.RateLimit.MaxAmount = 10 - conf.RateLimit.ValidDuration = 1 * time.Second - - conf.CredHelperImpl = "path/to/plugin" - }) - JustBeforeEach(func() { - err = conf.Validate() - }) - - Context("When all the configs are valid", func() { - It("should not error", func() { - Expect(err).NotTo(HaveOccurred()) + It("should error", func() { + Expect(err).To(MatchError(MatchRegexp("failed to read config file"))) + }) }) - }) - Context("when bindingdb url is not set", func() { - BeforeEach(func() { - conf.DB[db.BindingDb] = db.DatabaseConfig{URL: ""} - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: BindingDB URL is empty"))) - }) }) - Context("when policydb url is not set", func() { + Describe("Validate", func() { BeforeEach(func() { - conf.DB[db.PolicyDb] = db.DatabaseConfig{URL: ""} - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: PolicyDB URL is empty"))) - }) - }) - - Context("when scheduler url is not set", func() { - BeforeEach(func() { - conf.Scheduler.SchedulerURL = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: scheduler.scheduler_url is empty"))) - }) - }) - - Context("when neither the broker username nor its hash is set", func() { - BeforeEach(func() { - brokerCred1 := BrokerCredentialsConfig{ - BrokerPasswordHash: []byte(""), - BrokerPassword: "", + conf = &Config{} + conf.DB = make(map[string]db.DatabaseConfig) + conf.DB[db.BindingDb] = db.DatabaseConfig{ + URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", + MaxOpenConnections: 10, + MaxIdleConnections: 5, + ConnectionMaxLifetime: 60 * time.Second, } - var brokerCreds []BrokerCredentialsConfig - brokerCreds = append(brokerCreds, brokerCred1) - conf.BrokerCredentials = brokerCreds - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: both broker_username and broker_username_hash are empty, please provide one of them"))) - }) - }) - - Context("when both the broker username and its hash are set", func() { - BeforeEach(func() { - brokerCred1 := BrokerCredentialsConfig{ - BrokerUsername: "broker_username", - BrokerUsernameHash: []byte("$2a$10$WNO1cPko4iDAT6MkhaDojeJMU8ZdNH6gt.SapsFOsC0OF4cQ9qQwu"), + conf.DB[db.PolicyDb] = db.DatabaseConfig{ + URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", + MaxOpenConnections: 10, + MaxIdleConnections: 5, + ConnectionMaxLifetime: 60 * time.Second, } - var brokerCreds []BrokerCredentialsConfig - brokerCreds = append(brokerCreds, brokerCred1) - conf.BrokerCredentials = brokerCreds - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: both broker_username and broker_username_hash are set, please provide only one of them"))) - }) - }) - - Context("when just the broker username is set", func() { - BeforeEach(func() { - conf.BrokerCredentials[0].BrokerUsername = "broker_username" - conf.BrokerCredentials[0].BrokerUsernameHash = []byte("") - }) - It("should not error", func() { - Expect(err).NotTo(HaveOccurred()) - }) - }) - Context("when the broker username hash is set to an invalid value", func() { - BeforeEach(func() { - conf.BrokerCredentials[0].BrokerUsernameHash = []byte("not a bcrypt hash") - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: broker_username_hash is not a valid bcrypt hash"))) - }) - }) - - Context("when neither the broker password nor its hash is set", func() { - BeforeEach(func() { - conf.BrokerCredentials[0].BrokerPassword = "" - conf.BrokerCredentials[0].BrokerPasswordHash = []byte("") - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: both broker_password and broker_password_hash are empty, please provide one of them"))) - }) - }) - - Context("when both the broker password and its hash are set", func() { - BeforeEach(func() { - conf.BrokerCredentials[0].BrokerPassword = "broker_password" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: both broker_password and broker_password_hash are set, please provide only one of them"))) - }) - }) - - Context("when just the broker password is set", func() { - BeforeEach(func() { - conf.BrokerCredentials[0].BrokerPassword = "broker_password" - conf.BrokerCredentials[0].BrokerPasswordHash = []byte("") - }) - It("should not error", func() { - Expect(err).NotTo(HaveOccurred()) - }) - }) - - Context("when the broker password hash is set to an invalid value", func() { - BeforeEach(func() { brokerCred1 := BrokerCredentialsConfig{ - BrokerUsername: "broker_username", - BrokerPasswordHash: []byte("not a bcrypt hash"), + BrokerUsernameHash: []byte("$2a$10$WNO1cPko4iDAT6MkhaDojeJMU8ZdNH6gt.SapsFOsC0OF4cQ9qQwu"), // ruby -r bcrypt -e 'puts BCrypt::Password.create("broker_username")' + BrokerPasswordHash: []byte("$2a$10$evLviRLcIPKnWQqlBl3DJOvBZir9vJ4gdEeyoGgvnK/CGBnxIAFRu"), // ruby -r bcrypt -e 'puts BCrypt::Password.create("broker_password")' } var brokerCreds []BrokerCredentialsConfig brokerCreds = append(brokerCreds, brokerCred1) conf.BrokerCredentials = brokerCreds - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: broker_password_hash is not a valid bcrypt hash"))) - }) - }) - - Context("when eventgenerator url is not set", func() { - BeforeEach(func() { - conf.EventGenerator.EventGeneratorUrl = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: event_generator.event_generator_url is empty"))) - }) - }) - - Context("when scalingengine url is not set", func() { - BeforeEach(func() { - conf.ScalingEngine.ScalingEngineUrl = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: scaling_engine.scaling_engine_url is empty"))) - }) - }) - - Context("when metricsforwarder url is not set", func() { - BeforeEach(func() { - conf.MetricsForwarder.MetricsForwarderUrl = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: metrics_forwarder.metrics_forwarder_url is empty"))) - }) - }) - - Context("when catalog schema path is not set", func() { - BeforeEach(func() { - conf.CatalogSchemaPath = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: CatalogSchemaPath is empty"))) - }) - }) - Context("when catalog path is not set", func() { - BeforeEach(func() { - conf.CatalogPath = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: CatalogPath is empty"))) - }) - }) + conf.CatalogSchemaPath = "../schemas/catalog.schema.json" + conf.CatalogPath = "../exampleconfig/catalog-example.json" + conf.PolicySchemaPath = "../exampleconfig/policy.schema.json" + + conf.Scheduler.SchedulerURL = "https://localhost:8083" + + conf.ScalingEngine.ScalingEngineUrl = "https://localhost:8084" + conf.EventGenerator.EventGeneratorUrl = "https://localhost:8085" + conf.MetricsForwarder.MetricsForwarderUrl = "https://localhost:8088" + + conf.CF.API = "https://api.bosh-lite.com" + conf.CF.ClientID = "client-id" + conf.CF.Secret = "secret" + + conf.InfoFilePath = "../exampleconfig/info-file.json" + + conf.RateLimit.MaxAmount = 10 + conf.RateLimit.ValidDuration = 1 * time.Second + + conf.CredHelperImpl = "path/to/plugin" + }) + JustBeforeEach(func() { + err = conf.Validate() + }) + + Context("When all the configs are valid", func() { + It("should not error", func() { + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Context("when bindingdb url is not set", func() { + BeforeEach(func() { + conf.DB[db.BindingDb] = db.DatabaseConfig{URL: ""} + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: BindingDB URL is empty"))) + }) + }) + + Context("when policydb url is not set", func() { + BeforeEach(func() { + conf.DB[db.PolicyDb] = db.DatabaseConfig{URL: ""} + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: PolicyDB URL is empty"))) + }) + }) + + Context("when scheduler url is not set", func() { + BeforeEach(func() { + conf.Scheduler.SchedulerURL = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: scheduler.scheduler_url is empty"))) + }) + }) + + Context("when neither the broker username nor its hash is set", func() { + BeforeEach(func() { + brokerCred1 := BrokerCredentialsConfig{ + BrokerPasswordHash: []byte(""), + BrokerPassword: "", + } + var brokerCreds []BrokerCredentialsConfig + brokerCreds = append(brokerCreds, brokerCred1) + conf.BrokerCredentials = brokerCreds + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: both broker_username and broker_username_hash are empty, please provide one of them"))) + }) + }) + + Context("when both the broker username and its hash are set", func() { + BeforeEach(func() { + brokerCred1 := BrokerCredentialsConfig{ + BrokerUsername: "broker_username", + BrokerUsernameHash: []byte("$2a$10$WNO1cPko4iDAT6MkhaDojeJMU8ZdNH6gt.SapsFOsC0OF4cQ9qQwu"), + } + var brokerCreds []BrokerCredentialsConfig + brokerCreds = append(brokerCreds, brokerCred1) + conf.BrokerCredentials = brokerCreds + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: both broker_username and broker_username_hash are set, please provide only one of them"))) + }) + }) + + Context("when just the broker username is set", func() { + BeforeEach(func() { + conf.BrokerCredentials[0].BrokerUsername = "broker_username" + conf.BrokerCredentials[0].BrokerUsernameHash = []byte("") + }) + It("should not error", func() { + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Context("when the broker username hash is set to an invalid value", func() { + BeforeEach(func() { + conf.BrokerCredentials[0].BrokerUsernameHash = []byte("not a bcrypt hash") + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: broker_username_hash is not a valid bcrypt hash"))) + }) + }) + + Context("when neither the broker password nor its hash is set", func() { + BeforeEach(func() { + conf.BrokerCredentials[0].BrokerPassword = "" + conf.BrokerCredentials[0].BrokerPasswordHash = []byte("") + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: both broker_password and broker_password_hash are empty, please provide one of them"))) + }) + }) + + Context("when both the broker password and its hash are set", func() { + BeforeEach(func() { + conf.BrokerCredentials[0].BrokerPassword = "broker_password" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: both broker_password and broker_password_hash are set, please provide only one of them"))) + }) + }) + + Context("when just the broker password is set", func() { + BeforeEach(func() { + conf.BrokerCredentials[0].BrokerPassword = "broker_password" + conf.BrokerCredentials[0].BrokerPasswordHash = []byte("") + }) + It("should not error", func() { + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Context("when the broker password hash is set to an invalid value", func() { + BeforeEach(func() { + brokerCred1 := BrokerCredentialsConfig{ + BrokerUsername: "broker_username", + BrokerPasswordHash: []byte("not a bcrypt hash"), + } + var brokerCreds []BrokerCredentialsConfig + brokerCreds = append(brokerCreds, brokerCred1) + conf.BrokerCredentials = brokerCreds + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: broker_password_hash is not a valid bcrypt hash"))) + }) + }) + + Context("when eventgenerator url is not set", func() { + BeforeEach(func() { + conf.EventGenerator.EventGeneratorUrl = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: event_generator.event_generator_url is empty"))) + }) + }) + + Context("when scalingengine url is not set", func() { + BeforeEach(func() { + conf.ScalingEngine.ScalingEngineUrl = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: scaling_engine.scaling_engine_url is empty"))) + }) + }) + + Context("when metricsforwarder url is not set", func() { + BeforeEach(func() { + conf.MetricsForwarder.MetricsForwarderUrl = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: metrics_forwarder.metrics_forwarder_url is empty"))) + }) + }) + + Context("when catalog schema path is not set", func() { + BeforeEach(func() { + conf.CatalogSchemaPath = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: CatalogSchemaPath is empty"))) + }) + }) + + Context("when catalog path is not set", func() { + BeforeEach(func() { + conf.CatalogPath = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: CatalogPath is empty"))) + }) + }) + + Context("when policy schema path is not set", func() { + BeforeEach(func() { + conf.PolicySchemaPath = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: PolicySchemaPath is empty"))) + }) + }) + + Context("when catalog is not valid json", func() { + BeforeEach(func() { + conf.CatalogPath = "../exampleconfig/catalog-invalid-json-example.json" + }) + It("should err", func() { + Expect(err).To(MatchError("invalid character '[' after object key")) + }) + }) + + Context("when catalog is missing required fields", func() { + BeforeEach(func() { + conf.CatalogPath = "../exampleconfig/catalog-missing-example.json" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("{\"name is required\"}"))) + }) + }) + + Context("when catalog has invalid type fields", func() { + BeforeEach(func() { + conf.CatalogPath = "../exampleconfig/catalog-invalid-example.json" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("{\"Invalid type. Expected: boolean, given: integer\"}"))) + }) + }) + + Context("when info_file_path is not set", func() { + BeforeEach(func() { + conf.InfoFilePath = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: InfoFilePath is empty"))) + }) + }) + + Context("when cf.client_id is not set", func() { + BeforeEach(func() { + conf.CF.ClientID = "" + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: client_id is empty"))) + }) + }) + + Context("when rate_limit.max_amount is <= zero", func() { + BeforeEach(func() { + conf.RateLimit.MaxAmount = 0 + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: RateLimit.MaxAmount is equal or less than zero"))) + }) + }) - Context("when policy schema path is not set", func() { - BeforeEach(func() { - conf.PolicySchemaPath = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: PolicySchemaPath is empty"))) + Context("when rate_limit.valid_duration is <= 0 ns", func() { + BeforeEach(func() { + conf.RateLimit.ValidDuration = 0 * time.Nanosecond + }) + It("should err", func() { + Expect(err).To(MatchError(MatchRegexp("Configuration error: RateLimit.ValidDuration is equal or less than zero nanosecond"))) + }) }) - }) - Context("when catalog is not valid json", func() { - BeforeEach(func() { - conf.CatalogPath = "../exampleconfig/catalog-invalid-json-example.json" - }) - It("should err", func() { - Expect(err).To(MatchError("invalid character '[' after object key")) - }) }) - - Context("when catalog is missing required fields", func() { - BeforeEach(func() { - conf.CatalogPath = "../exampleconfig/catalog-missing-example.json" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("{\"name is required\"}"))) - }) - }) - - Context("when catalog has invalid type fields", func() { - BeforeEach(func() { - conf.CatalogPath = "../exampleconfig/catalog-invalid-example.json" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("{\"Invalid type. Expected: boolean, given: integer\"}"))) - }) - }) - - Context("when info_file_path is not set", func() { - BeforeEach(func() { - conf.InfoFilePath = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: InfoFilePath is empty"))) - }) - }) - - Context("when cf.client_id is not set", func() { - BeforeEach(func() { - conf.CF.ClientID = "" - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: client_id is empty"))) - }) - }) - - Context("when rate_limit.max_amount is <= zero", func() { - BeforeEach(func() { - conf.RateLimit.MaxAmount = 0 - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: RateLimit.MaxAmount is equal or less than zero"))) - }) - }) - - Context("when rate_limit.valid_duration is <= 0 ns", func() { - BeforeEach(func() { - conf.RateLimit.ValidDuration = 0 * time.Nanosecond - }) - It("should err", func() { - Expect(err).To(MatchError(MatchRegexp("Configuration error: RateLimit.ValidDuration is equal or less than zero nanosecond"))) - }) - }) - }) }) diff --git a/src/autoscaler/api/default_catalog.json b/src/autoscaler/api/default_catalog.json new file mode 100644 index 0000000000..a1d6f7f753 --- /dev/null +++ b/src/autoscaler/api/default_catalog.json @@ -0,0 +1,29 @@ +{ + "services": [ + { + "bindable": true, + "bindings_retrievable": true, + "description": "Automatically increase or decrease the number of application instances based on a policy you define.", + "id": "autoscaler-guid", + "instances_retrievable": true, + "name": "autoscaler-3119", + "plans": [ + { + "description": "This is the free service plan for the Auto-Scaling service.", + "id": "autoscaler-free-plan-id", + "name": "autoscaler-free-plan", + "plan_updateable": true + }, + { + "description": "This is the standard service plan for the Auto-Scaling service.", + "id": "acceptance-standard", + "name": "acceptance-standard", + "plan_updateable": false + } + ], + "tags": [ + "app-autoscaler" + ] + } + ] +} diff --git a/src/autoscaler/api/default_config.json b/src/autoscaler/api/default_config.json new file mode 100644 index 0000000000..b7456fb217 --- /dev/null +++ b/src/autoscaler/api/default_config.json @@ -0,0 +1,99 @@ +{ + "publicapiserver": { + "cf": { + "api": "https://api.autoscaler.app-runtime-interfaces.ci.cloudfoundry.org", + "client_id": "autoscaler_client_id", + "secret": "autoscaler_client_secret", + "skip_ssl_validation": false, + "max_retries": 3, + "max_retry_wait_ms": 0, + "idle_connection_timeout_ms": 5000, + "max_idle_conns_per_host_ms": 200 + }, + "broker_server": { + "port": 6102 + }, + "broker_credentials": [ + { + "broker_password": "REPLACE_ME", + "broker_username": "autoscaler-broker-user" + }, + { + "broker_password": "REPLACE_ME", + "broker_username": "autoscaler-broker-user-blue" + } + ], + "catalog_path": "/home/vcap/app/api/default_catalog.json", + "catalog_schema_path": "/home/vcap/app/api/schemas/catalog.schema.json", + "info_file_path": "/home/vcap/app/api/default_info.json", + "policy_schema_path": "/home/vcap/app/api/policyvalidator/policy_json.schema.json", + "dashboard_redirect_uri": null, + "default_credential_type": "binding-secret", + "health": { + "server_config": { + "port": 1080 + } + }, + "db": { + "policy_db": { + "url": "REPLACEME_WITH_SERVICE", + "max_open_connections": 100, + "max_idle_connections": 10, + "connection_max_lifetime": "60s" + }, + "binding_db": { + "url": "REPLACEME_WITH_SERVICE", + "max_open_connections": 20, + "max_idle_connections": 10, + "connection_max_lifetime": "60s" + } + }, + "scaling_engine": { + "scaling_engine_url": "https://autoscaler-3119.scalingengine.service.cf.internal:6104", + "tls": { + "key_file": "/var/vcap/jobs/golangapiserver/config/certs/scalingengine/client.key", + "cert_file": "/var/vcap/jobs/golangapiserver/config/certs/scalingengine/client.crt", + "ca_file": "/var/vcap/jobs/golangapiserver/config/certs/scalingengine/ca.crt" + } + }, + "scheduler": { + "scheduler_url": "https://autoscaler-3119.autoscalerscheduler.service.cf.internal:6102", + "tls": { + "key_file": "/var/vcap/jobs/golangapiserver/config/certs/scheduler/client.key", + "cert_file": "/var/vcap/jobs/golangapiserver/config/certs/scheduler/client.crt", + "ca_file": "/var/vcap/jobs/golangapiserver/config/certs/scheduler/ca.crt" + } + }, + "event_generator": { + "event_generator_url": "https://autoscaler-3119.eventgenerator.service.cf.internal:6105", + "tls": { + "key_file": "/var/vcap/jobs/golangapiserver/config/certs/eventgenerator/client.key", + "cert_file": "/var/vcap/jobs/golangapiserver/config/certs/eventgenerator/client.crt", + "ca_file": "/var/vcap/jobs/golangapiserver/config/certs/eventgenerator/ca.crt" + } + }, + "scaling_rules": { + "cpu": { + "lower_threshold": 1, + "upper_threshold": 100 + }, + "cpuutil": { + "lower_threshold": 1, + "upper_threshold": 100 + }, + "diskutil": { + "lower_threshold": 1, + "upper_threshold": 100 + }, + "disk": { + "lower_threshold": 1, + "upper_threshold": 2048 + } + }, + "rate_limit": { + "valid_duration": "1s", + "max_amount": 10 + }, + "cred_helper_impl": "default" + } +} diff --git a/src/autoscaler/api/default_info.json b/src/autoscaler/api/default_info.json new file mode 100644 index 0000000000..918bd6550b --- /dev/null +++ b/src/autoscaler/api/default_info.json @@ -0,0 +1,8 @@ + + +{ + "name": "Autoscaler", + "build": "14.3.0", + "support": "https://github.com/cloudfoundry/app-autoscaler-release", + "description": "Automatically increase or decrease the number of application instances based on a policy you define." +} diff --git a/src/autoscaler/build-extension-file.sh b/src/autoscaler/build-extension-file.sh index 0726ab57e1..e85fe1abf6 100755 --- a/src/autoscaler/build-extension-file.sh +++ b/src/autoscaler/build-extension-file.sh @@ -84,6 +84,8 @@ resources: parameters: config: publicapiserver: + cf: + skip_ssl_validation: true metrics_forwarder: metrics_forwarder_url: ${METRICSFORWARDER_HOST}.\${default-domain} metrics_forwarder_mtls_url: ${METRICSFORWARDER_MTLS_HOST}.\${default-domain} diff --git a/src/autoscaler/metricsforwarder/config/config_test.go b/src/autoscaler/metricsforwarder/config/config_test.go index 7d3797a092..b87d97937d 100644 --- a/src/autoscaler/metricsforwarder/config/config_test.go +++ b/src/autoscaler/metricsforwarder/config/config_test.go @@ -9,23 +9,12 @@ import ( "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/testhelpers" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) -func bytesToFile(b []byte) string { - if len(b) == 0 { - return "" - } - - file, err := os.CreateTemp("", "") - Expect(err).NotTo(HaveOccurred()) - _, err = file.Write(b) - Expect(err).NotTo(HaveOccurred()) - return file.Name() -} - var _ = Describe("Config", func() { var ( conf *Config @@ -191,7 +180,7 @@ var _ = Describe("Config", func() { When("config is read from file", func() { JustBeforeEach(func() { - configFile = bytesToFile(configBytes) + configFile = testhelpers.BytesToFile(configBytes) conf, err = LoadConfig(configFile, mockVCAPConfigurationReader) }) diff --git a/src/autoscaler/testhelpers/files.go b/src/autoscaler/testhelpers/files.go index c92cd9a5bb..6bb63aab23 100644 --- a/src/autoscaler/testhelpers/files.go +++ b/src/autoscaler/testhelpers/files.go @@ -12,3 +12,15 @@ func LoadFile(filename string) string { FailOnError("Could not read file", err) return string(file) } + +func BytesToFile(b []byte) string { + if len(b) == 0 { + return "" + } + + file, err := os.CreateTemp("", "") + FailOnError("Could create file", err) + _, err = file.Write(b) + FailOnError("Could write file", err) + return file.Name() +} From 92cfb0728b68ad1445af289c2b9797ec2ab3fc7a Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 18 Oct 2024 15:14:12 +0200 Subject: [PATCH 35/39] Update apis default_info.json on new release-autoscaler --- ci/autoscaler/scripts/release-autoscaler.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci/autoscaler/scripts/release-autoscaler.sh b/ci/autoscaler/scripts/release-autoscaler.sh index eb88e94ee0..7f12236ba0 100755 --- a/ci/autoscaler/scripts/release-autoscaler.sh +++ b/ci/autoscaler/scripts/release-autoscaler.sh @@ -33,7 +33,11 @@ function create_release() { echo " - creating release '${version}' in '${build_path}' as ${release_file}" yq eval -i ".properties.\"autoscaler.apiserver.info.build\".default = \"${version}\"" jobs/golangapiserver/spec + yq eval -i ".build = \"${version}\"" src/autoscaler/api/default_info.json + git add jobs/golangapiserver/spec + git add src/autoscaler/api/default_info.json + [ "${CI}" = "true" ] && git commit -S -m "Updated release version to ${version} in golangapiserver" # shellcheck disable=SC2086 From 61ab5c4eb9b3f14f1f3e772f35a01f4dde6af7fe Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 18 Oct 2024 15:15:36 +0200 Subject: [PATCH 36/39] Sets bindingDB and policyDB from vcap services --- src/autoscaler/api/config/config.go | 48 ++++++++++++++++++++++-- src/autoscaler/api/config/config_test.go | 46 ++++++++++++++++++----- src/autoscaler/api/default_config.json | 14 ------- 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/src/autoscaler/api/config/config.go b/src/autoscaler/api/config/config.go index b2b1f721fc..9006b7cb28 100644 --- a/src/autoscaler/api/config/config.go +++ b/src/autoscaler/api/config/config.go @@ -99,7 +99,7 @@ type Config struct { Logging helpers.LoggingConfig `yaml:"logging"` BrokerServer helpers.ServerConfig `yaml:"broker_server"` Server helpers.ServerConfig `yaml:"public_api_server"` - DB map[string]db.DatabaseConfig `yaml:"db"` + Db map[string]db.DatabaseConfig `yaml:"db"` BrokerCredentials []BrokerCredentialsConfig `yaml:"broker_credentials"` APIClientId string `yaml:"api_client_id"` PlanCheck *PlanCheckConfig `yaml:"plan_check"` @@ -196,6 +196,48 @@ func loadVcapConfig(conf *Config, vcapReader configutil.VCAPConfigurationReader) return err } + if conf.Db == nil { + conf.Db = make(map[string]db.DatabaseConfig) + } + + if err := configurePolicyDb(conf, vcapReader); err != nil { + return err + } + + if err := configureBindingDb(conf, vcapReader); err != nil { + return err + } + return nil +} + +func configurePolicyDb(conf *Config, vcapReader configutil.VCAPConfigurationReader) error { + currentPolicyDb, ok := conf.Db[db.PolicyDb] + if !ok { + conf.Db[db.PolicyDb] = db.DatabaseConfig{} + } + + dbURL, err := vcapReader.MaterializeDBFromService(db.PolicyDb) + currentPolicyDb.URL = dbURL + if err != nil { + return err + } + conf.Db[db.PolicyDb] = currentPolicyDb + return nil +} + +func configureBindingDb(conf *Config, vcapReader configutil.VCAPConfigurationReader) error { + currentBindingDb, ok := conf.Db[db.BindingDb] + if !ok { + conf.Db[db.BindingDb] = db.DatabaseConfig{} + } + + dbURL, err := vcapReader.MaterializeDBFromService(db.BindingDb) + currentBindingDb.URL = dbURL + if err != nil { + return err + } + conf.Db[db.BindingDb] = currentBindingDb + return nil } @@ -221,7 +263,7 @@ func (c *Config) Validate() error { return err } - if c.DB[db.PolicyDb].URL == "" { + if c.Db[db.PolicyDb].URL == "" { return fmt.Errorf("Configuration error: PolicyDB URL is empty") } if c.Scheduler.SchedulerURL == "" { @@ -261,7 +303,7 @@ func (c *Config) Validate() error { return fmt.Errorf("Configuration error: ScalingRules.CPU.UpperThreshold is less than zero") } - if c.DB[db.BindingDb].URL == "" { + if c.Db[db.BindingDb].URL == "" { return fmt.Errorf("Configuration error: BindingDB URL is empty") } diff --git a/src/autoscaler/api/config/config_test.go b/src/autoscaler/api/config/config_test.go index c3fc183cff..8b642b170b 100644 --- a/src/autoscaler/api/config/config_test.go +++ b/src/autoscaler/api/config/config_test.go @@ -63,6 +63,34 @@ var _ = Describe("Config", func() { Expect(err).To(MatchError(MatchRegexp("publicapiserver config service not found"))) }) }) + + When("VCAP_SERVICES has relational db service bind to app for policy db", func() { + BeforeEach(func() { + mockVCAPConfigurationReader.GetServiceCredentialContentReturns([]byte(`{ "cred_helper_impl": "default" }`), nil) // #nosec G101 + expectedDbUrl = "postgres://foo:bar@postgres.example.com:5432/policy_db?sslcert=%2Ftmp%2Fclient_cert.sslcert&sslkey=%2Ftmp%2Fclient_key.sslkey&sslrootcert=%2Ftmp%2Fserver_ca.sslrootcert" // #nosec G101 + }) + + It("loads the db config from VCAP_SERVICES successfully", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(conf.Db[db.PolicyDb].URL).To(Equal(expectedDbUrl)) + actualDbName := mockVCAPConfigurationReader.MaterializeDBFromServiceArgsForCall(0) + Expect(actualDbName).To(Equal(db.PolicyDb)) + }) + }) + + When("VCAP_SERVICES has relational db service bind to app for binding db", func() { + BeforeEach(func() { + mockVCAPConfigurationReader.GetServiceCredentialContentReturns([]byte(`{ "cred_helper_impl": "default" }`), nil) // #nosec G101 + expectedDbUrl = "postgres://foo:bar@postgres.example.com:5432/binding_db?sslcert=%2Ftmp%2Fclient_cert.sslcert&sslkey=%2Ftmp%2Fclient_key.sslkey&sslrootcert=%2Ftmp%2Fserver_ca.sslrootcert" // #nosec G101 + }) + + It("loads the db config from VCAP_SERVICES successfully", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(conf.Db[db.BindingDb].URL).To(Equal(expectedDbUrl)) + actualDbName := mockVCAPConfigurationReader.MaterializeDBFromServiceArgsForCall(1) + Expect(actualDbName).To(Equal(db.BindingDb)) + }) + }) }) When("config is read from file", func() { @@ -105,14 +133,14 @@ var _ = Describe("Config", func() { CertFile: "/var/vcap/jobs/autoscaler/config/certs/api.crt", }, )) - Expect(conf.DB[db.BindingDb]).To(Equal( + Expect(conf.Db[db.BindingDb]).To(Equal( db.DatabaseConfig{ URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", MaxOpenConnections: 10, MaxIdleConnections: 5, ConnectionMaxLifetime: 60 * time.Second, })) - Expect(conf.DB[db.PolicyDb]).To(Equal( + Expect(conf.Db[db.PolicyDb]).To(Equal( db.DatabaseConfig{ URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", MaxOpenConnections: 10, @@ -175,14 +203,14 @@ var _ = Describe("Config", func() { Expect(conf.Logging.Level).To(Equal("info")) Expect(conf.BrokerServer.Port).To(Equal(8080)) Expect(conf.Server.Port).To(Equal(8081)) - Expect(conf.DB[db.BindingDb]).To(Equal( + Expect(conf.Db[db.BindingDb]).To(Equal( db.DatabaseConfig{ URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", MaxOpenConnections: 0, MaxIdleConnections: 0, ConnectionMaxLifetime: 0 * time.Second, })) - Expect(conf.DB[db.PolicyDb]).To(Equal( + Expect(conf.Db[db.PolicyDb]).To(Equal( db.DatabaseConfig{ URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", MaxOpenConnections: 0, @@ -230,14 +258,14 @@ rate_limit: Describe("Validate", func() { BeforeEach(func() { conf = &Config{} - conf.DB = make(map[string]db.DatabaseConfig) - conf.DB[db.BindingDb] = db.DatabaseConfig{ + conf.Db = make(map[string]db.DatabaseConfig) + conf.Db[db.BindingDb] = db.DatabaseConfig{ URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", MaxOpenConnections: 10, MaxIdleConnections: 5, ConnectionMaxLifetime: 60 * time.Second, } - conf.DB[db.PolicyDb] = db.DatabaseConfig{ + conf.Db[db.PolicyDb] = db.DatabaseConfig{ URL: "postgres://postgres:postgres@localhost/autoscaler?sslmode=disable", MaxOpenConnections: 10, MaxIdleConnections: 5, @@ -285,7 +313,7 @@ rate_limit: Context("when bindingdb url is not set", func() { BeforeEach(func() { - conf.DB[db.BindingDb] = db.DatabaseConfig{URL: ""} + conf.Db[db.BindingDb] = db.DatabaseConfig{URL: ""} }) It("should err", func() { Expect(err).To(MatchError(MatchRegexp("Configuration error: BindingDB URL is empty"))) @@ -294,7 +322,7 @@ rate_limit: Context("when policydb url is not set", func() { BeforeEach(func() { - conf.DB[db.PolicyDb] = db.DatabaseConfig{URL: ""} + conf.Db[db.PolicyDb] = db.DatabaseConfig{URL: ""} }) It("should err", func() { Expect(err).To(MatchError(MatchRegexp("Configuration error: PolicyDB URL is empty"))) diff --git a/src/autoscaler/api/default_config.json b/src/autoscaler/api/default_config.json index b7456fb217..3d68b2eef0 100644 --- a/src/autoscaler/api/default_config.json +++ b/src/autoscaler/api/default_config.json @@ -34,20 +34,6 @@ "port": 1080 } }, - "db": { - "policy_db": { - "url": "REPLACEME_WITH_SERVICE", - "max_open_connections": 100, - "max_idle_connections": 10, - "connection_max_lifetime": "60s" - }, - "binding_db": { - "url": "REPLACEME_WITH_SERVICE", - "max_open_connections": 20, - "max_idle_connections": 10, - "connection_max_lifetime": "60s" - } - }, "scaling_engine": { "scaling_engine_url": "https://autoscaler-3119.scalingengine.service.cf.internal:6104", "tls": { From ec54859f5c774eb595531f9528230454c845766c Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 18 Oct 2024 15:27:32 +0200 Subject: [PATCH 37/39] Add OpenAPI client and server generation step to MySQL and PostgreSQL workflows --- .github/workflows/mysql.yaml | 1 + .github/workflows/postgres.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/mysql.yaml b/.github/workflows/mysql.yaml index f19bb7766e..57a328100f 100644 --- a/.github/workflows/mysql.yaml +++ b/.github/workflows/mysql.yaml @@ -42,6 +42,7 @@ jobs: - uses: actions/checkout@v4 - name: make build run: | + make generate-openapi-generated-clients-and-servers make build db_type=mysql - name: make ${{ matrix.suite }} diff --git a/.github/workflows/postgres.yaml b/.github/workflows/postgres.yaml index 107b7db988..0987d596b9 100644 --- a/.github/workflows/postgres.yaml +++ b/.github/workflows/postgres.yaml @@ -48,6 +48,7 @@ jobs: POSTGRES_HOST: postgres POSTGRES_PORT: 5432 run: | + make generate-openapi-generated-clients-and-servers make build - name: make ${{ matrix.suite }} From 9db24ff0d7fc6b415faf1945c536537f79567b7f Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 18 Oct 2024 15:35:44 +0200 Subject: [PATCH 38/39] Refactor configuration loading and database connection in api/main.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Implement VCAP configuration reader for loading config • Streamline error handling for configuration and database initialization • Update references from conf.DB to conf.Db to match case changes • Modify PublicApiServer to use s.conf.Server instead of s.conf.PublicApiServer --- src/autoscaler/api/cmd/api/main.go | 26 +++++++++---------- .../api/publicapiserver/public_api_server.go | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/autoscaler/api/cmd/api/main.go b/src/autoscaler/api/cmd/api/main.go index 37ef97d2cd..c183faba6f 100644 --- a/src/autoscaler/api/cmd/api/main.go +++ b/src/autoscaler/api/cmd/api/main.go @@ -10,6 +10,7 @@ import ( "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/config" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/api/publicapiserver" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cf" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/configutil" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/cred_helper" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/db/sqldb" @@ -26,26 +27,23 @@ import ( func main() { var path string + var err error + var conf *config.Config + flag.StringVar(&path, "c", "", "config file") flag.Parse() - if path == "" { - _, _ = fmt.Fprintln(os.Stderr, "missing config file") - os.Exit(1) - } - configFile, err := os.Open(path) + vcapConfiguration, err := configutil.NewVCAPConfigurationReader() if err != nil { - _, _ = fmt.Fprintf(os.Stdout, "failed to open config file '%s' : %s\n", path, err.Error()) + _, _ = fmt.Fprintf(os.Stdout, "failed to read vcap configuration : %s\n", err.Error()) os.Exit(1) } - var conf *config.Config - conf, err = config.LoadConfig(configFile) + conf, err = config.LoadConfig(path, vcapConfiguration) if err != nil { _, _ = fmt.Fprintf(os.Stdout, "failed to read config file '%s' : %s\n", path, err.Error()) os.Exit(1) } - _ = configFile.Close() err = conf.Validate() if err != nil { @@ -59,11 +57,11 @@ func main() { members := grouper.Members{} - policyDb := sqldb.CreatePolicyDb(conf.DB[db.PolicyDb], logger) + policyDb := sqldb.CreatePolicyDb(conf.Db[db.PolicyDb], logger) defer func() { _ = policyDb.Close() }() - logger.Debug("Connected to PolicyDB", lager.Data{"dbConfig": conf.DB[db.PolicyDb]}) + logger.Debug("Connected to PolicyDB", lager.Data{"dbConfig": conf.Db[db.PolicyDb]}) - credentialProvider := cred_helper.CredentialsProvider(conf.CredHelperImpl, conf.StoredProcedureConfig, conf.DB, 10*time.Second, 10*time.Minute, logger, policyDb) + credentialProvider := cred_helper.CredentialsProvider(conf.CredHelperImpl, conf.StoredProcedureConfig, conf.Db, 10*time.Second, 10*time.Minute, logger, policyDb) defer func() { _ = credentialProvider.Close() }() httpStatusCollector := healthendpoint.NewHTTPStatusCollector("autoscaler", "golangapiserver") @@ -77,9 +75,9 @@ func main() { } logger.Debug("Successfully logged into CF", lager.Data{"API": conf.CF.API}) - bindingDB, err := sqldb.NewBindingSQLDB(conf.DB[db.BindingDb], logger.Session("bindingdb-db")) + bindingDB, err := sqldb.NewBindingSQLDB(conf.Db[db.BindingDb], logger.Session("bindingdb-db")) if err != nil { - logger.Error("failed to connect bindingdb database", err, lager.Data{"dbConfig": conf.DB[db.BindingDb]}) + logger.Error("failed to connect bindingdb database", err, lager.Data{"dbConfig": conf.Db[db.BindingDb]}) os.Exit(1) } defer func() { _ = bindingDB.Close() }() diff --git a/src/autoscaler/api/publicapiserver/public_api_server.go b/src/autoscaler/api/publicapiserver/public_api_server.go index 62f9f8bbdf..6d52c219ab 100644 --- a/src/autoscaler/api/publicapiserver/public_api_server.go +++ b/src/autoscaler/api/publicapiserver/public_api_server.go @@ -99,7 +99,7 @@ func (s *PublicApiServer) GetMtlsServer() (ifrit.Runner, error) { mainRouter := setupMainRouter(r, healthRouter) - return helpers.NewHTTPServer(s.logger, s.conf.PublicApiServer, mainRouter) + return helpers.NewHTTPServer(s.logger, s.conf.Server, mainRouter) } func NewPublicApiServer(logger lager.Logger, conf *config.Config, policyDB db.PolicyDB, From f315395ab0f3464a6251dc9e38a0abd82d2a1c78 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 18 Oct 2024 15:55:01 +0200 Subject: [PATCH 39/39] Fix typos --- src/autoscaler/api/cmd/api/api_suite_test.go | 8 ++++---- src/autoscaler/api/cmd/api/api_test.go | 8 ++++---- src/autoscaler/integration/components_test.go | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/autoscaler/api/cmd/api/api_suite_test.go b/src/autoscaler/api/cmd/api/api_suite_test.go index 2cc648c415..b545e2f0ff 100644 --- a/src/autoscaler/api/cmd/api/api_suite_test.go +++ b/src/autoscaler/api/cmd/api/api_suite_test.go @@ -122,7 +122,7 @@ var _ = SynchronizedBeforeSuite(func() []byte { CACertFile: filepath.Join(testCertDir, "autoscaler-ca.crt"), }, } - cfg.PublicApiServer = helpers.ServerConfig{ + cfg.Server = helpers.ServerConfig{ Port: publicApiPort, TLS: models.TLSCerts{ KeyFile: filepath.Join(testCertDir, "api.key"), @@ -131,15 +131,15 @@ var _ = SynchronizedBeforeSuite(func() []byte { }, } cfg.Logging.Level = "info" - cfg.DB = make(map[string]db.DatabaseConfig) + cfg.Db = make(map[string]db.DatabaseConfig) dbUrl := GetDbUrl() - cfg.DB[db.BindingDb] = db.DatabaseConfig{ + cfg.Db[db.BindingDb] = db.DatabaseConfig{ URL: dbUrl, MaxOpenConnections: 10, MaxIdleConnections: 5, ConnectionMaxLifetime: 10 * time.Second, } - cfg.DB[db.PolicyDb] = db.DatabaseConfig{ + cfg.Db[db.PolicyDb] = db.DatabaseConfig{ URL: dbUrl, MaxOpenConnections: 10, MaxIdleConnections: 5, diff --git a/src/autoscaler/api/cmd/api/api_test.go b/src/autoscaler/api/cmd/api/api_test.go index cced2671b1..89196c0b47 100644 --- a/src/autoscaler/api/cmd/api/api_test.go +++ b/src/autoscaler/api/cmd/api/api_test.go @@ -42,7 +42,7 @@ var _ = Describe("Api", func() { healthHttpClient = &http.Client{} apiHttpClient = NewPublicApiClient() - serverURL, err = url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfg.PublicApiServer.Port)) + serverURL, err = url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfg.Server.Port)) Expect(err).NotTo(HaveOccurred()) brokerURL, err = url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfg.BrokerServer.Port)) @@ -95,9 +95,9 @@ var _ = Describe("Api", func() { runner.startCheck = "" missingConfig := cfg - missingConfig.DB = make(map[string]db.DatabaseConfig) - missingConfig.DB[db.PolicyDb] = db.DatabaseConfig{URL: ""} - missingConfig.DB[db.BindingDb] = db.DatabaseConfig{URL: ""} + missingConfig.Db = make(map[string]db.DatabaseConfig) + missingConfig.Db[db.PolicyDb] = db.DatabaseConfig{URL: ""} + missingConfig.Db[db.BindingDb] = db.DatabaseConfig{URL: ""} var brokerCreds []config.BrokerCredentialsConfig missingConfig.BrokerCredentials = brokerCreds diff --git a/src/autoscaler/integration/components_test.go b/src/autoscaler/integration/components_test.go index 3d911ca479..b63220fcbb 100644 --- a/src/autoscaler/integration/components_test.go +++ b/src/autoscaler/integration/components_test.go @@ -182,7 +182,7 @@ func (components *Components) PrepareGolangApiServerConfig(dbURI string, publicA Logging: helpers.LoggingConfig{ Level: LOGLEVEL, }, - PublicApiServer: helpers.ServerConfig{ + Server: helpers.ServerConfig{ Port: publicApiPort, TLS: models.TLSCerts{ KeyFile: filepath.Join(testCertDir, "api.key"), @@ -352,7 +352,7 @@ func (components *Components) PrepareEventGeneratorConfig(dbUri string, port int EvaluatorCount: 1, TriggerArrayChannelSize: 1, }, - DB: egConfig.DBConfig{ + Db: egConfig.DBConfig{ PolicyDB: db.DatabaseConfig{ URL: dbUri, },