From cd939cc20366170cc93ba9ded475c99e7378ea4a Mon Sep 17 00:00:00 2001 From: Glen Rodgers Date: Thu, 29 Aug 2019 17:07:22 +0100 Subject: [PATCH] Add pks-login command to Hammer (#6) * Add pks-login command to Hammer * Requires the API for PKS (configurable in OpsManager) to be in Env file * Remove parseUrl and inline parsing of urls and ip * Run go mod tidy --- README.md | 5 +- commands/cf_login.go | 2 +- commands/cf_login_test.go | 2 +- commands/pks_login.go | 35 +++++ commands/pks_login_test.go | 141 ++++++++++++++++++ environment/config.go | 32 +++- environment/config_test.go | 8 + environment/fixtures/lemon.json | 5 + environment/fixtures/reduced.json | 3 + go.mod | 2 +- go.sum | 4 +- integration/cf_login_command_test.go | 2 +- .../fixtures/claim_manatee_response.json | 5 + integration/fixtures/pks_script.sh | 5 + integration/pks_login_command_test.go | 42 ++++++ main.go | 9 ++ pks/login_runner.go | 35 +++++ pks/login_runner_test.go | 129 ++++++++++++++++ pks/pks_suite_test.go | 23 +++ 19 files changed, 477 insertions(+), 12 deletions(-) create mode 100644 commands/pks_login.go create mode 100644 commands/pks_login_test.go create mode 100644 integration/fixtures/pks_script.sh create mode 100644 integration/pks_login_command_test.go create mode 100644 pks/login_runner.go create mode 100644 pks/login_runner_test.go create mode 100644 pks/pks_suite_test.go diff --git a/README.md b/README.md index bd1fe61..69f0142 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,10 @@ In order to run the `hammer` tool against a given environment you need to have a }, "ops_manager_private_key": "OPSMAN-RSA-PRIVATE-KEY", "ops_manager_public_ip": "OPSMAN-PUBLIC-IP", - "sys_domain": "PAS-SYSTEM-DOMAIN" + "sys_domain": "PAS-SYSTEM-DOMAIN", + "pks_api": { + "url": "PKS-API-FQDN" + } } ``` This file can then be passed into the tool via `hammer -t path-to-env-config.json `. diff --git a/commands/cf_login.go b/commands/cf_login.go index 6d5edb3..e839184 100644 --- a/commands/cf_login.go +++ b/commands/cf_login.go @@ -29,7 +29,7 @@ func (c *CFLoginCommand) Execute(args []string) error { return err } - c.UI.DisplayText(fmt.Sprintf("Logging in to: %s\n", data.OpsManager.URL.String())) + c.UI.DisplayText(fmt.Sprintf("Logging in to CF at: %s\n", data.OpsManager.URL.String())) return c.CFLoginRunner.Run(data, c.File) } diff --git a/commands/cf_login_test.go b/commands/cf_login_test.go index 9850204..7c2e78a 100644 --- a/commands/cf_login_test.go +++ b/commands/cf_login_test.go @@ -73,7 +73,7 @@ var _ = Describe("cf login command", func() { It("displays that the cf is being logged into", func() { Expect(ui.DisplayTextCallCount()).To(Equal(1)) - Expect(ui.DisplayTextArgsForCall(0)).To(Equal("Logging in to: www.test-cf.io\n")) + Expect(ui.DisplayTextArgsForCall(0)).To(Equal("Logging in to CF at: www.test-cf.io\n")) }) It("runs the cf login tool using the retrieved environment config", func() { diff --git a/commands/pks_login.go b/commands/pks_login.go new file mode 100644 index 0000000..896a575 --- /dev/null +++ b/commands/pks_login.go @@ -0,0 +1,35 @@ +/* +Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. + +This program and the accompanying materials are made available under the terms of the under the Apache License, Version 2.0 (the "License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +package commands + +import ( + "fmt" +) + +type PKSLoginCommand struct { + TargetConfig string `short:"t" long:"target" env:"TARGET_ENVIRONMENT_CONFIG" hidden:"true"` + File bool `short:"f" long:"file" description:"write a script file but do not run it"` + + Env EnvReader + UI UI + PKSLoginRunner ToolRunner +} + +func (c *PKSLoginCommand) Execute(args []string) error { + data, err := c.Env.Read(c.TargetConfig) + if err != nil { + return err + } + + c.UI.DisplayText(fmt.Sprintf("Logging in to PKS at: %s\n", data.OpsManager.URL.String())) + + return c.PKSLoginRunner.Run(data, c.File) +} diff --git a/commands/pks_login_test.go b/commands/pks_login_test.go new file mode 100644 index 0000000..5ac1353 --- /dev/null +++ b/commands/pks_login_test.go @@ -0,0 +1,141 @@ +/* +Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. + +This program and the accompanying materials are made available under the terms of the under the Apache License, Version 2.0 (the "License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +package commands_test + +import ( + "fmt" + "net/url" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + fakes "github.com/pivotal/hammer/commands/commandsfakes" + "github.com/pivotal/hammer/environment" + + . "github.com/pivotal/hammer/commands" +) + +var _ = Describe("pks login command", func() { + var ( + err error + command *PKSLoginCommand + + envReader *fakes.FakeEnvReader + ui *fakes.FakeUI + pksLoginRunner *fakes.FakeToolRunner + commandArgs []string + ) + + BeforeEach(func() { + envReader = new(fakes.FakeEnvReader) + ui = new(fakes.FakeUI) + pksLoginRunner = new(fakes.FakeToolRunner) + commandArgs = []string{"arg1", "arg2"} + + command = &PKSLoginCommand{ + Env: envReader, + UI: ui, + PKSLoginRunner: pksLoginRunner, + File: true, + } + }) + + JustBeforeEach(func() { + err = command.Execute(commandArgs) + }) + + When("retrieving the environment config errors", func() { + BeforeEach(func() { + envReader.ReadReturns(environment.Config{}, fmt.Errorf("env-reader-error")) + }) + + It("doesn't attempt to run the pks login tool", func() { + Expect(pksLoginRunner.RunCallCount()).To(Equal(0)) + }) + + It("propagates the error", func() { + Expect(err).To(MatchError("env-reader-error")) + }) + }) + + When("retrieving the environment config is successful", func() { + BeforeEach(func() { + url, _ := url.Parse("www.test-pks.io") + envReader.ReadReturns(environment.Config{OpsManager: environment.OpsManager{URL: *url}}, nil) + }) + + It("displays that the pks is being logged into", func() { + Expect(ui.DisplayTextCallCount()).To(Equal(1)) + Expect(ui.DisplayTextArgsForCall(0)).To(Equal("Logging in to PKS at: www.test-pks.io\n")) + }) + + It("runs the pks login tool using the retrieved environment config", func() { + Expect(pksLoginRunner.RunCallCount()).To(Equal(1)) + + environmentConfig, _, _ := pksLoginRunner.RunArgsForCall(0) + + expectedURL, _ := url.Parse("www.test-pks.io") + Expect(environmentConfig).To(BeEquivalentTo(environment.Config{OpsManager: environment.OpsManager{URL: *expectedURL}})) + }) + + When("run with the file flag set", func() { + BeforeEach(func() { + command.File = true + }) + + It("runs the pks login tool in dry run mode", func() { + Expect(pksLoginRunner.RunCallCount()).To(Equal(1)) + + _, dryRun, _ := pksLoginRunner.RunArgsForCall(0) + Expect(dryRun).To(BeTrue()) + }) + }) + + When("run with the file flag unset", func() { + BeforeEach(func() { + command.File = false + }) + + It("runs the pks login tool in non-dry run mode", func() { + Expect(pksLoginRunner.RunCallCount()).To(Equal(1)) + + _, dryRun, _ := pksLoginRunner.RunArgsForCall(0) + Expect(dryRun).To(BeFalse()) + }) + }) + + It("runs the pks login tool with no additional args", func() { + Expect(pksLoginRunner.RunCallCount()).To(Equal(1)) + + _, _, args := pksLoginRunner.RunArgsForCall(0) + Expect(args).To(BeEmpty()) + }) + + When("running the pks login tool is successful", func() { + BeforeEach(func() { + pksLoginRunner.RunReturns(nil) + }) + + It("doesn't error", func() { + Expect(err).NotTo(HaveOccurred()) + }) + }) + + When("running the pks login tool errors", func() { + BeforeEach(func() { + pksLoginRunner.RunReturns(fmt.Errorf("pks-login-runnner-error")) + }) + + It("propagates the error", func() { + Expect(err).To(MatchError("pks-login-runnner-error")) + }) + }) + }) +}) diff --git a/environment/config.go b/environment/config.go index dc5bda9..dda6406 100644 --- a/environment/config.go +++ b/environment/config.go @@ -28,12 +28,19 @@ type OpsManager struct { PrivateKey string } +type PKSApi struct { + Username string + Password string + URL url.URL +} + type Config struct { Name string Version version.Version CFDomain string AppsDomain string OpsManager OpsManager + PKSApi PKSApi PasSubnet string ServiceSubnet string AZs []string @@ -54,6 +61,11 @@ type environmentReader struct { Username string `json:"username"` Password string `json:"password"` } `json:"ops_manager"` + PKSApi struct { + Username string `json:"uaa_admin_user"` + Password string `json:"uaa_admin_password"` + URL string `json:"url"` + } `json:"pks_api"` } func FromFile(path string) (Config, error) { @@ -82,16 +94,21 @@ func newLockfile(data environmentReader) (Config, error) { } } - parsedURL, err := url.Parse(data.OpsManager.URL) + parsedOpsManagerURL, err := url.Parse(data.OpsManager.URL) if err != nil { return Config{}, err } - ip := net.ParseIP(data.IP) - if ip == nil { + opsManagerIp := net.ParseIP(data.IP) + if opsManagerIp == nil { return Config{}, fmt.Errorf("Could not parse IP address: %s", data.IP) } + parsedPKSApiURL, err := url.Parse(data.PKSApi.URL) + if err != nil { + return Config{}, err + } + return Config{ Name: data.Name, Version: *parsedVersion, @@ -103,9 +120,14 @@ func newLockfile(data environmentReader) (Config, error) { OpsManager: OpsManager{ Username: data.OpsManager.Username, Password: data.OpsManager.Password, - URL: *parsedURL, - IP: ip, + URL: *parsedOpsManagerURL, + IP: opsManagerIp, PrivateKey: data.PrivateKey, }, + PKSApi: PKSApi{ + Username: data.PKSApi.Username, + Password: data.PKSApi.Password, + URL: *parsedPKSApiURL, + }, }, nil } diff --git a/environment/config_test.go b/environment/config_test.go index 771999b..e85ad13 100644 --- a/environment/config_test.go +++ b/environment/config_test.go @@ -63,6 +63,11 @@ func checkMatchLemon(e Config) { "PasSubnet": Equal("lemon-pas-subnet"), "ServiceSubnet": Equal("lemon-services-subnet"), "AZs": Equal([]string{"us-central1-f", "us-central1-a", "us-central1-c"}), + "PKSApi": MatchFields(IgnoreExtras, Fields{ + "Username": Equal("pivotalcf"), + "Password": Equal("fakePassword"), + "URL": Equal(mustParseURL("https://api.pks.lemon-lemon.cf-app.com")), + }), "OpsManager": MatchAllFields(Fields{ "Username": Equal("pivotalcf"), "Password": Equal("fakePassword"), @@ -77,6 +82,9 @@ func checkMatchReduced(e Config) { Expect(e).To(MatchFields(IgnoreExtras, Fields{ "Name": Equal("reduced-config"), "CFDomain": Equal("sys.reduced-config.cf-app.com"), + "PKSApi": MatchFields(IgnoreExtras, Fields{ + "URL": Equal(mustParseURL("https://api.pks.reduced-config.cf-app.com")), + }), "OpsManager": MatchFields(IgnoreExtras, Fields{ "Username": Equal("pivotalcf"), "Password": Equal("fakePassword"), diff --git a/environment/fixtures/lemon.json b/environment/fixtures/lemon.json index fba7f6b..570942f 100644 --- a/environment/fixtures/lemon.json +++ b/environment/fixtures/lemon.json @@ -32,6 +32,11 @@ "ops_manager_public_key": "ubuntu:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGPKi11B84ogwuGf4Zmq8/VJjvCxgLcpSLD5xuL0FDR1TCA1aeMidb9Pi/DKMfUORry0XbpllgWSbs3YbIEG6FFJEKe68zra/pJSyHH8UE41eULLGWP0ybdAPv5Tvq5D/g1ZtYUnX0jye333akVddM3qh52Dthye0+VKVt4MOH+q2XBLFQ66rhDd6U65MI9YzVpoPVam2WYngGY0a+Am+eo9pww36zk0/osDzLrD57hg3A0v+Otft7l/EiV6elTqLJxHg8BpO0bxWCdBFyJ/i0v+upfrbSgiMAP1QXjZkcSdDeLg0bS5QFX8Nei1/ZjvIHWQHwSUxDfIi67ly+BNmgQHuR2FPFemEkx7EqicV7TWuxfEevMc1edFFkdtzKv1tG8yQEtxPhX6tamz4TBmsu2a5fZ8er47bTI4x7tgCJdNEjhsDVlA1jsqU82i7JLjfXFhbu1hwfBVmKWaaWSeGYMxC8hAGKFXg77Ebo/Nd87urBApmwTOTj5sOFKrOz2BSqiZHYJ/2n8NeH7+CCXgdZQnHrt+u5DyNSpJSorQkNDgQzPunh9TmAVOh2of8gfqryjht6UayWyw1g3S1B1KXgP5RlvfnwL0yhOryLDk3qcpaivsAqFQAKVM6X9KHIbn83rljJxQQ7SmPTJ6TsSKtdZxFlWZklHz4e/smLnclOzQ==\n", "ops_manager_subnet": "lemon-management-subnet", "ops_manager_version": "2.0-build.314", + "pks_api": { + "uaa_admin_password": "fakePassword", + "uaa_admin_user": "pivotalcf", + "url": "https://api.pks.lemon-lemon.cf-app.com" + }, "project": "cf-toolsmiths-pool-us-2", "region": "us-central1", "service_network_name": "lemon-pcf-network", diff --git a/environment/fixtures/reduced.json b/environment/fixtures/reduced.json index 3f8db2b..d429799 100644 --- a/environment/fixtures/reduced.json +++ b/environment/fixtures/reduced.json @@ -1,5 +1,8 @@ { "name": "reduced-config", + "pks_api": { + "url": "https://api.pks.reduced-config.cf-app.com" + }, "ops_manager": { "password": "fakePassword", "url": "https://pcf.reduced-config.cf-app.com", diff --git a/go.mod b/go.mod index b72ff30..c035ea7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/pivotal/hammer go 1.12 require ( - github.com/golang/protobuf v1.3.1 // indirect + github.com/golang/protobuf v1.3.2 // indirect github.com/hashicorp/go-version v1.2.0 github.com/jessevdk/go-flags v1.4.0 github.com/kr/pretty v0.1.0 // indirect diff --git a/go.sum b/go.sum index a994cd8..e43ed29 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= diff --git a/integration/cf_login_command_test.go b/integration/cf_login_command_test.go index 93b28ea..1088af0 100644 --- a/integration/cf_login_command_test.go +++ b/integration/cf_login_command_test.go @@ -29,7 +29,7 @@ var _ = Describe("CF", func() { Eventually(session).Should(Exit(0)) Eventually(string(session.Err.Contents())).Should(Equal("")) - Eventually(session.Out).Should(Say("Logging in to: https://pcf.manatee.cf-app.com")) + Eventually(session.Out).Should(Say("Logging in to CF at: https://pcf.manatee.cf-app.com")) output := strings.TrimSuffix(string(session.Out.Contents()), "\n") lines := strings.Split(output, "\n") diff --git a/integration/fixtures/claim_manatee_response.json b/integration/fixtures/claim_manatee_response.json index ade6ebc..c0b3f5d 100644 --- a/integration/fixtures/claim_manatee_response.json +++ b/integration/fixtures/claim_manatee_response.json @@ -32,6 +32,11 @@ "ops_manager_public_key": "ubuntu:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGPKi11B84ogwuGf4Zmq8/VJjvCxgLcpSLD5xuL0FDR1TCA1aeMidb9Pi/DKMfUORry0XbpllgWSbs3YbIEG6FFJEKe68zra/pJSyHH8UE41eULLGWP0ybdAPv5Tvq5D/g1ZtYUnX0jye333akVddM3qh52Dthye0+VKVt4MOH+q2XBLFQ66rhDd6U65MI9YzVpoPVam2WYngGY0a+Am+eo9pww36zk0/osDzLrD57hg3A0v+Otft7l/EiV6elTqLJxHg8BpO0bxWCdBFyJ/i0v+upfrbSgiMAP1QXjZkcSdDeLg0bS5QFX8Nei1/ZjvIHWQHwSUxDfIi67ly+BNmgQHuR2FPFemEkx7EqicV7TWuxfEevMc1edFFkdtzKv1tG8yQEtxPhX6tamz4TBmsu2a5fZ8er47bTI4x7tgCJdNEjhsDVlA1jsqU82i7JLjfXFhbu1hwfBVmKWaaWSeGYMxC8hAGKFXg77Ebo/Nd87urBApmwTOTj5sOFKrOz2BSqiZHYJ/2n8NeH7+CCXgdZQnHrt+u5DyNSpJSorQkNDgQzPunh9TmAVOh2of8gfqryjht6UayWyw1g3S1B1KXgP5RlvfnwL0yhOryLDk3qcpaivsAqFQAKVM6X9KHIbn83rljJxQQ7SmPTJ6TsSKtdZxFlWZklHz4e/smLnclOzQ==\n", "ops_manager_subnet": "manatee-management-subnet", "ops_manager_version": "2.0-build.314", + "pks_api": { + "uaa_admin_password": "password ", + "uaa_admin_user": "admin", + "url": "https://api.pks.manatee.cf-app.com" + }, "project": "cf-toolsmiths-pool-us-2", "region": "us-central1", "service_network_name": "manatee-pcf-network", diff --git a/integration/fixtures/pks_script.sh b/integration/fixtures/pks_script.sh new file mode 100644 index 0000000..b11c92f --- /dev/null +++ b/integration/fixtures/pks_script.sh @@ -0,0 +1,5 @@ +prods="$(om -t https://pcf.manatee.cf-app.com -k -u pivotalcf -p fakePassword curl -s -p /api/v0/staged/products)" +guid="$(echo "$prods" | jq -r '.[] | select(.type == "pivotal-container-service") | .guid')" +creds="$(om -t https://pcf.manatee.cf-app.com -k -u pivotalcf -p fakePassword curl -s -p /api/v0/deployed/products/"$guid"/credentials/.properties.uaa_admin_password)" +pass="$(echo "$creds" | jq -r .credential.value.secret)" +pks login -a https://api.pks.manatee.cf-app.com -u admin -p "$pass" --skip-ssl-validation diff --git a/integration/pks_login_command_test.go b/integration/pks_login_command_test.go new file mode 100644 index 0000000..cc197ee --- /dev/null +++ b/integration/pks_login_command_test.go @@ -0,0 +1,42 @@ +/* +Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. + +This program and the accompanying materials are made available under the terms of the under the Apache License, Version 2.0 (the "License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +package integration + +import ( + "io/ioutil" + "os/exec" + "strings" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gbytes" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("pks", func() { + It("generates the correct script", func() { + command := exec.Command(pathToPcf, "pks-login", "-t", "fixtures/claim_manatee_response.json", "-f") + session, err := Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + + Eventually(session).Should(Exit(0)) + Eventually(string(session.Err.Contents())).Should(Equal("")) + Eventually(session.Out).Should(Say("Logging in to PKS at: https://pcf.manatee.cf-app.com")) + + output := strings.TrimSuffix(string(session.Out.Contents()), "\n") + lines := strings.Split(output, "\n") + pathToFile := lines[len(lines)-1] + contents, err := ioutil.ReadFile(pathToFile) + Expect(err).NotTo(HaveOccurred()) + + Expect(string(contents)).To(Equal(LoadFixture("pks_script.sh"))) + }) +}) diff --git a/main.go b/main.go index 9c3e7d8..e100723 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "github.com/pivotal/hammer/environment" "github.com/pivotal/hammer/om" "github.com/pivotal/hammer/open" + "github.com/pivotal/hammer/pks" "github.com/pivotal/hammer/scripting" "github.com/pivotal/hammer/ssh" "github.com/pivotal/hammer/sshuttle" @@ -58,6 +59,7 @@ func (e *targetConfigPath) UnmarshalFlag(path string) error { type options struct { Bosh commands.BoshCommand `command:"bosh" description:"display BOSH credentials, or run a BOSH command"` CFLogin commands.CFLoginCommand `command:"cf-login" description:"log in to the cf for the environment"` + PKSLogin commands.PKSLoginCommand `command:"pks-login" description:"log in to pks for the environment"` Open commands.OpenCommand `command:"open" description:"open a browser to this environment"` OM commands.OMCommand `command:"om" description:"run the 'om' command with credentials for this environment"` SSH commands.SSHCommand `command:"ssh" choice:"opsman" choice:"director" description:"open an ssh connection to the ops manager or director of this environment"` @@ -90,6 +92,13 @@ func main() { ScriptRunner: scriptRunner, }, }, + PKSLogin: commands.PKSLoginCommand{ + Env: &envReader, + UI: &ui, + PKSLoginRunner: &pks.LoginRunner{ + ScriptRunner: scriptRunner, + }, + }, OM: commands.OMCommand{ Env: &envReader, OMRunner: &om.Runner{ diff --git a/pks/login_runner.go b/pks/login_runner.go new file mode 100644 index 0000000..1ba9bb4 --- /dev/null +++ b/pks/login_runner.go @@ -0,0 +1,35 @@ +/* +Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. + +This program and the accompanying materials are made available under the terms of the under the Apache License, Version 2.0 (the "License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +package pks + +import ( + "fmt" + + "github.com/pivotal/hammer/scripting" + + "github.com/pivotal/hammer/environment" +) + +type LoginRunner struct { + ScriptRunner scripting.ScriptRunner +} + +func (r LoginRunner) Run(data environment.Config, dryRun bool, args ...string) error { + lines := []string{ + fmt.Sprintf(`prods="$(om -t %s -k -u %s -p %s curl -s -p /api/v0/staged/products)"`, data.OpsManager.URL.String(), data.OpsManager.Username, data.OpsManager.Password), + fmt.Sprintf(`guid="$(echo "$prods" | jq -r '.[] | select(.type == "pivotal-container-service") | .guid')"`), + fmt.Sprintf(`creds="$(om -t %s -k -u %s -p %s curl -s -p /api/v0/deployed/products/"$guid"/credentials/.properties.uaa_admin_password)"`, data.OpsManager.URL.String(), data.OpsManager.Username, data.OpsManager.Password), + fmt.Sprintf(`pass="$(echo "$creds" | jq -r .credential.value.secret)"`), + fmt.Sprintf(`pks login -a %s -u admin -p "$pass" --skip-ssl-validation`, data.PKSApi.URL.String()), + } + + return r.ScriptRunner.RunScript(lines, []string{"jq", "om", "pks"}, dryRun) +} diff --git a/pks/login_runner_test.go b/pks/login_runner_test.go new file mode 100644 index 0000000..20c1c4a --- /dev/null +++ b/pks/login_runner_test.go @@ -0,0 +1,129 @@ +/* +Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. + +This program and the accompanying materials are made available under the terms of the under the Apache License, Version 2.0 (the "License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +package pks_test + +import ( + "fmt" + "net/url" + + "github.com/pivotal/hammer/pks" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/pivotal/hammer/environment" + "github.com/pivotal/hammer/scripting/scriptingfakes" +) + +var _ = Describe("pks login runner", func() { + var ( + err error + pksLoginRunner pks.LoginRunner + scriptRunner *scriptingfakes.FakeScriptRunner + + data environment.Config + dryRun bool + ) + + BeforeEach(func() { + scriptRunner = new(scriptingfakes.FakeScriptRunner) + + opsmanUrl, _ := url.Parse("www.test-url.io") + pksApiUrl, _ := url.Parse("api.test-url.io") + data = environment.Config{ + OpsManager: environment.OpsManager{ + Username: "username", + Password: "password", + URL: *opsmanUrl, + }, + PKSApi: environment.PKSApi{ + URL: *pksApiUrl, + Username: "username", + Password: "password", + }, + } + + pksLoginRunner = pks.LoginRunner{ + ScriptRunner: scriptRunner, + } + }) + + JustBeforeEach(func() { + err = pksLoginRunner.Run(data, dryRun) + }) + + It("runs the script with a pks login", func() { + Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) + + lines, _, _ := scriptRunner.RunScriptArgsForCall(0) + + Expect(lines).To(Equal([]string{ + `prods="$(om -t www.test-url.io -k -u username -p password curl -s -p /api/v0/staged/products)"`, + `guid="$(echo "$prods" | jq -r '.[] | select(.type == "pivotal-container-service") | .guid')"`, + `creds="$(om -t www.test-url.io -k -u username -p password curl -s -p /api/v0/deployed/products/"$guid"/credentials/.properties.uaa_admin_password)"`, + `pass="$(echo "$creds" | jq -r .credential.value.secret)"`, + `pks login -a api.test-url.io -u admin -p "$pass" --skip-ssl-validation`, + })) + }) + + It("specifies the appropriate prerequisites when running the script", func() { + Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) + + _, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) + + Expect(prereqs).To(ConsistOf("jq", "om", "pks")) + }) + + When("run with dry run set to false", func() { + BeforeEach(func() { + dryRun = false + }) + + It("runs the script in dry run mode", func() { + Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) + + _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) + Expect(dryRun).To(Equal(false)) + }) + }) + + When("run with dry run set to true", func() { + BeforeEach(func() { + dryRun = true + }) + + It("runs the script in dry run mode", func() { + Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) + + _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) + Expect(dryRun).To(Equal(true)) + }) + }) + + When("running the script succeeds", func() { + BeforeEach(func() { + scriptRunner.RunScriptReturns(nil) + }) + + It("doesn't error", func() { + Expect(err).NotTo(HaveOccurred()) + }) + }) + + When("running the script errors", func() { + BeforeEach(func() { + scriptRunner.RunScriptReturns(fmt.Errorf("run-script-error")) + }) + + It("propagates the error", func() { + Expect(err).To(MatchError("run-script-error")) + }) + }) +}) diff --git a/pks/pks_suite_test.go b/pks/pks_suite_test.go new file mode 100644 index 0000000..bfd6c2a --- /dev/null +++ b/pks/pks_suite_test.go @@ -0,0 +1,23 @@ +/* +Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. + +This program and the accompanying materials are made available under the terms of the under the Apache License, Version 2.0 (the "License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +package pks_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestCommand(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "PKS Suite") +}