From 945a66ae3134f88bec3b1dd4b05d739960277ea5 Mon Sep 17 00:00:00 2001 From: Alan Moran Date: Fri, 24 May 2024 11:43:40 +0200 Subject: [PATCH] wip: refactor tests --- src/autoscaler/metricsforwarder/Makefile | 26 +-- .../server/custom_metrics_handlers_test.go | 80 +++++-- .../server/server_suite_test.go | 85 ++------ .../metricsforwarder/server/server_test.go | 200 +++++++++++------- 4 files changed, 225 insertions(+), 166 deletions(-) diff --git a/src/autoscaler/metricsforwarder/Makefile b/src/autoscaler/metricsforwarder/Makefile index 3c03aa3470..f101c5cac7 100644 --- a/src/autoscaler/metricsforwarder/Makefile +++ b/src/autoscaler/metricsforwarder/Makefile @@ -2,18 +2,7 @@ PR_NUMBER := $(shell gh pr view --json number --jq '.number') DEPLOYMENT_NAME ?= autoscaler-$(PR_NUMBER) VM_NAME := $(shell bosh -d $(DEPLOYMENT_NAME) vms --json | jq ".Tables | .[0] | .Rows | .[] | .instance" -r | grep metricsforwarder) - - - - -set-security-group: - $(eval ORG := $(shell cf target |grep "org\:" |cut -d':' -f2 | xargs)) - $(eval SPACE := $(shell cf target |grep "space\:" |cut -d':' -f2 | xargs)) - - cf create-security-group metricsforwarder security-group.json - cf bind-security-group metricsforwarder $(ORG) - -fetch-config: +fetch-config: cf-login # how to define variables in deployment name mkdir -p assets/certs/policy_db assets/certs/storedprocedure_db assets/certs/syslog_client @@ -49,5 +38,18 @@ fetch-config: sed -i '' 's|$(DEPLOYMENT_NAME).autoscalerpostgres.service.cf.internal|$(POSTGRES_IP)|g' metricsforwarder.yml sed -i '' 's|log-cache.service.cf.internal|$(LOG_CACHE_IP)|g' metricsforwarder.yml +set-security-group: + $(eval ORG := $(shell cf target |grep "org\:" |cut -d':' -f2 | xargs)) + $(eval SPACE := $(shell cf target |grep "space\:" |cut -d':' -f2 | xargs)) + + cf create-security-group metricsforwarder security-group.json + cf bind-security-group metricsforwarder $(ORG) + build-cf: @cd ../; make build-cf-metricsforwarder + + + +cf-login: + @cd ../../../; make cf-login + diff --git a/src/autoscaler/metricsforwarder/server/custom_metrics_handlers_test.go b/src/autoscaler/metricsforwarder/server/custom_metrics_handlers_test.go index 9eea0d12ef..5df4dd34f0 100644 --- a/src/autoscaler/metricsforwarder/server/custom_metrics_handlers_test.go +++ b/src/autoscaler/metricsforwarder/server/custom_metrics_handlers_test.go @@ -1,17 +1,21 @@ package server_test import ( - "bytes" "encoding/json" + "fmt" + "path/filepath" "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/config" . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/server" "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/ginkgomon_v2" "net/http" "net/http/httptest" @@ -20,7 +24,6 @@ import ( ) var _ = Describe("MetricHandler", func() { - var ( handler *CustomMetricsHandler @@ -42,9 +45,44 @@ var _ = Describe("MetricHandler", func() { scalingPolicy *models.ScalingPolicy ) - BeforeEach(func() { + testCertDir := "../../../../test-certs" + loggregatorConfig := config.LoggregatorConfig{ + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "metron.key"), + CertFile: filepath.Join(testCertDir, "metron.crt"), + CACertFile: filepath.Join(testCertDir, "loggregator-ca.crt"), + }, + MetronAddress: "invalid-host-name-blah:12345", + } + serverConfig := helpers.ServerConfig{ + Port: 2223 + GinkgoParallelProcess(), + } + + loggerConfig := helpers.LoggingConfig{ + Level: "debug", + } + + conf := &config.Config{ + Server: serverConfig, + Logging: loggerConfig, + LoggregatorConfig: loggregatorConfig, + } + policyDB = &fakes.FakePolicyDB{} + allowedMetricCache = *cache.New(10*time.Minute, -1) + httpStatusCollector := &fakes.FakeHTTPStatusCollector{} + rateLimiter = &fakes.FakeLimiter{} + fakeCredentials = &fakes.FakeCredentials{} + logger := lager.NewLogger("metrichandler-test") + logger.RegisterSink(lager.NewWriterSink(GinkgoWriter, lager.DEBUG)) + + 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) + policyDB = &fakes.FakePolicyDB{} metricsforwarder = &fakes.FakeMetricForwarder{} allowedMetricCache = *cache.New(10*time.Minute, -1) @@ -52,16 +90,31 @@ var _ = Describe("MetricHandler", func() { vars = make(map[string]string) resp = httptest.NewRecorder() handler = NewCustomMetricsHandler(logger, metricsforwarder, policyDB, allowedMetricCache) + serverUrl = fmt.Sprintf("http://127.0.0.1:%d", conf.Server.Port) allowedMetricCache.Flush() }) + JustBeforeEach(func() { + + }) + AfterEach(func() { + ginkgomon_v2.Interrupt(serverProcess) + // wait for the server to shutdown + // this is necessary because the server is running in a separate goroutine + // and the test can exit before the server has a chance to shutdown + + Eventually(serverProcess.Wait(), 5).Should(Receive()) + + }) + + JustBeforeEach(func() { + req = CreateRequest(body, "/v1/apps/an-app-id/metrics") + req.SetBasicAuth("username", "password") + vars["appid"] = "an-app-id" + handler.VerifyCredentialsAndPublishMetrics(resp, req, vars) + }) + Describe("PublishMetrics", func() { - JustBeforeEach(func() { - req = CreateRequest(body) - Expect(err).ToNot(HaveOccurred()) - vars["appid"] = "an-app-id" - handler.VerifyCredentialsAndPublishMetrics(resp, req, vars) - }) Context("when a request to publish custom metrics comes with malformed request body", func() { BeforeEach(func() { @@ -71,7 +124,7 @@ var _ = Describe("MetricHandler", func() { }, nil) body = []byte(`{ "instance_index":0, - "test" : + "test" : "metrics":[ { "name":"custom_metric1", @@ -268,10 +321,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 827209fd79..24d37867ae 100644 --- a/src/autoscaler/metricsforwarder/server/server_suite_test.go +++ b/src/autoscaler/metricsforwarder/server/server_suite_test.go @@ -1,44 +1,38 @@ package server_test import ( - "fmt" + "bytes" + "net/http" "os" - "path/filepath" - "time" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/config" - . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/server" - "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" - - "code.cloudfoundry.org/lager/v3" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/patrickmn/go-cache" "github.com/tedsuo/ifrit" - "github.com/tedsuo/ifrit/ginkgomon_v2" "testing" ) +func TestServer(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Server Suite") +} + var ( - serverProcess ifrit.Process - serverUrl string policyDB *fakes.FakePolicyDB rateLimiter *fakes.FakeLimiter fakeCredentials *fakes.FakeCredentials - allowedMetricCache cache.Cache -) - -func TestServer(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Server Suite") -} + httpStatusCollector *fakes.FakeHTTPStatusCollector + serverUrl string + httpServer ifrit.Runner + serverProcess ifrit.Process -var _ = SynchronizedBeforeSuite(func() []byte { + conf *config.Config +) +var _ = BeforeSuite(func() { _, err := os.ReadFile("../../../../test-certs/metron.key") Expect(err).NotTo(HaveOccurred()) @@ -47,49 +41,12 @@ var _ = SynchronizedBeforeSuite(func() []byte { _, err = os.ReadFile("../../../../test-certs/loggregator-ca.crt") Expect(err).NotTo(HaveOccurred()) - - return nil -}, func(_ []byte) { - - testCertDir := "../../../../test-certs" - loggregatorConfig := config.LoggregatorConfig{ - TLS: models.TLSCerts{ - KeyFile: filepath.Join(testCertDir, "metron.key"), - CertFile: filepath.Join(testCertDir, "metron.crt"), - CACertFile: filepath.Join(testCertDir, "loggregator-ca.crt"), - }, - MetronAddress: "invalid-host-name-blah:12345", - } - serverConfig := helpers.ServerConfig{ - Port: 2222 + GinkgoParallelProcess(), - } - - loggerConfig := helpers.LoggingConfig{ - Level: "debug", - } - - conf := &config.Config{ - Server: serverConfig, - Logging: loggerConfig, - LoggregatorConfig: loggregatorConfig, - } - policyDB = &fakes.FakePolicyDB{} - allowedMetricCache = *cache.New(10*time.Minute, -1) - httpStatusCollector := &fakes.FakeHTTPStatusCollector{} - rateLimiter = &fakes.FakeLimiter{} - fakeCredentials = &fakes.FakeCredentials{} - - logger := lager.NewLogger("server_suite_test") - logger.RegisterSink(lager.NewWriterSink(GinkgoWriter, lager.DEBUG)) - - 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) }) -var _ = SynchronizedAfterSuite(func() { - ginkgomon_v2.Interrupt(serverProcess) -}, func() { -}) +func CreateRequest(body []byte, path string) *http.Request { + req, err := http.NewRequest(http.MethodPost, serverUrl+path, bytes.NewReader(body)) + Expect(err).ToNot(HaveOccurred()) + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ=") + return req +} diff --git a/src/autoscaler/metricsforwarder/server/server_test.go b/src/autoscaler/metricsforwarder/server/server_test.go index 193796f024..5302c4217d 100644 --- a/src/autoscaler/metricsforwarder/server/server_test.go +++ b/src/autoscaler/metricsforwarder/server/server_test.go @@ -1,30 +1,106 @@ package server_test import ( - "bytes" + "encoding/base64" "encoding/json" "errors" + "fmt" + "math/rand" "net/http" + "path/filepath" + "time" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/fakes" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/helpers" + "code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/config" + . "code.cloudfoundry.org/app-autoscaler/src/autoscaler/metricsforwarder/server" "code.cloudfoundry.org/app-autoscaler/src/autoscaler/models" + "code.cloudfoundry.org/lager/v3" _ "github.com/jackc/pgx/v5/stdlib" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/patrickmn/go-cache" + "github.com/tedsuo/ifrit/ginkgomon_v2" ) var _ = Describe("CustomMetrics Server", func() { + var ( resp *http.Response req *http.Request body []byte err error scalingPolicy *models.ScalingPolicy + + allowedMetricCache cache.Cache + + customMetrics []*models.CustomMetric ) + BeforeEach(func() { + testCertDir := "../../../../test-certs" + + loggregatorConfig := config.LoggregatorConfig{ + TLS: models.TLSCerts{ + KeyFile: filepath.Join(testCertDir, "metron.key"), + CertFile: filepath.Join(testCertDir, "metron.crt"), + CACertFile: filepath.Join(testCertDir, "loggregator-ca.crt"), + }, + MetronAddress: "invalid-host-name-blah:12345", + } + + //random number from 1 to 1000 + //to avoid port conflict in parallel test + + rand.Seed(time.Now().UnixNano()) + randomNumber := rand.Intn(1000) + 1 + + serverConfig := helpers.ServerConfig{ + Port: 2222 + randomNumber, + } + + loggerConfig := helpers.LoggingConfig{ + Level: "debug", + } + + conf = &config.Config{ + Server: serverConfig, + Logging: loggerConfig, + LoggregatorConfig: loggregatorConfig, + } + policyDB = &fakes.FakePolicyDB{} + allowedMetricCache = *cache.New(10*time.Minute, -1) + httpStatusCollector = &fakes.FakeHTTPStatusCollector{} + rateLimiter = &fakes.FakeLimiter{} + fakeCredentials = &fakes.FakeCredentials{} + + customMetrics = []*models.CustomMetric{ + { + Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 1, AppGUID: "an-app-id", + }, + } + }) + + JustBeforeEach(func() { + logger := lager.NewLogger("server_suite_test") + logger.RegisterSink(lager.NewWriterSink(GinkgoWriter, lager.DEBUG)) + 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) + }) + + AfterEach(func() { + ginkgomon_v2.Interrupt(serverProcess) + fakeCredentials = nil + serverUrl = "" + }) + Context("when a request to forward custom metrics comes", func() { BeforeEach(func() { - scalingPolicy = &models.ScalingPolicy{ InstanceMin: 1, InstanceMax: 6, @@ -36,25 +112,19 @@ var _ = Describe("CustomMetrics Server", func() { CoolDownSeconds: 60, Adjustment: "+1"}}} policyDB.GetAppPolicyReturns(scalingPolicy, nil) - customMetrics := []*models.CustomMetric{ - { - Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 1, AppGUID: "an-app-id", - }, - } - body, err = json.Marshal(models.MetricsConsumer{InstanceIndex: 0, CustomMetrics: customMetrics}) - Expect(err).NotTo(HaveOccurred()) - fakeCredentials.ValidateReturns(true, nil) - client := &http.Client{} - req, err = http.NewRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", bytes.NewReader(body)) - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ=") - resp, err = client.Do(req) - Expect(err).NotTo(HaveOccurred()) }) It("returns status code 200", func() { + Expect(fakeCredentials.ValidateCallCount()).To(Equal(0)) + body, err = json.Marshal(models.MetricsConsumer{InstanceIndex: 0, CustomMetrics: customMetrics}) + Expect(err).NotTo(HaveOccurred()) + + client := &http.Client{} + + req = CreateRequest(body, "/v1/apps/san-app-id/metrics") + resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) resp.Body.Close() @@ -63,19 +133,17 @@ var _ = Describe("CustomMetrics Server", func() { Context("when a request to forward custom metrics comes without Authorization header", func() { BeforeEach(func() { - body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) - Expect(err).NotTo(HaveOccurred()) - client := &http.Client{} - req, err = http.NewRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", bytes.NewReader(body)) - req.Header.Add("Content-Type", "application/json") - - fakeCredentials.ValidateReturns(true, nil) - - resp, err = client.Do(req) - Expect(err).NotTo(HaveOccurred()) + fakeCredentials.ValidateReturns(false, nil) }) It("returns status code 401", func() { + Expect(fakeCredentials.ValidateCallCount()).To(Equal(0)) + body, err = json.Marshal(models.MetricsConsumer{InstanceIndex: 0, CustomMetrics: customMetrics}) + Expect(err).NotTo(HaveOccurred()) + client := &http.Client{} + req = CreateRequest(body, "/v1/apps/san-app-id/metrics") + resp, err = client.Do(req) + Expect(fakeCredentials.ValidateCallCount()).To(Equal(1)) Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) resp.Body.Close() @@ -84,65 +152,55 @@ var _ = Describe("CustomMetrics Server", func() { Context("when a request to forward custom metrics comes without 'Basic'", func() { BeforeEach(func() { - body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) - Expect(err).NotTo(HaveOccurred()) - client := &http.Client{} - req, err = http.NewRequest("POST", serverUrl+"/v1/apps/san-app-id/metrics", bytes.NewReader(body)) - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", "dXNlcm5hbWU6cGFzc3dvcmQ=") - fakeCredentials.ValidateReturns(true, nil) - - resp, err = client.Do(req) - Expect(err).NotTo(HaveOccurred()) }) It("returns status code 401", func() { + Expect(fakeCredentials.ValidateCallCount()).To(Equal(0)) + body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) + Expect(err).NotTo(HaveOccurred()) + req = CreateRequest(body, "/v1/apps/san-app-id/metrics") + client := &http.Client{} + resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + Expect(fakeCredentials.ValidateCallCount()).To(Equal(1)) resp.Body.Close() }) }) Context("when a request to forward custom metrics comes with wrong user credentials", func() { BeforeEach(func() { + fakeCredentials.ValidateReturns(false, errors.New("wrong credentials")) + }) + + It("returns status code 401", func() { + Expect(fakeCredentials.ValidateCallCount()).To(Equal(0)) body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) Expect(err).NotTo(HaveOccurred()) - fakeCredentials.ValidateReturns(false, errors.New("wrong credentials")) - client := &http.Client{} - req, err = http.NewRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", bytes.NewReader(body)) - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", "Basic M2YxZWY2MTJiMThlYTM5YmJlODRjZjUxMzY4MWYwYjc6YWYyNjk1Y2RmZDE0MzA4NThhMWY3MzJhYTI5NTQ2ZTk=") - + req = CreateRequest(body, "/v1/apps/an-app-id/metrics") resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) - }) - - It("returns status code 401", func() { - Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusUnauthorized)) + Expect(fakeCredentials.ValidateCallCount()).To(Equal(1)) resp.Body.Close() }) }) Context("when a request to forward custom metrics comes with unmatched metric types", func() { BeforeEach(func() { - body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) - Expect(err).NotTo(HaveOccurred()) - fakeCredentials.ValidateReturns(true, nil) - - client := &http.Client{} - req, err = http.NewRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", bytes.NewReader(body)) - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ=") - resp, err = client.Do(req) - Expect(err).NotTo(HaveOccurred()) }) It("returns status code 400", func() { + Expect(fakeCredentials.ValidateCallCount()).To(Equal(0)) + body, err = json.Marshal(models.CustomMetric{Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 123, AppGUID: "an-app-id"}) + Expect(err).NotTo(HaveOccurred()) + client := &http.Client{} + req = CreateRequest(body, "/v1/apps/an-app-id/metrics") + resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) resp.Body.Close() @@ -162,33 +220,29 @@ var _ = Describe("CustomMetrics Server", func() { Operator: ">", CoolDownSeconds: 60, Adjustment: "+1"}}} + policyDB.GetAppPolicyReturns(scalingPolicy, nil) - customMetrics := []*models.CustomMetric{ - { - Name: "queuelength", Value: 12, Unit: "unit", InstanceIndex: 1, AppGUID: "an-app-id", - }, - } - body, err = json.Marshal(models.MetricsConsumer{InstanceIndex: 0, CustomMetrics: customMetrics}) - Expect(err).NotTo(HaveOccurred()) fakeCredentials.ValidateReturns(true, nil) - client := &http.Client{} - req, err = http.NewRequest("POST", serverUrl+"/v1/apps/an-app-id/metrics", bytes.NewReader(body)) - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ=") - resp, err = client.Do(req) - Expect(err).NotTo(HaveOccurred()) - }) - AfterEach(func() { - rateLimiter.ExceedsLimitReturns(false) }) It("returns status code 429", func() { + Expect(fakeCredentials.ValidateCallCount()).To(Equal(0)) + body, err = json.Marshal(models.MetricsConsumer{InstanceIndex: 0, CustomMetrics: customMetrics}) + Expect(err).NotTo(HaveOccurred()) + + client := &http.Client{} + req = CreateRequest(body, "/v1/apps/an-app-id/metrics") + resp, err = client.Do(req) Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusTooManyRequests)) resp.Body.Close() }) }) - }) + +func basicAuth(username, password string) string { + auth := username + ":" + password + return base64.StdEncoding.EncodeToString([]byte(auth)) +}