├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── bosh ├── bosh_suite_test.go ├── runner.go └── runner_test.go ├── cf ├── cf_suite_test.go ├── login_runner.go └── login_runner_test.go ├── commands ├── bosh.go ├── bosh_test.go ├── cf_login.go ├── cf_login_test.go ├── commands_suite_test.go ├── commandsfakes │ ├── fake_env_reader.go │ ├── fake_tool_runner.go │ └── fake_ui.go ├── completion_bash.go ├── interfaces.go ├── om.go ├── om_test.go ├── open.go ├── open_test.go ├── pks_login.go ├── pks_login_test.go ├── ssh.go ├── ssh_test.go ├── sshuttle.go └── sshuttle_test.go ├── environment ├── config.go ├── config_test.go ├── environment_suite_test.go ├── fixtures │ ├── empty.json │ ├── lemon.json │ ├── lemon.yaml │ ├── multiple.json │ └── reduced.json └── reader.go ├── go.mod ├── go.sum ├── hammer.rb ├── integration ├── base_command_test.go ├── bosh_command_test.go ├── cf_login_command_test.go ├── completion_script_command_test.go ├── fixtures │ ├── bosh_cmd_script.sh │ ├── bosh_creds_script.sh │ ├── cf_script.sh │ ├── claim_manatee_response.json │ ├── multiple_environment_config.json │ ├── om_script.sh │ ├── om_script_json.sh │ ├── open_script.sh │ ├── pks_script.sh │ ├── ssh_director_script.sh │ ├── ssh_opsman_script.sh │ └── sshuttle_script.sh ├── integration_suite_test.go ├── om_command_test.go ├── open_command_test.go ├── pks_login_command_test.go ├── readers_test.go ├── ssh_command_test.go ├── sshuttle_command_test.go ├── utils.go └── version_command_test.go ├── main.go ├── om ├── om_suite_test.go ├── runner.go └── runner_test.go ├── open ├── open_suite_test.go ├── runner.go └── runner_test.go ├── pks ├── login_runner.go ├── login_runner_test.go └── pks_suite_test.go ├── scripting ├── prereqs.go ├── prereqs_test.go ├── print_file.go ├── print_file_test.go ├── run_script.go ├── scripting_suite_test.go └── scriptingfakes │ └── fake_script_runner.go ├── ssh ├── director_runner.go ├── director_runner_test.go ├── opsmanager_runner.go ├── opsmanager_runner_test.go └── ssh_suite_test.go ├── sshuttle ├── runner.go ├── runner_test.go └── sshuttle_suite_test.go ├── tools └── tools.go └── ui ├── ui.go ├── ui_suite_test.go └── ui_test.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "05:00" 8 | timezone: UTC 9 | open-pull-requests-limit: 10 10 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | jobs: 8 | test: 9 | name: Test 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Set up Go 13 | uses: actions/setup-go@v5 14 | with: 15 | go-version: stable 16 | - name: Check out code 17 | uses: actions/checkout@v4 18 | - name: Unit tests 19 | run: | 20 | go install github.com/onsi/ginkgo/v2/ginkgo 21 | make unit-test 22 | lint: 23 | name: Lint 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Check out code 27 | uses: actions/checkout@v4 28 | - name: golangci-lint 29 | uses: golangci/golangci-lint-action@v6 30 | with: 31 | version: v1.64.8 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | linters: 3 | disable-all: true 4 | enable: 5 | - errcheck 6 | - gochecknoinits 7 | - goconst 8 | - gocritic 9 | - goimports 10 | - gosimple 11 | - govet 12 | - ineffassign 13 | - misspell 14 | - staticcheck 15 | - typecheck 16 | - unconvert 17 | - unused 18 | linters-settings: 19 | goconst: 20 | min-len: 2 21 | min-occurrences: 3 22 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Documentation at http://goreleaser.com 2 | --- 3 | version: 2 4 | project_name: hammer 5 | before: 6 | hooks: 7 | - go mod download 8 | builds: 9 | - main: ./main.go 10 | goos: 11 | - darwin 12 | - linux 13 | - windows 14 | goarch: 15 | - amd64 16 | - arm64 17 | env: 18 | - GO111MODULE=on 19 | - CGO_ENABLED=0 20 | archives: 21 | - id: archive 22 | formats: 23 | - tar.gz 24 | name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" 25 | format_overrides: 26 | - goos: windows 27 | formats: 28 | - zip 29 | brews: 30 | - repository: 31 | owner: pivotal 32 | name: hammer 33 | ids: 34 | - archive 35 | test: | 36 | system "#{bin}/hammer version" 37 | changelog: 38 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Pivotal Projects 2 | 3 | We’d love to accept your patches and contributions to this project. Please review the following guidelines you'll need to follow in order to make a contribution. 4 | 5 | ## Contributor License Agreement 6 | 7 | All contributors to this project must have a signed Contributor License Agreement (**"CLA"**) on file with us. The CLA grants us the permissions we need to use and redistribute your contributions as part of the project; you or your employer retain the copyright to your contribution. Head over to [Pivotal CLA](https://cla.pivotal.io/) to see your current agreement(s) on file or to sign a new one. 8 | 9 | We generally only need you (or your employer) to sign our CLA once and once signed, you should be able to submit contributions to any Pivotal project. 10 | 11 | Note: if you would like to submit an "_obvious fix_" for something like a typo, formatting issue or spelling mistake, you may not need to sign the CLA. Please see our information on [obvious fixes](https://cla.pivotal.io/about#obvious-fix) for more details. 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | generate: 2 | go generate ./... 3 | 4 | test: unit-test lint 5 | 6 | lint: 7 | golangci-lint run -v 8 | 9 | unit-test: 10 | ginkgo -r . 11 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | hammer 2 | 3 | Copyright (c) 2019-Present Pivotal Software, Inc. All Rights Reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hammer - wrapper CLI for interacting with PCF environments 2 | [![Test Status](https://github.com/pivotal/hammer/workflows/Test/badge.svg)](https://github.com/pivotal/hammer/actions) 3 | 4 | ## Installation 5 | 6 | The latest build of the `hammer` cli is available from the releases page. 7 | Download the tar for your platform, untar it, and move it to your $PATH. 8 | 9 | Or using `brew` on macOS or Linux: 10 | ```bash 11 | brew tap pivotal/hammer https://github.com/pivotal/hammer 12 | brew install hammer 13 | ``` 14 | 15 | Alternatively you can build `hammer` from source if you have Go installed: 16 | ```bash 17 | git clone git@github.com:pivotal/hammer.git && cd hammer && go install 18 | ``` 19 | 20 | You will also need to install, separately, any of the underlying cli tools that `hammer` will use in your workflow. `hammer` does not include `cf`, `bosh`, `om`, etc. 21 | 22 | ## Config 23 | 24 | In order to run the `hammer` tool against a given environment you need to have an environment config file in the following format: 25 | ```json 26 | { 27 | "name": "ENVIRONMENT-NAME", 28 | "ops_manager": { 29 | "url": "OPSMAN-URL", 30 | "client_id": "OPSMAN-CLIENT-ID", 31 | "client_secret": "OPSMAN-CLIENT-SECRET", 32 | "username": "OPSMAN-USERNAME", 33 | "password": "OPSMAN-PASSWORD" 34 | }, 35 | "ops_manager_private_key": "OPSMAN-RSA-PRIVATE-KEY", 36 | "ops_manager_public_ip": "OPSMAN-PUBLIC-IP", 37 | "ops_manager_ssh_user": "OPSMAN-SSH-USER", 38 | "sys_domain": "PAS-SYSTEM-DOMAIN", 39 | "pks_api": { 40 | "url": "PKS-API-URL" 41 | } 42 | } 43 | ``` 44 | Or the equivalent in yaml: 45 | ```yaml 46 | name: ENVIRONMENT-NAME 47 | ops_manager: 48 | client_id: OPSMAN-CLIENT-ID 49 | client_secret: OPSMAN-CLIENT-SECRET 50 | password: OPSMAN-PASSWORD 51 | url: OPSMAN-URL 52 | username: OPSMAN-USERNAME 53 | ops_manager_private_key: OPSMAN-RSA-PRIVATE-KEY 54 | ops_manager_public_ip: OPSMAN-PUBLIC-IP 55 | ops_manager_ssh_user: OPSMAN-SSH-USER 56 | pks_api: 57 | url: PKS-API-URL 58 | sys_domain: PAS-SYSTEM-DOMAIN 59 | ``` 60 | This file can then be passed into the tool via `hammer -t path-to-env-config `. 61 | 62 | `ops_manager_ssh_user` is an optional field and if not set then `hammer -t path-to-env-config ssh opsman` will use 63 | `ubuntu` to ssh to the OpsManager VM, if users need to ssh via a different username they should set this as appropriate. 64 | 65 | Only one set of `ops_manager.client_id` and `ops_manager.client_secret` or `ops_manager.username` and `ops_manager.password` 66 | need to be specified, if both sets are specified then in line with `om` the client details will be used. 67 | 68 | NB: `sys_domain` and `pks_api.url` are only needed for using `hammer cf-login` and `hammer pks-login` respectively. 69 | 70 | ### Multiple environments in a single config 71 | 72 | The config file can contain a list of environments in previously defined structure, such as: 73 | ```json 74 | [{ 75 | "name": "ENVIRONMENT-NAME-1", 76 | "ops_manager": { 77 | "url": "OPSMAN-URL", 78 | "client_id": "OPSMAN-CLIENT-ID", 79 | "client_secret": "OPSMAN-CLIENT-SECRET", 80 | "username": "OPSMAN-USERNAME", 81 | "password": "OPSMAN-PASSWORD" 82 | }, 83 | "ops_manager_private_key": "OPSMAN-RSA-PRIVATE-KEY", 84 | "ops_manager_public_ip": "OPSMAN-PUBLIC-IP", 85 | "ops_manager_ssh_user": "OPSMAN-SSH-USER", 86 | "sys_domain": "PAS-SYSTEM-DOMAIN", 87 | "pks_api": { 88 | "url": "PKS-API-URL" 89 | } 90 | }, 91 | { 92 | "name": "ENVIRONMENT-NAME-2", 93 | "ops_manager": { 94 | "url": "OPSMAN-URL", 95 | "client_id": "OPSMAN-CLIENT-ID", 96 | "client_secret": "OPSMAN-CLIENT-SECRET", 97 | "username": "OPSMAN-USERNAME", 98 | "password": "OPSMAN-PASSWORD" 99 | }, 100 | "ops_manager_private_key": "OPSMAN-RSA-PRIVATE-KEY", 101 | "ops_manager_public_ip": "OPSMAN-PUBLIC-IP", 102 | "ops_manager_ssh_user": "OPSMAN-SSH-USER", 103 | "sys_domain": "PAS-SYSTEM-DOMAIN", 104 | "pks_api": { 105 | "url": "PKS-API-URL" 106 | } 107 | }] 108 | ``` 109 | Or the equivalent in yaml: 110 | ```yaml 111 | - name: ENVIRONMENT-NAME-1 112 | ops_manager: 113 | client_id: OPSMAN-CLIENT-ID 114 | client_secret: OPSMAN-CLIENT-SECRET 115 | password: OPSMAN-PASSWORD 116 | url: OPSMAN-URL 117 | username: OPSMAN-USERNAME 118 | ops_manager_private_key: OPSMAN-RSA-PRIVATE-KEY 119 | ops_manager_public_ip: OPSMAN-PUBLIC-IP 120 | ops_manager_ssh_user: OPSMAN-SSH-USER 121 | pks_api: 122 | url: PKS-API-URL 123 | sys_domain: PAS-SYSTEM-DOMAIN 124 | - name: ENVIRONMENT-NAME-2 125 | ops_manager: 126 | client_id: OPSMAN-CLIENT-ID 127 | client_secret: OPSMAN-CLIENT-SECRET 128 | password: OPSMAN-PASSWORD 129 | url: OPSMAN-URL 130 | username: OPSMAN-USERNAME 131 | ops_manager_private_key: OPSMAN-RSA-PRIVATE-KEY 132 | ops_manager_public_ip: OPSMAN-PUBLIC-IP 133 | ops_manager_ssh_user: OPSMAN-SSH-USER 134 | pks_api: 135 | url: PKS-API-URL 136 | sys_domain: PAS-SYSTEM-DOMAIN 137 | ``` 138 | An environment can then be specified via `hammer -t path-to-env-config -e environment-name `. If an environment 139 | name is not specified then the first environment in the config will be used. 140 | 141 | ## Development 142 | 143 | Unit and integration tests can be run if you have [Ginkgo](https://github.com/onsi/ginkgo) installed: 144 | ```bash 145 | ginkgo -r . 146 | ``` 147 | 148 | Linters can also be run using [golangci-lint](https://github.com/golangci/golangci-lint): 149 | ```bash 150 | golangci-lint run 151 | ``` 152 | 153 | Or just run both with: 154 | ```bash 155 | make test 156 | ``` 157 | 158 | --- 159 | 160 | Special thanks to [@blgm](https://github.com/blgm) for letting an internal tool he created serve as the basis for this tool. 161 | -------------------------------------------------------------------------------- /bosh/bosh_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package bosh_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "Bosh Suite") 23 | } 24 | -------------------------------------------------------------------------------- /bosh/runner.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package bosh 12 | 13 | import ( 14 | "fmt" 15 | "strings" 16 | 17 | "github.com/pivotal/hammer/scripting" 18 | 19 | "github.com/pivotal/hammer/environment" 20 | ) 21 | 22 | type Runner struct { 23 | ScriptRunner scripting.ScriptRunner 24 | } 25 | 26 | func (r Runner) Run(data environment.Config, dryRun bool, boshArgs ...string) error { 27 | lines := []string{ 28 | `ssh_key_path=$(mktemp)`, 29 | fmt.Sprintf(`echo "%s" >"$ssh_key_path"`, data.OpsManager.PrivateKey), 30 | `chmod 0600 "${ssh_key_path}"`, 31 | 32 | `bosh_ca_path=$(mktemp)`, 33 | fmt.Sprintf(`ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i "${ssh_key_path}" %s@"%s" cat /var/tempest/workspaces/default/root_ca_certificate 1>"${bosh_ca_path}" 2>/dev/null`, data.OpsManager.SshUser, data.OpsManager.IP.String()), 34 | `chmod 0600 "${bosh_ca_path}"`, 35 | 36 | fmt.Sprintf(`creds="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p /api/v0/deployed/director/credentials/bosh_commandline_credentials)"`, 37 | data.OpsManager.ClientID, 38 | data.OpsManager.ClientSecret, 39 | data.OpsManager.Username, 40 | data.OpsManager.Password, 41 | data.OpsManager.URL.String()), 42 | `bosh_all="$(echo "$creds" | jq -r .credential | tr ' ' '\n' | grep '=')"`, 43 | 44 | `bosh_client="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_CLIENT=')"`, 45 | `bosh_env="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_ENVIRONMENT=')"`, 46 | `bosh_secret="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_CLIENT_SECRET=')"`, 47 | `bosh_ca_cert="BOSH_CA_CERT=$bosh_ca_path"`, 48 | fmt.Sprintf(`bosh_proxy="BOSH_ALL_PROXY=ssh+socks5://%s@%s:22?private-key=${ssh_key_path}"`, data.OpsManager.SshUser, data.OpsManager.IP.String()), 49 | fmt.Sprintf(`bosh_gw_host="BOSH_GW_HOST=%s"`, data.OpsManager.IP.String()), 50 | fmt.Sprintf(`bosh_gw_user="BOSH_GW_USER=%s"`, data.OpsManager.SshUser), 51 | `bosh_gw_private_key="BOSH_GW_PRIVATE_KEY=${ssh_key_path}"`, 52 | } 53 | 54 | prereqs := []string{"jq", "om", "ssh"} 55 | 56 | if len(boshArgs) > 0 { 57 | lines = append( 58 | lines, 59 | `trap 'rm -f ${ssh_key_path} ${bosh_ca_path}' EXIT`, 60 | fmt.Sprintf(`/usr/bin/env "$bosh_client" "$bosh_env" "$bosh_secret" "$bosh_ca_cert" "$bosh_proxy" "$bosh_gw_host" "$bosh_gw_user" "$bosh_gw_private_key" bosh %s`, strings.Join(boshArgs, " ")), 61 | ) 62 | prereqs = append(prereqs, "bosh") 63 | } else { 64 | lines = append( 65 | lines, 66 | fmt.Sprintf(`echo "export BOSH_ENV_NAME=%s"`, data.Name), 67 | `echo "export $bosh_client"`, 68 | `echo "export $bosh_env"`, 69 | `echo "export $bosh_secret"`, 70 | `echo "export $bosh_ca_cert"`, 71 | `echo "export $bosh_proxy"`, 72 | `echo "export $bosh_gw_host"`, 73 | `echo "export $bosh_gw_user"`, 74 | `echo "export $bosh_gw_private_key"`, 75 | `echo "export CREDHUB_SERVER=\"\${BOSH_ENVIRONMENT}:8844\""`, 76 | `echo "export CREDHUB_PROXY=\"\${BOSH_ALL_PROXY}\""`, 77 | `echo "export CREDHUB_CLIENT=\"\${BOSH_CLIENT}\""`, 78 | `echo "export CREDHUB_SECRET=\"\${BOSH_CLIENT_SECRET}\""`, 79 | `echo "export CREDHUB_CA_CERT=\"\${BOSH_CA_CERT}\""`, 80 | ) 81 | } 82 | 83 | return r.ScriptRunner.RunScript(lines, prereqs, dryRun) 84 | } 85 | -------------------------------------------------------------------------------- /cf/cf_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package cf_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "CF Suite") 23 | } 24 | -------------------------------------------------------------------------------- /cf/login_runner.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package cf 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/pivotal/hammer/environment" 17 | "github.com/pivotal/hammer/scripting" 18 | ) 19 | 20 | type LoginRunner struct { 21 | ScriptRunner scripting.ScriptRunner 22 | } 23 | 24 | func (r LoginRunner) Run(data environment.Config, dryRun bool, args ...string) error { 25 | lines := []string{ 26 | fmt.Sprintf(`prods="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p /api/v0/staged/products)"`, 27 | data.OpsManager.ClientID, 28 | data.OpsManager.ClientSecret, 29 | data.OpsManager.Username, 30 | data.OpsManager.Password, 31 | data.OpsManager.URL.String()), 32 | `guid="$(echo "$prods" | jq -r '.[] | select(.type == "cf") | .guid')"`, 33 | fmt.Sprintf(`creds="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p /api/v0/deployed/products/"$guid"/credentials/.uaa.admin_credentials)"`, 34 | data.OpsManager.ClientID, 35 | data.OpsManager.ClientSecret, 36 | data.OpsManager.Username, 37 | data.OpsManager.Password, 38 | data.OpsManager.URL.String()), 39 | `export CF_USERNAME="$(echo "$creds" | jq -r .credential.value.identity)"`, 40 | `export CF_PASSWORD="$(echo "$creds" | jq -r .credential.value.password)"`, 41 | fmt.Sprintf(`cf api "api.%s" --skip-ssl-validation`, data.CFDomain), 42 | `cf auth`, 43 | } 44 | 45 | return r.ScriptRunner.RunScript(lines, []string{"jq", "om", "cf"}, dryRun) 46 | } 47 | -------------------------------------------------------------------------------- /cf/login_runner_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package cf_test 12 | 13 | import ( 14 | "fmt" 15 | "net/url" 16 | 17 | "github.com/pivotal/hammer/cf" 18 | 19 | . "github.com/onsi/ginkgo/v2" 20 | . "github.com/onsi/gomega" 21 | "github.com/pivotal/hammer/environment" 22 | "github.com/pivotal/hammer/scripting/scriptingfakes" 23 | ) 24 | 25 | var _ = Describe("cf login runner", func() { 26 | var ( 27 | err error 28 | cfLoginRunner cf.LoginRunner 29 | scriptRunner *scriptingfakes.FakeScriptRunner 30 | 31 | data environment.Config 32 | dryRun bool 33 | ) 34 | 35 | BeforeEach(func() { 36 | scriptRunner = new(scriptingfakes.FakeScriptRunner) 37 | 38 | url, _ := url.Parse("https://www.test-url.io") 39 | data = environment.Config{ 40 | CFDomain: "sys.test-url.io", 41 | OpsManager: environment.OpsManager{ 42 | URL: *url, 43 | Username: "username", 44 | Password: "password", 45 | ClientID: "client_id", 46 | ClientSecret: "client_secret", 47 | }, 48 | } 49 | 50 | cfLoginRunner = cf.LoginRunner{ 51 | ScriptRunner: scriptRunner, 52 | } 53 | }) 54 | 55 | JustBeforeEach(func() { 56 | err = cfLoginRunner.Run(data, dryRun) 57 | }) 58 | 59 | It("runs the script with a cf login", func() { 60 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 61 | 62 | lines, _, _ := scriptRunner.RunScriptArgsForCall(0) 63 | 64 | Expect(lines).To(Equal([]string{ 65 | `prods="$(OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t https://www.test-url.io -k curl -s -p /api/v0/staged/products)"`, 66 | `guid="$(echo "$prods" | jq -r '.[] | select(.type == "cf") | .guid')"`, 67 | `creds="$(OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t https://www.test-url.io -k curl -s -p /api/v0/deployed/products/"$guid"/credentials/.uaa.admin_credentials)"`, 68 | `export CF_USERNAME="$(echo "$creds" | jq -r .credential.value.identity)"`, 69 | `export CF_PASSWORD="$(echo "$creds" | jq -r .credential.value.password)"`, 70 | `cf api "api.sys.test-url.io" --skip-ssl-validation`, 71 | `cf auth`, 72 | })) 73 | }) 74 | 75 | It("specifies the appropriate prerequisites when running the script", func() { 76 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 77 | 78 | _, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) 79 | 80 | Expect(prereqs).To(ConsistOf("jq", "om", "cf")) 81 | }) 82 | 83 | When("run with dry run set to false", func() { 84 | BeforeEach(func() { 85 | dryRun = false 86 | }) 87 | 88 | It("runs the script in dry run mode", func() { 89 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 90 | 91 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 92 | Expect(dryRun).To(Equal(false)) 93 | }) 94 | }) 95 | 96 | When("run with dry run set to true", func() { 97 | BeforeEach(func() { 98 | dryRun = true 99 | }) 100 | 101 | It("runs the script in dry run mode", func() { 102 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 103 | 104 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 105 | Expect(dryRun).To(Equal(true)) 106 | }) 107 | }) 108 | 109 | When("running the script succeeds", func() { 110 | BeforeEach(func() { 111 | scriptRunner.RunScriptReturns(nil) 112 | }) 113 | 114 | It("doesn't error", func() { 115 | Expect(err).NotTo(HaveOccurred()) 116 | }) 117 | }) 118 | 119 | When("running the script errors", func() { 120 | BeforeEach(func() { 121 | scriptRunner.RunScriptReturns(fmt.Errorf("run-script-error")) 122 | }) 123 | 124 | It("propagates the error", func() { 125 | Expect(err).To(MatchError("run-script-error")) 126 | }) 127 | }) 128 | }) 129 | -------------------------------------------------------------------------------- /commands/bosh.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | type BoshCommand struct { 14 | TargetConfig string `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 15 | EnvironmentName string `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 16 | File bool `short:"f" long:"file" description:"write a script file but do not run it"` 17 | 18 | Env EnvReader 19 | UI UI 20 | BoshRunner ToolRunner 21 | } 22 | 23 | func (c *BoshCommand) Execute(args []string) error { 24 | data, err := c.Env.Read(c.TargetConfig, c.EnvironmentName) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | return c.BoshRunner.Run(data, c.File, args...) 30 | } 31 | -------------------------------------------------------------------------------- /commands/bosh_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands_test 12 | 13 | import ( 14 | "fmt" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | fakes "github.com/pivotal/hammer/commands/commandsfakes" 19 | "github.com/pivotal/hammer/environment" 20 | 21 | . "github.com/pivotal/hammer/commands" 22 | ) 23 | 24 | var _ = Describe("Bosh command", func() { 25 | var ( 26 | err error 27 | command *BoshCommand 28 | 29 | envReader *fakes.FakeEnvReader 30 | ui *fakes.FakeUI 31 | boshRunner *fakes.FakeToolRunner 32 | commandArgs []string 33 | ) 34 | 35 | BeforeEach(func() { 36 | envReader = new(fakes.FakeEnvReader) 37 | ui = new(fakes.FakeUI) 38 | boshRunner = new(fakes.FakeToolRunner) 39 | commandArgs = []string{"arg1", "arg2"} 40 | 41 | command = &BoshCommand{ 42 | Env: envReader, 43 | UI: ui, 44 | BoshRunner: boshRunner, 45 | } 46 | }) 47 | 48 | JustBeforeEach(func() { 49 | err = command.Execute(commandArgs) 50 | }) 51 | 52 | When("retrieving the environment config errors", func() { 53 | BeforeEach(func() { 54 | envReader.ReadReturns(environment.Config{}, fmt.Errorf("env-reader-error")) 55 | }) 56 | 57 | It("doesn't attempt to run the bosh tool", func() { 58 | Expect(boshRunner.RunCallCount()).To(Equal(0)) 59 | }) 60 | 61 | It("propagates the error", func() { 62 | Expect(err).To(MatchError("env-reader-error")) 63 | }) 64 | }) 65 | 66 | When("retrieving the environment config is successful", func() { 67 | BeforeEach(func() { 68 | envReader.ReadReturns(environment.Config{Name: "env-name"}, nil) 69 | }) 70 | 71 | It("runs the bosh tool using the retrieved environment config", func() { 72 | Expect(boshRunner.RunCallCount()).To(Equal(1)) 73 | 74 | environmentConfig, _, _ := boshRunner.RunArgsForCall(0) 75 | Expect(environmentConfig).To(BeEquivalentTo(environment.Config{Name: "env-name"})) 76 | }) 77 | 78 | When("run with the file flag set", func() { 79 | BeforeEach(func() { 80 | command.File = true 81 | }) 82 | 83 | It("runs the bosh tool in dry run mode", func() { 84 | Expect(boshRunner.RunCallCount()).To(Equal(1)) 85 | 86 | _, dryRun, _ := boshRunner.RunArgsForCall(0) 87 | Expect(dryRun).To(BeTrue()) 88 | }) 89 | }) 90 | 91 | When("run with the file flag unset", func() { 92 | BeforeEach(func() { 93 | command.File = false 94 | }) 95 | 96 | It("runs the bosh tool in non-dry run mode", func() { 97 | Expect(boshRunner.RunCallCount()).To(Equal(1)) 98 | 99 | _, dryRun, _ := boshRunner.RunArgsForCall(0) 100 | Expect(dryRun).To(BeFalse()) 101 | }) 102 | }) 103 | 104 | It("runs the bosh tool using the supplied command args", func() { 105 | Expect(boshRunner.RunCallCount()).To(Equal(1)) 106 | 107 | _, _, args := boshRunner.RunArgsForCall(0) 108 | Expect(args).To(BeEquivalentTo([]string{"arg1", "arg2"})) 109 | }) 110 | 111 | When("running the bosh tool is successful", func() { 112 | BeforeEach(func() { 113 | boshRunner.RunReturns(nil) 114 | }) 115 | 116 | It("doesn't error", func() { 117 | Expect(err).NotTo(HaveOccurred()) 118 | }) 119 | }) 120 | 121 | When("running the bosh tool errors", func() { 122 | BeforeEach(func() { 123 | boshRunner.RunReturns(fmt.Errorf("bosh-runnner-error")) 124 | }) 125 | 126 | It("propagates the error", func() { 127 | Expect(err).To(MatchError("bosh-runnner-error")) 128 | }) 129 | }) 130 | }) 131 | }) 132 | -------------------------------------------------------------------------------- /commands/cf_login.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | import ( 14 | "fmt" 15 | ) 16 | 17 | type CFLoginCommand struct { 18 | TargetConfig string `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 19 | EnvironmentName string `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 20 | File bool `short:"f" long:"file" description:"write a script file but do not run it"` 21 | 22 | Env EnvReader 23 | UI UI 24 | CFLoginRunner ToolRunner 25 | } 26 | 27 | func (c *CFLoginCommand) Execute(args []string) error { 28 | data, err := c.Env.Read(c.TargetConfig, c.EnvironmentName) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | c.UI.DisplayText(fmt.Sprintf("Logging in to CF at: %s\n", data.OpsManager.URL.String())) 34 | 35 | return c.CFLoginRunner.Run(data, c.File) 36 | } 37 | -------------------------------------------------------------------------------- /commands/cf_login_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands_test 12 | 13 | import ( 14 | "fmt" 15 | "net/url" 16 | 17 | . "github.com/onsi/ginkgo/v2" 18 | . "github.com/onsi/gomega" 19 | fakes "github.com/pivotal/hammer/commands/commandsfakes" 20 | "github.com/pivotal/hammer/environment" 21 | 22 | . "github.com/pivotal/hammer/commands" 23 | ) 24 | 25 | var _ = Describe("cf login command", func() { 26 | var ( 27 | err error 28 | command *CFLoginCommand 29 | 30 | envReader *fakes.FakeEnvReader 31 | ui *fakes.FakeUI 32 | cfLoginRunner *fakes.FakeToolRunner 33 | commandArgs []string 34 | ) 35 | 36 | BeforeEach(func() { 37 | envReader = new(fakes.FakeEnvReader) 38 | ui = new(fakes.FakeUI) 39 | cfLoginRunner = new(fakes.FakeToolRunner) 40 | commandArgs = []string{"arg1", "arg2"} 41 | 42 | command = &CFLoginCommand{ 43 | Env: envReader, 44 | UI: ui, 45 | CFLoginRunner: cfLoginRunner, 46 | File: true, 47 | } 48 | }) 49 | 50 | JustBeforeEach(func() { 51 | err = command.Execute(commandArgs) 52 | }) 53 | 54 | When("retrieving the environment config errors", func() { 55 | BeforeEach(func() { 56 | envReader.ReadReturns(environment.Config{}, fmt.Errorf("env-reader-error")) 57 | }) 58 | 59 | It("doesn't attempt to run the cf login tool", func() { 60 | Expect(cfLoginRunner.RunCallCount()).To(Equal(0)) 61 | }) 62 | 63 | It("propagates the error", func() { 64 | Expect(err).To(MatchError("env-reader-error")) 65 | }) 66 | }) 67 | 68 | When("retrieving the environment config is successful", func() { 69 | BeforeEach(func() { 70 | url, _ := url.Parse("www.test-cf.io") 71 | envReader.ReadReturns(environment.Config{OpsManager: environment.OpsManager{URL: *url}}, nil) 72 | }) 73 | 74 | It("displays that the cf is being logged into", func() { 75 | Expect(ui.DisplayTextCallCount()).To(Equal(1)) 76 | Expect(ui.DisplayTextArgsForCall(0)).To(Equal("Logging in to CF at: www.test-cf.io\n")) 77 | }) 78 | 79 | It("runs the cf login tool using the retrieved environment config", func() { 80 | Expect(cfLoginRunner.RunCallCount()).To(Equal(1)) 81 | 82 | environmentConfig, _, _ := cfLoginRunner.RunArgsForCall(0) 83 | 84 | expectedURL, _ := url.Parse("www.test-cf.io") 85 | Expect(environmentConfig).To(BeEquivalentTo(environment.Config{OpsManager: environment.OpsManager{URL: *expectedURL}})) 86 | }) 87 | 88 | When("run with the file flag set", func() { 89 | BeforeEach(func() { 90 | command.File = true 91 | }) 92 | 93 | It("runs the cf login tool in dry run mode", func() { 94 | Expect(cfLoginRunner.RunCallCount()).To(Equal(1)) 95 | 96 | _, dryRun, _ := cfLoginRunner.RunArgsForCall(0) 97 | Expect(dryRun).To(BeTrue()) 98 | }) 99 | }) 100 | 101 | When("run with the file flag unset", func() { 102 | BeforeEach(func() { 103 | command.File = false 104 | }) 105 | 106 | It("runs the cf login tool in non-dry run mode", func() { 107 | Expect(cfLoginRunner.RunCallCount()).To(Equal(1)) 108 | 109 | _, dryRun, _ := cfLoginRunner.RunArgsForCall(0) 110 | Expect(dryRun).To(BeFalse()) 111 | }) 112 | }) 113 | 114 | It("runs the cf login tool with no additional args", func() { 115 | Expect(cfLoginRunner.RunCallCount()).To(Equal(1)) 116 | 117 | _, _, args := cfLoginRunner.RunArgsForCall(0) 118 | Expect(args).To(BeEmpty()) 119 | }) 120 | 121 | When("running the cf login tool is successful", func() { 122 | BeforeEach(func() { 123 | cfLoginRunner.RunReturns(nil) 124 | }) 125 | 126 | It("doesn't error", func() { 127 | Expect(err).NotTo(HaveOccurred()) 128 | }) 129 | }) 130 | 131 | When("running the cf login tool errors", func() { 132 | BeforeEach(func() { 133 | cfLoginRunner.RunReturns(fmt.Errorf("cf-login-runnner-error")) 134 | }) 135 | 136 | It("propagates the error", func() { 137 | Expect(err).To(MatchError("cf-login-runnner-error")) 138 | }) 139 | }) 140 | }) 141 | }) 142 | -------------------------------------------------------------------------------- /commands/commands_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "Commands Suite") 23 | } 24 | -------------------------------------------------------------------------------- /commands/commandsfakes/fake_env_reader.go: -------------------------------------------------------------------------------- 1 | // Code generated by counterfeiter. DO NOT EDIT. 2 | package commandsfakes 3 | 4 | import ( 5 | "sync" 6 | 7 | "github.com/pivotal/hammer/commands" 8 | "github.com/pivotal/hammer/environment" 9 | ) 10 | 11 | type FakeEnvReader struct { 12 | ReadStub func(string, string) (environment.Config, error) 13 | readMutex sync.RWMutex 14 | readArgsForCall []struct { 15 | arg1 string 16 | arg2 string 17 | } 18 | readReturns struct { 19 | result1 environment.Config 20 | result2 error 21 | } 22 | readReturnsOnCall map[int]struct { 23 | result1 environment.Config 24 | result2 error 25 | } 26 | invocations map[string][][]interface{} 27 | invocationsMutex sync.RWMutex 28 | } 29 | 30 | func (fake *FakeEnvReader) Read(arg1 string, arg2 string) (environment.Config, error) { 31 | fake.readMutex.Lock() 32 | ret, specificReturn := fake.readReturnsOnCall[len(fake.readArgsForCall)] 33 | fake.readArgsForCall = append(fake.readArgsForCall, struct { 34 | arg1 string 35 | arg2 string 36 | }{arg1, arg2}) 37 | stub := fake.ReadStub 38 | fakeReturns := fake.readReturns 39 | fake.recordInvocation("Read", []interface{}{arg1, arg2}) 40 | fake.readMutex.Unlock() 41 | if stub != nil { 42 | return stub(arg1, arg2) 43 | } 44 | if specificReturn { 45 | return ret.result1, ret.result2 46 | } 47 | return fakeReturns.result1, fakeReturns.result2 48 | } 49 | 50 | func (fake *FakeEnvReader) ReadCallCount() int { 51 | fake.readMutex.RLock() 52 | defer fake.readMutex.RUnlock() 53 | return len(fake.readArgsForCall) 54 | } 55 | 56 | func (fake *FakeEnvReader) ReadCalls(stub func(string, string) (environment.Config, error)) { 57 | fake.readMutex.Lock() 58 | defer fake.readMutex.Unlock() 59 | fake.ReadStub = stub 60 | } 61 | 62 | func (fake *FakeEnvReader) ReadArgsForCall(i int) (string, string) { 63 | fake.readMutex.RLock() 64 | defer fake.readMutex.RUnlock() 65 | argsForCall := fake.readArgsForCall[i] 66 | return argsForCall.arg1, argsForCall.arg2 67 | } 68 | 69 | func (fake *FakeEnvReader) ReadReturns(result1 environment.Config, result2 error) { 70 | fake.readMutex.Lock() 71 | defer fake.readMutex.Unlock() 72 | fake.ReadStub = nil 73 | fake.readReturns = struct { 74 | result1 environment.Config 75 | result2 error 76 | }{result1, result2} 77 | } 78 | 79 | func (fake *FakeEnvReader) ReadReturnsOnCall(i int, result1 environment.Config, result2 error) { 80 | fake.readMutex.Lock() 81 | defer fake.readMutex.Unlock() 82 | fake.ReadStub = nil 83 | if fake.readReturnsOnCall == nil { 84 | fake.readReturnsOnCall = make(map[int]struct { 85 | result1 environment.Config 86 | result2 error 87 | }) 88 | } 89 | fake.readReturnsOnCall[i] = struct { 90 | result1 environment.Config 91 | result2 error 92 | }{result1, result2} 93 | } 94 | 95 | func (fake *FakeEnvReader) Invocations() map[string][][]interface{} { 96 | fake.invocationsMutex.RLock() 97 | defer fake.invocationsMutex.RUnlock() 98 | fake.readMutex.RLock() 99 | defer fake.readMutex.RUnlock() 100 | copiedInvocations := map[string][][]interface{}{} 101 | for key, value := range fake.invocations { 102 | copiedInvocations[key] = value 103 | } 104 | return copiedInvocations 105 | } 106 | 107 | func (fake *FakeEnvReader) recordInvocation(key string, args []interface{}) { 108 | fake.invocationsMutex.Lock() 109 | defer fake.invocationsMutex.Unlock() 110 | if fake.invocations == nil { 111 | fake.invocations = map[string][][]interface{}{} 112 | } 113 | if fake.invocations[key] == nil { 114 | fake.invocations[key] = [][]interface{}{} 115 | } 116 | fake.invocations[key] = append(fake.invocations[key], args) 117 | } 118 | 119 | var _ commands.EnvReader = new(FakeEnvReader) 120 | -------------------------------------------------------------------------------- /commands/commandsfakes/fake_tool_runner.go: -------------------------------------------------------------------------------- 1 | // Code generated by counterfeiter. DO NOT EDIT. 2 | package commandsfakes 3 | 4 | import ( 5 | "sync" 6 | 7 | "github.com/pivotal/hammer/commands" 8 | "github.com/pivotal/hammer/environment" 9 | ) 10 | 11 | type FakeToolRunner struct { 12 | RunStub func(environment.Config, bool, ...string) error 13 | runMutex sync.RWMutex 14 | runArgsForCall []struct { 15 | arg1 environment.Config 16 | arg2 bool 17 | arg3 []string 18 | } 19 | runReturns struct { 20 | result1 error 21 | } 22 | runReturnsOnCall map[int]struct { 23 | result1 error 24 | } 25 | invocations map[string][][]interface{} 26 | invocationsMutex sync.RWMutex 27 | } 28 | 29 | func (fake *FakeToolRunner) Run(arg1 environment.Config, arg2 bool, arg3 ...string) error { 30 | fake.runMutex.Lock() 31 | ret, specificReturn := fake.runReturnsOnCall[len(fake.runArgsForCall)] 32 | fake.runArgsForCall = append(fake.runArgsForCall, struct { 33 | arg1 environment.Config 34 | arg2 bool 35 | arg3 []string 36 | }{arg1, arg2, arg3}) 37 | stub := fake.RunStub 38 | fakeReturns := fake.runReturns 39 | fake.recordInvocation("Run", []interface{}{arg1, arg2, arg3}) 40 | fake.runMutex.Unlock() 41 | if stub != nil { 42 | return stub(arg1, arg2, arg3...) 43 | } 44 | if specificReturn { 45 | return ret.result1 46 | } 47 | return fakeReturns.result1 48 | } 49 | 50 | func (fake *FakeToolRunner) RunCallCount() int { 51 | fake.runMutex.RLock() 52 | defer fake.runMutex.RUnlock() 53 | return len(fake.runArgsForCall) 54 | } 55 | 56 | func (fake *FakeToolRunner) RunCalls(stub func(environment.Config, bool, ...string) error) { 57 | fake.runMutex.Lock() 58 | defer fake.runMutex.Unlock() 59 | fake.RunStub = stub 60 | } 61 | 62 | func (fake *FakeToolRunner) RunArgsForCall(i int) (environment.Config, bool, []string) { 63 | fake.runMutex.RLock() 64 | defer fake.runMutex.RUnlock() 65 | argsForCall := fake.runArgsForCall[i] 66 | return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 67 | } 68 | 69 | func (fake *FakeToolRunner) RunReturns(result1 error) { 70 | fake.runMutex.Lock() 71 | defer fake.runMutex.Unlock() 72 | fake.RunStub = nil 73 | fake.runReturns = struct { 74 | result1 error 75 | }{result1} 76 | } 77 | 78 | func (fake *FakeToolRunner) RunReturnsOnCall(i int, result1 error) { 79 | fake.runMutex.Lock() 80 | defer fake.runMutex.Unlock() 81 | fake.RunStub = nil 82 | if fake.runReturnsOnCall == nil { 83 | fake.runReturnsOnCall = make(map[int]struct { 84 | result1 error 85 | }) 86 | } 87 | fake.runReturnsOnCall[i] = struct { 88 | result1 error 89 | }{result1} 90 | } 91 | 92 | func (fake *FakeToolRunner) Invocations() map[string][][]interface{} { 93 | fake.invocationsMutex.RLock() 94 | defer fake.invocationsMutex.RUnlock() 95 | fake.runMutex.RLock() 96 | defer fake.runMutex.RUnlock() 97 | copiedInvocations := map[string][][]interface{}{} 98 | for key, value := range fake.invocations { 99 | copiedInvocations[key] = value 100 | } 101 | return copiedInvocations 102 | } 103 | 104 | func (fake *FakeToolRunner) recordInvocation(key string, args []interface{}) { 105 | fake.invocationsMutex.Lock() 106 | defer fake.invocationsMutex.Unlock() 107 | if fake.invocations == nil { 108 | fake.invocations = map[string][][]interface{}{} 109 | } 110 | if fake.invocations[key] == nil { 111 | fake.invocations[key] = [][]interface{}{} 112 | } 113 | fake.invocations[key] = append(fake.invocations[key], args) 114 | } 115 | 116 | var _ commands.ToolRunner = new(FakeToolRunner) 117 | -------------------------------------------------------------------------------- /commands/commandsfakes/fake_ui.go: -------------------------------------------------------------------------------- 1 | // Code generated by counterfeiter. DO NOT EDIT. 2 | package commandsfakes 3 | 4 | import ( 5 | "sync" 6 | 7 | "github.com/pivotal/hammer/commands" 8 | ) 9 | 10 | type FakeUI struct { 11 | DisplayErrorStub func(error) 12 | displayErrorMutex sync.RWMutex 13 | displayErrorArgsForCall []struct { 14 | arg1 error 15 | } 16 | DisplayTextStub func(string) 17 | displayTextMutex sync.RWMutex 18 | displayTextArgsForCall []struct { 19 | arg1 string 20 | } 21 | invocations map[string][][]interface{} 22 | invocationsMutex sync.RWMutex 23 | } 24 | 25 | func (fake *FakeUI) DisplayError(arg1 error) { 26 | fake.displayErrorMutex.Lock() 27 | fake.displayErrorArgsForCall = append(fake.displayErrorArgsForCall, struct { 28 | arg1 error 29 | }{arg1}) 30 | stub := fake.DisplayErrorStub 31 | fake.recordInvocation("DisplayError", []interface{}{arg1}) 32 | fake.displayErrorMutex.Unlock() 33 | if stub != nil { 34 | fake.DisplayErrorStub(arg1) 35 | } 36 | } 37 | 38 | func (fake *FakeUI) DisplayErrorCallCount() int { 39 | fake.displayErrorMutex.RLock() 40 | defer fake.displayErrorMutex.RUnlock() 41 | return len(fake.displayErrorArgsForCall) 42 | } 43 | 44 | func (fake *FakeUI) DisplayErrorCalls(stub func(error)) { 45 | fake.displayErrorMutex.Lock() 46 | defer fake.displayErrorMutex.Unlock() 47 | fake.DisplayErrorStub = stub 48 | } 49 | 50 | func (fake *FakeUI) DisplayErrorArgsForCall(i int) error { 51 | fake.displayErrorMutex.RLock() 52 | defer fake.displayErrorMutex.RUnlock() 53 | argsForCall := fake.displayErrorArgsForCall[i] 54 | return argsForCall.arg1 55 | } 56 | 57 | func (fake *FakeUI) DisplayText(arg1 string) { 58 | fake.displayTextMutex.Lock() 59 | fake.displayTextArgsForCall = append(fake.displayTextArgsForCall, struct { 60 | arg1 string 61 | }{arg1}) 62 | stub := fake.DisplayTextStub 63 | fake.recordInvocation("DisplayText", []interface{}{arg1}) 64 | fake.displayTextMutex.Unlock() 65 | if stub != nil { 66 | fake.DisplayTextStub(arg1) 67 | } 68 | } 69 | 70 | func (fake *FakeUI) DisplayTextCallCount() int { 71 | fake.displayTextMutex.RLock() 72 | defer fake.displayTextMutex.RUnlock() 73 | return len(fake.displayTextArgsForCall) 74 | } 75 | 76 | func (fake *FakeUI) DisplayTextCalls(stub func(string)) { 77 | fake.displayTextMutex.Lock() 78 | defer fake.displayTextMutex.Unlock() 79 | fake.DisplayTextStub = stub 80 | } 81 | 82 | func (fake *FakeUI) DisplayTextArgsForCall(i int) string { 83 | fake.displayTextMutex.RLock() 84 | defer fake.displayTextMutex.RUnlock() 85 | argsForCall := fake.displayTextArgsForCall[i] 86 | return argsForCall.arg1 87 | } 88 | 89 | func (fake *FakeUI) Invocations() map[string][][]interface{} { 90 | fake.invocationsMutex.RLock() 91 | defer fake.invocationsMutex.RUnlock() 92 | fake.displayErrorMutex.RLock() 93 | defer fake.displayErrorMutex.RUnlock() 94 | fake.displayTextMutex.RLock() 95 | defer fake.displayTextMutex.RUnlock() 96 | copiedInvocations := map[string][][]interface{}{} 97 | for key, value := range fake.invocations { 98 | copiedInvocations[key] = value 99 | } 100 | return copiedInvocations 101 | } 102 | 103 | func (fake *FakeUI) recordInvocation(key string, args []interface{}) { 104 | fake.invocationsMutex.Lock() 105 | defer fake.invocationsMutex.Unlock() 106 | if fake.invocations == nil { 107 | fake.invocations = map[string][][]interface{}{} 108 | } 109 | if fake.invocations[key] == nil { 110 | fake.invocations[key] = [][]interface{}{} 111 | } 112 | fake.invocations[key] = append(fake.invocations[key], args) 113 | } 114 | 115 | var _ commands.UI = new(FakeUI) 116 | -------------------------------------------------------------------------------- /commands/completion_bash.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | import ( 14 | "fmt" 15 | ) 16 | 17 | type CompletionCommand struct { 18 | Bash BashCompletionCommand `command:"bash" description:"bash completion script"` 19 | } 20 | 21 | type BashCompletionCommand struct { 22 | } 23 | 24 | func (c *BashCompletionCommand) Execute(args []string) error { 25 | fmt.Printf(`# This script allows hammer to do autocompletion via Bash. 26 | # Add the following to your .bashrc file, making sure that the path matches your system: 27 | # eval "$(/path/to/hammer completion bash)" 28 | 29 | _complete_hammer() { 30 | args=("${COMP_WORDS[@]:1:$COMP_CWORD}") # Skip first arg 31 | local IFS=$'\n' # Split into lines 32 | COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}")) 33 | return 0 34 | } 35 | 36 | complete -F _complete_hammer hammer 37 | `) 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /commands/interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | import "github.com/pivotal/hammer/environment" 14 | 15 | //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . EnvReader 16 | 17 | type EnvReader interface { 18 | Read(targetConfigPath, environmentName string) (environment.Config, error) 19 | } 20 | 21 | //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . ToolRunner 22 | 23 | type ToolRunner interface { 24 | Run(data environment.Config, dryRun bool, args ...string) error 25 | } 26 | 27 | //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . UI 28 | 29 | type UI interface { 30 | DisplayText(text string) 31 | DisplayError(err error) 32 | } 33 | -------------------------------------------------------------------------------- /commands/om.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | type OMCommand struct { 14 | TargetConfig string `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 15 | EnvironmentName string `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 16 | File bool `short:"f" long:"file" description:"write a script file but do not run it"` 17 | 18 | Env EnvReader 19 | UI UI 20 | OMRunner ToolRunner 21 | } 22 | 23 | func (c *OMCommand) Execute(args []string) error { 24 | data, err := c.Env.Read(c.TargetConfig, c.EnvironmentName) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | return c.OMRunner.Run(data, c.File, args...) 30 | } 31 | -------------------------------------------------------------------------------- /commands/om_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands_test 12 | 13 | import ( 14 | "fmt" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | fakes "github.com/pivotal/hammer/commands/commandsfakes" 19 | "github.com/pivotal/hammer/environment" 20 | 21 | . "github.com/pivotal/hammer/commands" 22 | ) 23 | 24 | var _ = Describe("om command", func() { 25 | var ( 26 | err error 27 | command *OMCommand 28 | 29 | envReader *fakes.FakeEnvReader 30 | ui *fakes.FakeUI 31 | omRunner *fakes.FakeToolRunner 32 | commandArgs []string 33 | ) 34 | 35 | BeforeEach(func() { 36 | envReader = new(fakes.FakeEnvReader) 37 | ui = new(fakes.FakeUI) 38 | omRunner = new(fakes.FakeToolRunner) 39 | commandArgs = []string{"arg1", "arg2"} 40 | 41 | command = &OMCommand{ 42 | Env: envReader, 43 | UI: ui, 44 | OMRunner: omRunner, 45 | File: true, 46 | } 47 | }) 48 | 49 | JustBeforeEach(func() { 50 | err = command.Execute(commandArgs) 51 | }) 52 | 53 | When("retrieving the environment config errors", func() { 54 | BeforeEach(func() { 55 | envReader.ReadReturns(environment.Config{}, fmt.Errorf("env-reader-error")) 56 | }) 57 | 58 | It("doesn't attempt to run the om tool", func() { 59 | Expect(omRunner.RunCallCount()).To(Equal(0)) 60 | }) 61 | 62 | It("propagates the error", func() { 63 | Expect(err).To(MatchError("env-reader-error")) 64 | }) 65 | }) 66 | 67 | When("retrieving the environment config is successful", func() { 68 | BeforeEach(func() { 69 | envReader.ReadReturns(environment.Config{Name: "env-name"}, nil) 70 | }) 71 | 72 | It("runs the om tool using the retrieved environment config", func() { 73 | Expect(omRunner.RunCallCount()).To(Equal(1)) 74 | 75 | environmentConfig, _, _ := omRunner.RunArgsForCall(0) 76 | Expect(environmentConfig).To(BeEquivalentTo(environment.Config{Name: "env-name"})) 77 | }) 78 | 79 | When("run with the file flag set", func() { 80 | BeforeEach(func() { 81 | command.File = true 82 | }) 83 | 84 | It("runs the om tool in dry run mode", func() { 85 | Expect(omRunner.RunCallCount()).To(Equal(1)) 86 | 87 | _, dryRun, _ := omRunner.RunArgsForCall(0) 88 | Expect(dryRun).To(BeTrue()) 89 | }) 90 | }) 91 | 92 | When("run with the file flag unset", func() { 93 | BeforeEach(func() { 94 | command.File = false 95 | }) 96 | 97 | It("runs the om tool in non-dry run mode", func() { 98 | Expect(omRunner.RunCallCount()).To(Equal(1)) 99 | 100 | _, dryRun, _ := omRunner.RunArgsForCall(0) 101 | Expect(dryRun).To(BeFalse()) 102 | }) 103 | }) 104 | 105 | It("runs the om tool using the supplied command args", func() { 106 | Expect(omRunner.RunCallCount()).To(Equal(1)) 107 | 108 | _, _, args := omRunner.RunArgsForCall(0) 109 | Expect(args).To(BeEquivalentTo([]string{"arg1", "arg2"})) 110 | }) 111 | 112 | When("running the om tool is successful", func() { 113 | BeforeEach(func() { 114 | omRunner.RunReturns(nil) 115 | }) 116 | 117 | It("doesn't error", func() { 118 | Expect(err).NotTo(HaveOccurred()) 119 | }) 120 | }) 121 | 122 | When("running the om tool errors", func() { 123 | BeforeEach(func() { 124 | omRunner.RunReturns(fmt.Errorf("om-runnner-error")) 125 | }) 126 | 127 | It("propagates the error", func() { 128 | Expect(err).To(MatchError("om-runnner-error")) 129 | }) 130 | }) 131 | }) 132 | }) 133 | -------------------------------------------------------------------------------- /commands/open.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | import ( 14 | "fmt" 15 | ) 16 | 17 | type OpenCommand struct { 18 | TargetConfig string `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 19 | EnvironmentName string `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 20 | File bool `short:"f" long:"file" description:"write a script file but do not run it"` 21 | Show bool `short:"s" long:"show" description:"only show the credentials"` 22 | 23 | Env EnvReader 24 | UI UI 25 | OpenRunner ToolRunner 26 | } 27 | 28 | func (c *OpenCommand) Execute(args []string) error { 29 | data, err := c.Env.Read(c.TargetConfig, c.EnvironmentName) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | if c.Show { 35 | c.UI.DisplayText(fmt.Sprintf("%s\n", data.OpsManager.URL.String())) 36 | if data.OpsManager.ClientID != "" { 37 | c.UI.DisplayText(fmt.Sprintf("client id: %s\n", data.OpsManager.ClientID)) 38 | c.UI.DisplayText(fmt.Sprintf("client secret: %s\n", data.OpsManager.ClientSecret)) 39 | } else { 40 | c.UI.DisplayText(fmt.Sprintf("username: %s\n", data.OpsManager.Username)) 41 | c.UI.DisplayText(fmt.Sprintf("password: %s\n", data.OpsManager.Password)) 42 | } 43 | return nil 44 | } 45 | 46 | c.UI.DisplayText(fmt.Sprintf("Opening: %s\n", data.OpsManager.URL.String())) 47 | if data.OpsManager.ClientID != "" { 48 | c.UI.DisplayText(fmt.Sprintf("Client ID is: %s\n", data.OpsManager.ClientID)) 49 | c.UI.DisplayText("Client Secret is in the clipboard\n") 50 | } else { 51 | c.UI.DisplayText(fmt.Sprintf("Username is: %s\n", data.OpsManager.Username)) 52 | c.UI.DisplayText("Password is in the clipboard\n") 53 | } 54 | 55 | return c.OpenRunner.Run(data, c.File) 56 | } 57 | -------------------------------------------------------------------------------- /commands/pks_login.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | import ( 14 | "fmt" 15 | ) 16 | 17 | type PKSLoginCommand struct { 18 | TargetConfig string `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 19 | EnvironmentName string `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 20 | File bool `short:"f" long:"file" description:"write a script file but do not run it"` 21 | 22 | Env EnvReader 23 | UI UI 24 | PKSLoginRunner ToolRunner 25 | } 26 | 27 | func (c *PKSLoginCommand) Execute(args []string) error { 28 | data, err := c.Env.Read(c.TargetConfig, c.EnvironmentName) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | c.UI.DisplayText(fmt.Sprintf("Logging in to PKS at: %s\n", data.OpsManager.URL.String())) 34 | 35 | return c.PKSLoginRunner.Run(data, c.File) 36 | } 37 | -------------------------------------------------------------------------------- /commands/pks_login_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands_test 12 | 13 | import ( 14 | "fmt" 15 | "net/url" 16 | 17 | . "github.com/onsi/ginkgo/v2" 18 | . "github.com/onsi/gomega" 19 | fakes "github.com/pivotal/hammer/commands/commandsfakes" 20 | "github.com/pivotal/hammer/environment" 21 | 22 | . "github.com/pivotal/hammer/commands" 23 | ) 24 | 25 | var _ = Describe("pks login command", func() { 26 | var ( 27 | err error 28 | command *PKSLoginCommand 29 | 30 | envReader *fakes.FakeEnvReader 31 | ui *fakes.FakeUI 32 | pksLoginRunner *fakes.FakeToolRunner 33 | commandArgs []string 34 | ) 35 | 36 | BeforeEach(func() { 37 | envReader = new(fakes.FakeEnvReader) 38 | ui = new(fakes.FakeUI) 39 | pksLoginRunner = new(fakes.FakeToolRunner) 40 | commandArgs = []string{"arg1", "arg2"} 41 | 42 | command = &PKSLoginCommand{ 43 | Env: envReader, 44 | UI: ui, 45 | PKSLoginRunner: pksLoginRunner, 46 | File: true, 47 | } 48 | }) 49 | 50 | JustBeforeEach(func() { 51 | err = command.Execute(commandArgs) 52 | }) 53 | 54 | When("retrieving the environment config errors", func() { 55 | BeforeEach(func() { 56 | envReader.ReadReturns(environment.Config{}, fmt.Errorf("env-reader-error")) 57 | }) 58 | 59 | It("doesn't attempt to run the pks login tool", func() { 60 | Expect(pksLoginRunner.RunCallCount()).To(Equal(0)) 61 | }) 62 | 63 | It("propagates the error", func() { 64 | Expect(err).To(MatchError("env-reader-error")) 65 | }) 66 | }) 67 | 68 | When("retrieving the environment config is successful", func() { 69 | BeforeEach(func() { 70 | url, _ := url.Parse("www.test-pks.io") 71 | envReader.ReadReturns(environment.Config{OpsManager: environment.OpsManager{URL: *url}}, nil) 72 | }) 73 | 74 | It("displays that the pks is being logged into", func() { 75 | Expect(ui.DisplayTextCallCount()).To(Equal(1)) 76 | Expect(ui.DisplayTextArgsForCall(0)).To(Equal("Logging in to PKS at: www.test-pks.io\n")) 77 | }) 78 | 79 | It("runs the pks login tool using the retrieved environment config", func() { 80 | Expect(pksLoginRunner.RunCallCount()).To(Equal(1)) 81 | 82 | environmentConfig, _, _ := pksLoginRunner.RunArgsForCall(0) 83 | 84 | expectedURL, _ := url.Parse("www.test-pks.io") 85 | Expect(environmentConfig).To(BeEquivalentTo(environment.Config{OpsManager: environment.OpsManager{URL: *expectedURL}})) 86 | }) 87 | 88 | When("run with the file flag set", func() { 89 | BeforeEach(func() { 90 | command.File = true 91 | }) 92 | 93 | It("runs the pks login tool in dry run mode", func() { 94 | Expect(pksLoginRunner.RunCallCount()).To(Equal(1)) 95 | 96 | _, dryRun, _ := pksLoginRunner.RunArgsForCall(0) 97 | Expect(dryRun).To(BeTrue()) 98 | }) 99 | }) 100 | 101 | When("run with the file flag unset", func() { 102 | BeforeEach(func() { 103 | command.File = false 104 | }) 105 | 106 | It("runs the pks login tool in non-dry run mode", func() { 107 | Expect(pksLoginRunner.RunCallCount()).To(Equal(1)) 108 | 109 | _, dryRun, _ := pksLoginRunner.RunArgsForCall(0) 110 | Expect(dryRun).To(BeFalse()) 111 | }) 112 | }) 113 | 114 | It("runs the pks login tool with no additional args", func() { 115 | Expect(pksLoginRunner.RunCallCount()).To(Equal(1)) 116 | 117 | _, _, args := pksLoginRunner.RunArgsForCall(0) 118 | Expect(args).To(BeEmpty()) 119 | }) 120 | 121 | When("running the pks login tool is successful", func() { 122 | BeforeEach(func() { 123 | pksLoginRunner.RunReturns(nil) 124 | }) 125 | 126 | It("doesn't error", func() { 127 | Expect(err).NotTo(HaveOccurred()) 128 | }) 129 | }) 130 | 131 | When("running the pks login tool errors", func() { 132 | BeforeEach(func() { 133 | pksLoginRunner.RunReturns(fmt.Errorf("pks-login-runnner-error")) 134 | }) 135 | 136 | It("propagates the error", func() { 137 | Expect(err).To(MatchError("pks-login-runnner-error")) 138 | }) 139 | }) 140 | }) 141 | }) 142 | -------------------------------------------------------------------------------- /commands/ssh.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | import ( 14 | "fmt" 15 | "os" 16 | ) 17 | 18 | type targetConfigPath struct{} 19 | 20 | // If `-t` is specified on the ssh command (rather than a subcommand) 21 | // then set `HAMMER_TARGET_CONFIG` so the subcommand can read it 22 | func (e *targetConfigPath) UnmarshalFlag(path string) error { 23 | return os.Setenv("HAMMER_TARGET_CONFIG", path) 24 | } 25 | 26 | type environmentName struct{} 27 | 28 | // If `-e` is specified on the hammer command (rather than a subcommand) 29 | // then set `HAMMER_ENVIRONMENT_NAME` so the subcommand can read it 30 | func (e *environmentName) UnmarshalFlag(name string) error { 31 | return os.Setenv("HAMMER_ENVIRONMENT_NAME", name) 32 | } 33 | 34 | type SSHCommand struct { 35 | TargetConfig targetConfigPath `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 36 | EnvironmentName environmentName `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 37 | Director SSHDirectorCommand `command:"director"` 38 | OpsManager SSHOpsManagerCommand `command:"opsman"` 39 | } 40 | 41 | type SSHDirectorCommand struct { 42 | TargetConfig string `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 43 | EnvironmentName string `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 44 | File bool `short:"f" long:"file" description:"write a script file but do not run it"` 45 | 46 | Env EnvReader 47 | UI UI 48 | SSHRunner ToolRunner 49 | } 50 | 51 | func (c *SSHDirectorCommand) Execute(args []string) error { 52 | data, err := c.Env.Read(c.TargetConfig, c.EnvironmentName) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | c.UI.DisplayText(fmt.Sprintf("Connecting to: %s\n", data.Name)) 58 | 59 | return c.SSHRunner.Run(data, c.File) 60 | } 61 | 62 | type SSHOpsManagerCommand struct { 63 | TargetConfig string `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 64 | EnvironmentName string `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 65 | File bool `short:"f" long:"file" description:"write a script file but do not run it"` 66 | 67 | Env EnvReader 68 | UI UI 69 | SSHRunner ToolRunner 70 | } 71 | 72 | func (c *SSHOpsManagerCommand) Execute(args []string) error { 73 | data, err := c.Env.Read(c.TargetConfig, c.EnvironmentName) 74 | if err != nil { 75 | return err 76 | } 77 | 78 | c.UI.DisplayText(fmt.Sprintf("Connecting to: %s\n", data.Name)) 79 | 80 | return c.SSHRunner.Run(data, c.File) 81 | } 82 | -------------------------------------------------------------------------------- /commands/sshuttle.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands 12 | 13 | type SshuttleCommand struct { 14 | TargetConfig string `short:"t" long:"target" env:"HAMMER_TARGET_CONFIG" hidden:"true"` 15 | EnvironmentName string `short:"e" long:"environment-name" env:"HAMMER_ENVIRONMENT_NAME" hidden:"true"` 16 | File bool `short:"f" long:"file" description:"write a script file but do not run it"` 17 | 18 | Env EnvReader 19 | UI UI 20 | SshuttleRunner ToolRunner 21 | } 22 | 23 | func (c *SshuttleCommand) Execute(args []string) error { 24 | data, err := c.Env.Read(c.TargetConfig, c.EnvironmentName) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | return c.SshuttleRunner.Run(data, c.File) 30 | } 31 | -------------------------------------------------------------------------------- /commands/sshuttle_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package commands_test 12 | 13 | import ( 14 | "fmt" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | fakes "github.com/pivotal/hammer/commands/commandsfakes" 19 | "github.com/pivotal/hammer/environment" 20 | 21 | . "github.com/pivotal/hammer/commands" 22 | ) 23 | 24 | var _ = Describe("sshuttle command", func() { 25 | var ( 26 | err error 27 | command *SshuttleCommand 28 | 29 | envReader *fakes.FakeEnvReader 30 | ui *fakes.FakeUI 31 | sshuttleRunner *fakes.FakeToolRunner 32 | commandArgs []string 33 | ) 34 | 35 | BeforeEach(func() { 36 | envReader = new(fakes.FakeEnvReader) 37 | ui = new(fakes.FakeUI) 38 | sshuttleRunner = new(fakes.FakeToolRunner) 39 | commandArgs = []string{"arg1", "arg2"} 40 | 41 | command = &SshuttleCommand{ 42 | Env: envReader, 43 | UI: ui, 44 | SshuttleRunner: sshuttleRunner, 45 | File: true, 46 | } 47 | }) 48 | 49 | JustBeforeEach(func() { 50 | err = command.Execute(commandArgs) 51 | }) 52 | 53 | When("retrieving the environment config errors", func() { 54 | BeforeEach(func() { 55 | envReader.ReadReturns(environment.Config{}, fmt.Errorf("env-reader-error")) 56 | }) 57 | 58 | It("doesn't attempt to run the sshuttle tool", func() { 59 | Expect(sshuttleRunner.RunCallCount()).To(Equal(0)) 60 | }) 61 | 62 | It("propagates the error", func() { 63 | Expect(err).To(MatchError("env-reader-error")) 64 | }) 65 | }) 66 | 67 | When("retrieving the environment config is successful", func() { 68 | BeforeEach(func() { 69 | envReader.ReadReturns(environment.Config{Name: "env-name"}, nil) 70 | }) 71 | 72 | It("runs the sshuttle tool using the retrieved environment config", func() { 73 | Expect(sshuttleRunner.RunCallCount()).To(Equal(1)) 74 | 75 | environmentConfig, _, _ := sshuttleRunner.RunArgsForCall(0) 76 | Expect(environmentConfig).To(BeEquivalentTo(environment.Config{Name: "env-name"})) 77 | }) 78 | 79 | When("run with the file flag set", func() { 80 | BeforeEach(func() { 81 | command.File = true 82 | }) 83 | 84 | It("runs the sshuttle tool in dry run mode", func() { 85 | Expect(sshuttleRunner.RunCallCount()).To(Equal(1)) 86 | 87 | _, dryRun, _ := sshuttleRunner.RunArgsForCall(0) 88 | Expect(dryRun).To(BeTrue()) 89 | }) 90 | }) 91 | 92 | When("run with the file flag unset", func() { 93 | BeforeEach(func() { 94 | command.File = false 95 | }) 96 | 97 | It("runs the sshuttle tool in non-dry run mode", func() { 98 | Expect(sshuttleRunner.RunCallCount()).To(Equal(1)) 99 | 100 | _, dryRun, _ := sshuttleRunner.RunArgsForCall(0) 101 | Expect(dryRun).To(BeFalse()) 102 | }) 103 | }) 104 | 105 | It("runs the sshuttle tool using the supplied command args", func() { 106 | Expect(sshuttleRunner.RunCallCount()).To(Equal(1)) 107 | 108 | _, _, args := sshuttleRunner.RunArgsForCall(0) 109 | Expect(args).To(BeEmpty()) 110 | }) 111 | 112 | When("running the sshuttle tool is successful", func() { 113 | BeforeEach(func() { 114 | sshuttleRunner.RunReturns(nil) 115 | }) 116 | 117 | It("doesn't error", func() { 118 | Expect(err).NotTo(HaveOccurred()) 119 | }) 120 | }) 121 | 122 | When("running the sshuttle tool errors", func() { 123 | BeforeEach(func() { 124 | sshuttleRunner.RunReturns(fmt.Errorf("sshuttle-runnner-error")) 125 | }) 126 | 127 | It("propagates the error", func() { 128 | Expect(err).To(MatchError("sshuttle-runnner-error")) 129 | }) 130 | }) 131 | }) 132 | }) 133 | -------------------------------------------------------------------------------- /environment/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package environment 12 | 13 | import ( 14 | "fmt" 15 | "net" 16 | "net/url" 17 | "os" 18 | 19 | "github.com/hashicorp/go-version" 20 | "gopkg.in/yaml.v2" 21 | ) 22 | 23 | const defaultSSHUser = "ubuntu" 24 | 25 | type OpsManager struct { 26 | Username string 27 | Password string 28 | ClientID string 29 | ClientSecret string 30 | URL url.URL 31 | IP net.IP 32 | PrivateKey string 33 | SshUser string 34 | } 35 | 36 | type PKSApi struct { 37 | Username string 38 | Password string 39 | URL url.URL 40 | } 41 | 42 | type Config struct { 43 | Name string 44 | Version version.Version 45 | CFDomain string 46 | AppsDomain string 47 | OpsManager OpsManager 48 | PKSApi PKSApi 49 | PasSubnet string 50 | ServiceSubnet string 51 | AZs []string 52 | } 53 | 54 | type environmentReader struct { 55 | Name string `yaml:"name"` 56 | Version string `yaml:"version"` 57 | SysDomain string `yaml:"sys_domain"` 58 | AppsDomain string `yaml:"apps_domain"` 59 | PrivateKey string `yaml:"ops_manager_private_key"` 60 | IP string `yaml:"ops_manager_public_ip"` 61 | SshUser string `yaml:"ops_manager_ssh_user"` 62 | PasSubnet string `yaml:"ert_subnet"` 63 | ServiceSubnet string `yaml:"service_subnet_name"` 64 | AZs []string `yaml:"azs"` 65 | OpsManager struct { 66 | URL string `yaml:"url"` 67 | Username string `yaml:"username"` 68 | Password string `yaml:"password"` 69 | ClientID string `yaml:"client_id"` 70 | ClientSecret string `yaml:"client_secret"` 71 | } `yaml:"ops_manager"` 72 | PKSApi struct { 73 | Username string `yaml:"uaa_admin_user"` 74 | Password string `yaml:"uaa_admin_password"` 75 | URL string `yaml:"url"` 76 | } `yaml:"pks_api"` 77 | } 78 | 79 | func FromFile(path, environmentName string) (Config, error) { 80 | contents, err := os.ReadFile(path) 81 | if err != nil { 82 | return Config{}, err 83 | } 84 | 85 | var selectedEnvironment environmentReader 86 | err = yaml.Unmarshal(contents, &selectedEnvironment) 87 | if err == nil { 88 | if environmentName != "" && environmentName != selectedEnvironment.Name { 89 | return Config{}, fmt.Errorf("Environment name '%s' specified but does not match environment in config", environmentName) 90 | } 91 | return newLockfile(selectedEnvironment) 92 | } 93 | 94 | var environments []environmentReader 95 | if arrayErr := yaml.Unmarshal(contents, &environments); arrayErr != nil { 96 | return Config{}, fmt.Errorf("Unable to unmarshal specified config as either single environment, '%s' or multiple environments, '%s'", err.Error(), arrayErr.Error()) 97 | } 98 | 99 | if len(environments) == 0 { 100 | return Config{}, fmt.Errorf("Target config is an empty array") 101 | } 102 | 103 | if environmentName == "" { 104 | return newLockfile(environments[0]) 105 | } 106 | 107 | for _, environment := range environments { 108 | if environmentName == environment.Name { 109 | selectedEnvironment = environment 110 | } 111 | } 112 | 113 | if selectedEnvironment.Name == "" { 114 | return Config{}, fmt.Errorf("Environment name '%s' specified but does not match environment in config", environmentName) 115 | } 116 | return newLockfile(selectedEnvironment) 117 | 118 | } 119 | 120 | func newLockfile(data environmentReader) (Config, error) { 121 | var err error 122 | 123 | parsedVersion := &version.Version{} 124 | if data.Version != "" { 125 | parsedVersion, err = version.NewVersion(data.Version) 126 | if err != nil { 127 | return Config{}, err 128 | } 129 | } 130 | 131 | parsedOpsManagerURL, err := url.Parse(data.OpsManager.URL) 132 | if err != nil { 133 | return Config{}, err 134 | } 135 | 136 | opsManagerIp := net.ParseIP(data.IP) 137 | if opsManagerIp == nil { 138 | return Config{}, fmt.Errorf("Could not parse IP address: %s", data.IP) 139 | } 140 | 141 | parsedPKSApiURL, err := url.Parse(data.PKSApi.URL) 142 | if err != nil { 143 | return Config{}, err 144 | } 145 | 146 | sshUser := data.SshUser 147 | if sshUser == "" { 148 | sshUser = defaultSSHUser 149 | } 150 | 151 | return Config{ 152 | Name: data.Name, 153 | Version: *parsedVersion, 154 | CFDomain: data.SysDomain, 155 | AppsDomain: data.AppsDomain, 156 | PasSubnet: data.PasSubnet, 157 | ServiceSubnet: data.ServiceSubnet, 158 | AZs: data.AZs, 159 | OpsManager: OpsManager{ 160 | Username: data.OpsManager.Username, 161 | Password: data.OpsManager.Password, 162 | ClientID: data.OpsManager.ClientID, 163 | ClientSecret: data.OpsManager.ClientSecret, 164 | URL: *parsedOpsManagerURL, 165 | IP: opsManagerIp, 166 | PrivateKey: data.PrivateKey, 167 | SshUser: sshUser, 168 | }, 169 | PKSApi: PKSApi{ 170 | Username: data.PKSApi.Username, 171 | Password: data.PKSApi.Password, 172 | URL: *parsedPKSApiURL, 173 | }, 174 | }, nil 175 | } 176 | -------------------------------------------------------------------------------- /environment/config_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package environment_test 12 | 13 | import ( 14 | "net" 15 | "net/url" 16 | "os" 17 | "path" 18 | 19 | "github.com/hashicorp/go-version" 20 | . "github.com/onsi/ginkgo/v2" 21 | . "github.com/onsi/gomega" 22 | . "github.com/onsi/gomega/gstruct" 23 | 24 | . "github.com/pivotal/hammer/environment" 25 | ) 26 | 27 | var _ = Describe("Config", func() { 28 | AfterEach(func() { 29 | os.Unsetenv("HAMMER_TARGET_CONFIG") 30 | }) 31 | 32 | Describe("FromFile", func() { 33 | It("reads data from a json file", func() { 34 | env, err := FromFile(path.Join("fixtures", "lemon.json"), "") 35 | 36 | Expect(err).NotTo(HaveOccurred()) 37 | checkMatchLemon(env) 38 | }) 39 | 40 | It("reads data from a yaml file", func() { 41 | env, err := FromFile(path.Join("fixtures", "lemon.yaml"), "") 42 | 43 | Expect(err).NotTo(HaveOccurred()) 44 | checkMatchLemon(env) 45 | }) 46 | 47 | It("reads data from a config file that does not contain subnets, CIDRs, AZs or version", func() { 48 | env, err := FromFile(path.Join("fixtures", "reduced.json"), "") 49 | 50 | Expect(err).NotTo(HaveOccurred()) 51 | checkMatchReduced(env) 52 | }) 53 | 54 | When("a non-matching environment name is specified", func() { 55 | It("errors with a helpful message", func() { 56 | env, err := FromFile(path.Join("fixtures", "lemon.json"), "non-matching-environment") 57 | 58 | Expect(err).To(MatchError("Environment name 'non-matching-environment' specified but does not match environment in config")) 59 | Expect(env).To(BeEquivalentTo(Config{})) 60 | }) 61 | }) 62 | 63 | When("a file with multiple environment configs is specified", func() { 64 | It("selects the first config if an environment name is not specified", func() { 65 | env, err := FromFile(path.Join("fixtures", "multiple.json"), "") 66 | 67 | Expect(err).NotTo(HaveOccurred()) 68 | checkMatchLemon(env) 69 | }) 70 | 71 | It("selects the appropriate config if a matching environment name is specified", func() { 72 | env, err := FromFile(path.Join("fixtures", "multiple.json"), "reduced-config") 73 | 74 | Expect(err).NotTo(HaveOccurred()) 75 | checkMatchReduced(env) 76 | }) 77 | 78 | It("errors if a non-matching environment name is specified", func() { 79 | env, err := FromFile(path.Join("fixtures", "multiple.json"), "non-existent-config") 80 | 81 | Expect(err).To(MatchError("Environment name 'non-existent-config' specified but does not match environment in config")) 82 | Expect(env).To(BeEquivalentTo(Config{})) 83 | }) 84 | }) 85 | 86 | When("a file with an empty array is specified", func() { 87 | It("errors", func() { 88 | env, err := FromFile(path.Join("fixtures", "empty.json"), "") 89 | 90 | Expect(err).To(MatchError("Target config is an empty array")) 91 | Expect(env).To(BeEquivalentTo(Config{})) 92 | }) 93 | }) 94 | }) 95 | }) 96 | 97 | func mustParseURL(u string) url.URL { 98 | url, err := url.Parse(u) 99 | if err != nil { 100 | panic(err) 101 | } 102 | return *url 103 | } 104 | 105 | func checkMatchLemon(e Config) { 106 | Expect(e).To(MatchAllFields(Fields{ 107 | "Name": Equal("lemon"), 108 | "Version": Equal(*version.Must(version.NewVersion("1.11"))), 109 | "CFDomain": Equal("sys.lemon.cf-app.com"), 110 | "AppsDomain": Equal("apps.lemon.cf-app.com"), 111 | "PasSubnet": Equal("lemon-pas-subnet"), 112 | "ServiceSubnet": Equal("lemon-services-subnet"), 113 | "AZs": Equal([]string{"us-central1-f", "us-central1-a", "us-central1-c"}), 114 | "PKSApi": MatchFields(IgnoreExtras, Fields{ 115 | "Username": Equal("pivotalcf"), 116 | "Password": Equal("fakePassword"), 117 | "URL": Equal(mustParseURL("https://api.pks.lemon-lemon.cf-app.com")), 118 | }), 119 | "OpsManager": MatchAllFields(Fields{ 120 | "Username": Equal("pivotalcf"), 121 | "Password": Equal("fakePassword"), 122 | "ClientID": Equal("fakeClientID"), 123 | "ClientSecret": Equal("fakeClientSecret"), 124 | "URL": Equal(mustParseURL("https://pcf.lemon.cf-app.com")), 125 | "IP": Equal(net.ParseIP("35.225.148.133")), 126 | "SshUser": Equal("ubuntu"), 127 | "PrivateKey": ContainSubstring("BEGIN RSA"), 128 | }), 129 | })) 130 | } 131 | 132 | func checkMatchReduced(e Config) { 133 | Expect(e).To(MatchFields(IgnoreExtras, Fields{ 134 | "Name": Equal("reduced-config"), 135 | "CFDomain": Equal("sys.reduced-config.cf-app.com"), 136 | "PKSApi": MatchFields(IgnoreExtras, Fields{ 137 | "URL": Equal(mustParseURL("https://api.pks.reduced-config.cf-app.com")), 138 | }), 139 | "OpsManager": MatchFields(IgnoreExtras, Fields{ 140 | "Username": Equal("pivotalcf"), 141 | "Password": Equal("fakePassword"), 142 | "URL": Equal(mustParseURL("https://pcf.reduced-config.cf-app.com")), 143 | "IP": Equal(net.ParseIP("35.225.148.133")), 144 | "SshUser": Equal("ubuntu"), 145 | "PrivateKey": ContainSubstring("BEGIN RSA"), 146 | }), 147 | })) 148 | } 149 | -------------------------------------------------------------------------------- /environment/environment_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package environment_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestReader(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "Environment Suite") 23 | } 24 | -------------------------------------------------------------------------------- /environment/fixtures/empty.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /environment/fixtures/lemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps_domain": "apps.lemon.cf-app.com", 3 | "azs": [ 4 | "us-central1-f", 5 | "us-central1-a", 6 | "us-central1-c" 7 | ], 8 | "env_dns_zone_name_servers": [ 9 | "ns-cloud-d1.googledomains.com.", 10 | "ns-cloud-d2.googledomains.com.", 11 | "ns-cloud-d3.googledomains.com.", 12 | "ns-cloud-d4.googledomains.com." 13 | ], 14 | "ert_cidr": "10.0.4.0/24", 15 | "ert_gateway": "10.0.4.1", 16 | "ert_subnet": "lemon-pas-subnet", 17 | "ert_version": "2.0.15", 18 | "http_lb_backend_name": "lemon-httpslb", 19 | "iaas_type": "gcp", 20 | "id": 30, 21 | "name": "lemon", 22 | "ops_manager": { 23 | "password": "fakePassword", 24 | "url": "https://pcf.lemon.cf-app.com", 25 | "client_id": "fakeClientID", 26 | "client_secret": "fakeClientSecret", 27 | "username": "pivotalcf" 28 | }, 29 | "ops_manager_cidr": "10.0.0.0/24", 30 | "ops_manager_dns": "pcf.lemon.cf-app.com", 31 | "ops_manager_gateway": "10.0.0.1", 32 | "ops_manager_private_key": "-----BEGIN RSA PRIVATE KEY-----\nfake\n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\n-----END RSA PRIVATE KEY-----\n", 33 | "ops_manager_public_ip": "35.225.148.133", 34 | "ops_manager_ssh_user": "ubuntu", 35 | "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", 36 | "ops_manager_subnet": "lemon-management-subnet", 37 | "ops_manager_version": "2.0-build.314", 38 | "pks_api": { 39 | "uaa_admin_password": "fakePassword", 40 | "uaa_admin_user": "pivotalcf", 41 | "url": "https://api.pks.lemon-lemon.cf-app.com" 42 | }, 43 | "project": "cf-toolsmiths-pool-us-2", 44 | "region": "us-central1", 45 | "service_network_name": "lemon-pcf-network", 46 | "service_subnet_name": "lemon-services-subnet", 47 | "services_cidr": "10.0.8.0/24", 48 | "services_gateway": "10.0.8.1", 49 | "ssh_router_pool": "lemon-cf-ssh", 50 | "sys_domain": "sys.lemon.cf-app.com", 51 | "tcp_domain": "tcp.lemon.cf-app.com", 52 | "tcp_router_pool": "lemon-cf-tcp", 53 | "version": "1.11", 54 | "vm_tag": "lemon-vms", 55 | "ws_router_pool": "lemon-cf-ws" 56 | } 57 | -------------------------------------------------------------------------------- /environment/fixtures/lemon.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apps_domain: apps.lemon.cf-app.com 3 | azs: 4 | - us-central1-f 5 | - us-central1-a 6 | - us-central1-c 7 | env_dns_zone_name_servers: 8 | - ns-cloud-d1.googledomains.com. 9 | - ns-cloud-d2.googledomains.com. 10 | - ns-cloud-d3.googledomains.com. 11 | - ns-cloud-d4.googledomains.com. 12 | ert_cidr: 10.0.4.0/24 13 | ert_gateway: 10.0.4.1 14 | ert_subnet: lemon-pas-subnet 15 | ert_version: 2.0.15 16 | http_lb_backend_name: lemon-httpslb 17 | iaas_type: gcp 18 | id: 30 19 | name: lemon 20 | ops_manager: 21 | password: fakePassword 22 | url: https://pcf.lemon.cf-app.com 23 | client_id: "fakeClientID" 24 | client_secret: "fakeClientSecret" 25 | username: pivotalcf 26 | ops_manager_cidr: 10.0.0.0/24 27 | ops_manager_dns: pcf.lemon.cf-app.com 28 | ops_manager_gateway: 10.0.0.1 29 | ops_manager_private_key: | 30 | -----BEGIN RSA PRIVATE KEY----- 31 | fake 32 | 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 81 | -----END RSA PRIVATE KEY----- 82 | ops_manager_public_ip: 35.225.148.133 83 | ops_manager_ssh_user: ubuntu 84 | ops_manager_public_key: | 85 | ubuntu:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGPKi11B84ogwuGf4Zmq8/VJjvCxgLcpSLD5xuL0FDR1TCA1aeMidb9Pi/DKMfUORry0XbpllgWSbs3YbIEG6FFJEKe68zra/pJSyHH8UE41eULLGWP0ybdAPv5Tvq5D/g1ZtYUnX0jye333akVddM3qh52Dthye0+VKVt4MOH+q2XBLFQ66rhDd6U65MI9YzVpoPVam2WYngGY0a+Am+eo9pww36zk0/osDzLrD57hg3A0v+Otft7l/EiV6elTqLJxHg8BpO0bxWCdBFyJ/i0v+upfrbSgiMAP1QXjZkcSdDeLg0bS5QFX8Nei1/ZjvIHWQHwSUxDfIi67ly+BNmgQHuR2FPFemEkx7EqicV7TWuxfEevMc1edFFkdtzKv1tG8yQEtxPhX6tamz4TBmsu2a5fZ8er47bTI4x7tgCJdNEjhsDVlA1jsqU82i7JLjfXFhbu1hwfBVmKWaaWSeGYMxC8hAGKFXg77Ebo/Nd87urBApmwTOTj5sOFKrOz2BSqiZHYJ/2n8NeH7+CCXgdZQnHrt+u5DyNSpJSorQkNDgQzPunh9TmAVOh2of8gfqryjht6UayWyw1g3S1B1KXgP5RlvfnwL0yhOryLDk3qcpaivsAqFQAKVM6X9KHIbn83rljJxQQ7SmPTJ6TsSKtdZxFlWZklHz4e/smLnclOzQ== 86 | ops_manager_subnet: lemon-management-subnet 87 | ops_manager_version: 2.0-build.314 88 | pks_api: 89 | uaa_admin_password: fakePassword 90 | uaa_admin_user: pivotalcf 91 | url: https://api.pks.lemon-lemon.cf-app.com 92 | project: cf-toolsmiths-pool-us-2 93 | region: us-central1 94 | service_network_name: lemon-pcf-network 95 | service_subnet_name: lemon-services-subnet 96 | services_cidr: 10.0.8.0/24 97 | services_gateway: 10.0.8.1 98 | ssh_router_pool: lemon-cf-ssh 99 | sys_domain: sys.lemon.cf-app.com 100 | tcp_domain: tcp.lemon.cf-app.com 101 | tcp_router_pool: lemon-cf-tcp 102 | version: "1.11" 103 | vm_tag: lemon-vms 104 | ws_router_pool: lemon-cf-ws 105 | -------------------------------------------------------------------------------- /environment/fixtures/reduced.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reduced-config", 3 | "pks_api": { 4 | "url": "https://api.pks.reduced-config.cf-app.com" 5 | }, 6 | "ops_manager": { 7 | "password": "fakePassword", 8 | "url": "https://pcf.reduced-config.cf-app.com", 9 | "username": "pivotalcf" 10 | }, 11 | "ops_manager_private_key": "-----BEGIN RSA PRIVATE KEY-----\nfake\n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\n-----END RSA PRIVATE KEY-----\n", 12 | "ops_manager_public_ip": "35.225.148.133", 13 | "sys_domain": "sys.reduced-config.cf-app.com" 14 | } 15 | -------------------------------------------------------------------------------- /environment/reader.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package environment 12 | 13 | import ( 14 | "fmt" 15 | ) 16 | 17 | type Reader struct{} 18 | 19 | func (er *Reader) Read(targetConfigPath, environmentName string) (Config, error) { 20 | if targetConfigPath == "" { 21 | return Config{}, fmt.Errorf("You must specify the target environment config path (--target | -t) flag") 22 | } 23 | return FromFile(targetConfigPath, environmentName) 24 | } 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pivotal/hammer 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.2 6 | 7 | require ( 8 | github.com/hashicorp/go-version v1.7.0 9 | github.com/jessevdk/go-flags v1.6.1 10 | github.com/maxbrunsfeld/counterfeiter/v6 v6.4.1 11 | github.com/onsi/ginkgo/v2 v2.23.3 12 | github.com/onsi/gomega v1.37.0 13 | gopkg.in/yaml.v2 v2.4.0 14 | ) 15 | 16 | require ( 17 | github.com/go-logr/logr v1.4.2 // indirect 18 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 19 | github.com/google/go-cmp v0.7.0 // indirect 20 | github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect 21 | golang.org/x/mod v0.23.0 // indirect 22 | golang.org/x/net v0.38.0 // indirect 23 | golang.org/x/sync v0.12.0 // indirect 24 | golang.org/x/sys v0.31.0 // indirect 25 | golang.org/x/text v0.23.0 // indirect 26 | golang.org/x/tools v0.30.0 // indirect 27 | gopkg.in/yaml.v3 v3.0.1 // indirect 28 | ) 29 | -------------------------------------------------------------------------------- /hammer.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | # This file was generated by GoReleaser. DO NOT EDIT. 5 | class Hammer < Formula 6 | desc "" 7 | homepage "" 8 | version "0.13.0" 9 | 10 | on_macos do 11 | if Hardware::CPU.intel? 12 | url "https://github.com/pivotal/hammer/releases/download/v0.13.0/hammer_darwin_amd64.tar.gz" 13 | sha256 "83590b8f19eb26ca29ebeaa8257bedb27ab83a059f5faeabb4f816d27f2281d6" 14 | 15 | def install 16 | bin.install "hammer" 17 | end 18 | end 19 | if Hardware::CPU.arm? 20 | url "https://github.com/pivotal/hammer/releases/download/v0.13.0/hammer_darwin_arm64.tar.gz" 21 | sha256 "eb8ad96275d64455d6605a3f356ad933a1c6946dad86917d18bebe24357a4362" 22 | 23 | def install 24 | bin.install "hammer" 25 | end 26 | end 27 | end 28 | 29 | on_linux do 30 | if Hardware::CPU.intel? and Hardware::CPU.is_64_bit? 31 | url "https://github.com/pivotal/hammer/releases/download/v0.13.0/hammer_linux_amd64.tar.gz" 32 | sha256 "8f188383d6f5764d78cf616d36693ff446ed33c5649e95619913f9383cd83f5f" 33 | def install 34 | bin.install "hammer" 35 | end 36 | end 37 | if Hardware::CPU.arm? and Hardware::CPU.is_64_bit? 38 | url "https://github.com/pivotal/hammer/releases/download/v0.13.0/hammer_linux_arm64.tar.gz" 39 | sha256 "e4317a61e92dfd724c96d28a8eb53d1a32e7929b82aec6a4363f4f1a40d5c3a0" 40 | def install 41 | bin.install "hammer" 42 | end 43 | end 44 | end 45 | 46 | test do 47 | system "#{bin}/hammer version" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /integration/base_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os/exec" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | . "github.com/onsi/gomega/gbytes" 19 | . "github.com/onsi/gomega/gexec" 20 | ) 21 | 22 | var _ = Describe("CLI", func() { 23 | When("no command or flag is passed", func() { 24 | It("displays help and exits zero", func() { 25 | command := exec.Command(pathToPcf) 26 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 27 | Expect(err).NotTo(HaveOccurred()) 28 | 29 | Eventually(session).Should(Exit(0)) 30 | 31 | Eventually(session).Should(SatisfyAll( 32 | Say(`Usage:`), 33 | Say(`hammer \[OPTIONS\] `), 34 | Say(`Application Options:`), 35 | Say(`-t, --target= path to the target environment config`), 36 | Say(`-e, --environment-name= name of the environment in the config to target`), 37 | Say(`Help Options:`), 38 | Say(`-h, --help Show this help message`), 39 | Say(`Available commands:`), 40 | Say(`bosh display BOSH credentials, or run a BOSH command`), 41 | Say(`cf-login log in to the cf for the environment`), 42 | Say(`completion command completion script`), 43 | Say(`om run the 'om' command with credentials for this environment`), 44 | Say(`open open a browser to this environment`), 45 | Say(`ssh open an ssh connection to the ops manager or director of this environment`), 46 | Say(`sshuttle sshuttle to this environment`), 47 | Say(`version version of command \(aliases: ver\)`), 48 | )) 49 | }) 50 | }) 51 | 52 | When("--help is passed", func() { 53 | It("displays help and exits zero", func() { 54 | command := exec.Command(pathToPcf, "--help") 55 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 56 | Expect(err).NotTo(HaveOccurred()) 57 | 58 | Eventually(session).Should(Exit(0)) 59 | 60 | Eventually(session).Should(SatisfyAll( 61 | Say(`Usage:`), 62 | Say(`hammer \[OPTIONS\] `), 63 | Say(`Application Options:`), 64 | Say(`-t, --target= path to the target environment config`), 65 | Say(`-e, --environment-name= name of the environment in the config to target`), 66 | Say(`Help Options:`), 67 | Say(`-h, --help Show this help message`), 68 | Say(`Available commands:`), 69 | Say(`bosh display BOSH credentials, or run a BOSH command`), 70 | Say(`cf-login log in to the cf for the environment`), 71 | Say(`completion command completion script`), 72 | Say(`om run the 'om' command with credentials for this environment`), 73 | Say(`open open a browser to this environment`), 74 | Say(`ssh open an ssh connection to the ops manager or director of this environment`), 75 | Say(`sshuttle sshuttle to this environment`), 76 | Say(`version version of command \(aliases: ver\)`), 77 | )) 78 | }) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /integration/bosh_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os" 15 | "os/exec" 16 | "strings" 17 | 18 | . "github.com/onsi/ginkgo/v2" 19 | . "github.com/onsi/gomega" 20 | . "github.com/onsi/gomega/gexec" 21 | ) 22 | 23 | var _ = Describe("BOSH", func() { 24 | When("getting the BOSH credentials", func() { 25 | It("generates the correct script", func() { 26 | command := exec.Command(pathToPcf, "bosh", "-t", "fixtures/claim_manatee_response.json", "-f") 27 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 28 | Expect(err).NotTo(HaveOccurred()) 29 | 30 | Eventually(session).Should(Exit(0)) 31 | Eventually(string(session.Err.Contents())).Should(Equal("")) 32 | 33 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 34 | pathToFile := LastLine(output) 35 | contents, err := os.ReadFile(pathToFile) 36 | Expect(err).NotTo(HaveOccurred()) 37 | 38 | Expect(string(contents)).To(Equal(LoadFixture("bosh_creds_script.sh"))) 39 | }) 40 | }) 41 | 42 | When("running a BOSH command", func() { 43 | It("generates the correct script", func() { 44 | command := exec.Command(pathToPcf, "bosh", "-t", "fixtures/claim_manatee_response.json", "-f", "deployments") 45 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 46 | Expect(err).NotTo(HaveOccurred()) 47 | 48 | Eventually(session).Should(Exit(0)) 49 | Eventually(string(session.Err.Contents())).Should(Equal("")) 50 | 51 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 52 | pathToFile := LastLine(output) 53 | contents, err := os.ReadFile(pathToFile) 54 | Expect(err).NotTo(HaveOccurred()) 55 | 56 | Expect(string(contents)).To(Equal(LoadFixture("bosh_cmd_script.sh"))) 57 | }) 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /integration/cf_login_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os" 15 | "os/exec" 16 | "strings" 17 | 18 | . "github.com/onsi/ginkgo/v2" 19 | . "github.com/onsi/gomega" 20 | . "github.com/onsi/gomega/gbytes" 21 | . "github.com/onsi/gomega/gexec" 22 | ) 23 | 24 | var _ = Describe("CF", func() { 25 | It("generates the correct script", func() { 26 | command := exec.Command(pathToPcf, "cf-login", "-t", "fixtures/claim_manatee_response.json", "-f") 27 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 28 | Expect(err).NotTo(HaveOccurred()) 29 | 30 | Eventually(session).Should(Exit(0)) 31 | Eventually(string(session.Err.Contents())).Should(Equal("")) 32 | Eventually(session.Out).Should(Say("Logging in to CF at: https://pcf.manatee.cf-app.com")) 33 | 34 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 35 | pathToFile := LastLine(output) 36 | contents, err := os.ReadFile(pathToFile) 37 | Expect(err).NotTo(HaveOccurred()) 38 | 39 | Expect(string(contents)).To(Equal(LoadFixture("cf_script.sh"))) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /integration/completion_script_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | . "github.com/onsi/ginkgo/v2" 15 | . "github.com/onsi/gomega" 16 | . "github.com/onsi/gomega/gbytes" 17 | . "github.com/onsi/gomega/gexec" 18 | 19 | "os/exec" 20 | ) 21 | 22 | var _ = Describe("Completion Script", func() { 23 | It("prints something that looks like a script", func() { 24 | command := exec.Command(pathToPcf, "completion", "bash") 25 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 26 | Expect(err).NotTo(HaveOccurred()) 27 | 28 | Eventually(session).Should(Exit(0)) 29 | Eventually(string(session.Err.Contents())).Should(Equal("")) 30 | Eventually(session.Out).Should(Say("GO_FLAGS_COMPLETION")) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /integration/fixtures/bosh_cmd_script.sh: -------------------------------------------------------------------------------- 1 | ssh_key_path=$(mktemp) 2 | echo "-----BEGIN RSA PRIVATE KEY-----fake 3 | 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 52 | -----END RSA PRIVATE KEY----- 53 | " >"$ssh_key_path" 54 | chmod 0600 "${ssh_key_path}" 55 | bosh_ca_path=$(mktemp) 56 | ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i "${ssh_key_path}" ubuntu@"35.225.148.133" cat /var/tempest/workspaces/default/root_ca_certificate 1>"${bosh_ca_path}" 2>/dev/null 57 | chmod 0600 "${bosh_ca_path}" 58 | creds="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/deployed/director/credentials/bosh_commandline_credentials)" 59 | bosh_all="$(echo "$creds" | jq -r .credential | tr ' ' '\n' | grep '=')" 60 | bosh_client="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_CLIENT=')" 61 | bosh_env="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_ENVIRONMENT=')" 62 | bosh_secret="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_CLIENT_SECRET=')" 63 | bosh_ca_cert="BOSH_CA_CERT=$bosh_ca_path" 64 | bosh_proxy="BOSH_ALL_PROXY=ssh+socks5://ubuntu@35.225.148.133:22?private-key=${ssh_key_path}" 65 | bosh_gw_host="BOSH_GW_HOST=35.225.148.133" 66 | bosh_gw_user="BOSH_GW_USER=ubuntu" 67 | bosh_gw_private_key="BOSH_GW_PRIVATE_KEY=${ssh_key_path}" 68 | trap 'rm -f ${ssh_key_path} ${bosh_ca_path}' EXIT 69 | /usr/bin/env "$bosh_client" "$bosh_env" "$bosh_secret" "$bosh_ca_cert" "$bosh_proxy" "$bosh_gw_host" "$bosh_gw_user" "$bosh_gw_private_key" bosh deployments 70 | -------------------------------------------------------------------------------- /integration/fixtures/bosh_creds_script.sh: -------------------------------------------------------------------------------- 1 | ssh_key_path=$(mktemp) 2 | echo "-----BEGIN RSA PRIVATE KEY-----fake 3 | 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 52 | -----END RSA PRIVATE KEY----- 53 | " >"$ssh_key_path" 54 | chmod 0600 "${ssh_key_path}" 55 | bosh_ca_path=$(mktemp) 56 | ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i "${ssh_key_path}" ubuntu@"35.225.148.133" cat /var/tempest/workspaces/default/root_ca_certificate 1>"${bosh_ca_path}" 2>/dev/null 57 | chmod 0600 "${bosh_ca_path}" 58 | creds="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/deployed/director/credentials/bosh_commandline_credentials)" 59 | bosh_all="$(echo "$creds" | jq -r .credential | tr ' ' '\n' | grep '=')" 60 | bosh_client="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_CLIENT=')" 61 | bosh_env="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_ENVIRONMENT=')" 62 | bosh_secret="$(echo "$bosh_all" | tr ' ' '\n' | grep 'BOSH_CLIENT_SECRET=')" 63 | bosh_ca_cert="BOSH_CA_CERT=$bosh_ca_path" 64 | bosh_proxy="BOSH_ALL_PROXY=ssh+socks5://ubuntu@35.225.148.133:22?private-key=${ssh_key_path}" 65 | bosh_gw_host="BOSH_GW_HOST=35.225.148.133" 66 | bosh_gw_user="BOSH_GW_USER=ubuntu" 67 | bosh_gw_private_key="BOSH_GW_PRIVATE_KEY=${ssh_key_path}" 68 | echo "export BOSH_ENV_NAME=manatee" 69 | echo "export $bosh_client" 70 | echo "export $bosh_env" 71 | echo "export $bosh_secret" 72 | echo "export $bosh_ca_cert" 73 | echo "export $bosh_proxy" 74 | echo "export $bosh_gw_host" 75 | echo "export $bosh_gw_user" 76 | echo "export $bosh_gw_private_key" 77 | echo "export CREDHUB_SERVER=\"\${BOSH_ENVIRONMENT}:8844\"" 78 | echo "export CREDHUB_PROXY=\"\${BOSH_ALL_PROXY}\"" 79 | echo "export CREDHUB_CLIENT=\"\${BOSH_CLIENT}\"" 80 | echo "export CREDHUB_SECRET=\"\${BOSH_CLIENT_SECRET}\"" 81 | echo "export CREDHUB_CA_CERT=\"\${BOSH_CA_CERT}\"" 82 | -------------------------------------------------------------------------------- /integration/fixtures/cf_script.sh: -------------------------------------------------------------------------------- 1 | prods="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/staged/products)" 2 | guid="$(echo "$prods" | jq -r '.[] | select(.type == "cf") | .guid')" 3 | creds="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/deployed/products/"$guid"/credentials/.uaa.admin_credentials)" 4 | export CF_USERNAME="$(echo "$creds" | jq -r .credential.value.identity)" 5 | export CF_PASSWORD="$(echo "$creds" | jq -r .credential.value.password)" 6 | cf api "api.sys.manatee.cf-app.com" --skip-ssl-validation 7 | cf auth 8 | -------------------------------------------------------------------------------- /integration/fixtures/claim_manatee_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps_domain": "apps.manatee.cf-app.com", 3 | "azs": [ 4 | "us-central1-f", 5 | "us-central1-a", 6 | "us-central1-c" 7 | ], 8 | "env_dns_zone_name_servers": [ 9 | "ns-cloud-d1.googledomains.com.", 10 | "ns-cloud-d2.googledomains.com.", 11 | "ns-cloud-d3.googledomains.com.", 12 | "ns-cloud-d4.googledomains.com." 13 | ], 14 | "ert_cidr": "10.0.4.0/24", 15 | "ert_gateway": "10.0.4.1", 16 | "ert_subnet": "manatee-pas-subnet", 17 | "ert_version": "2.0.15", 18 | "http_lb_backend_name": "manatee-httpslb", 19 | "iaas_type": "gcp", 20 | "id": 30, 21 | "name": "manatee", 22 | "ops_manager": { 23 | "password": "fakePassword", 24 | "url": "https://pcf.manatee.cf-app.com", 25 | "client_id": "fakeClientID", 26 | "client_secret": "fakeClientSecret", 27 | "username": "pivotalcf" 28 | }, 29 | "ops_manager_cidr": "10.0.0.0/24", 30 | "ops_manager_dns": "pcf.manatee.cf-app.com", 31 | "ops_manager_gateway": "10.0.0.1", 32 | "ops_manager_private_key": "-----BEGIN RSA PRIVATE KEY-----fake\n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\n-----END RSA PRIVATE KEY-----\n", 33 | "ops_manager_public_ip": "35.225.148.133", 34 | "ops_manager_ssh_user": "ubuntu", 35 | "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", 36 | "ops_manager_subnet": "manatee-management-subnet", 37 | "ops_manager_version": "2.0-build.314", 38 | "pks_api": { 39 | "uaa_admin_password": "password ", 40 | "uaa_admin_user": "admin", 41 | "url": "https://api.pks.manatee.cf-app.com" 42 | }, 43 | "project": "cf-toolsmiths-pool-us-2", 44 | "region": "us-central1", 45 | "service_network_name": "manatee-pcf-network", 46 | "service_subnet_name": "manatee-services-subnet", 47 | "services_cidr": "10.0.8.0/24", 48 | "services_gateway": "10.0.8.1", 49 | "ssh_router_pool": "manatee-cf-ssh", 50 | "sys_domain": "sys.manatee.cf-app.com", 51 | "tcp_domain": "tcp.manatee.cf-app.com", 52 | "tcp_router_pool": "manatee-cf-tcp", 53 | "version": "2.0", 54 | "vm_tag": "manatee-vms", 55 | "ws_router_pool": "manatee-cf-ws" 56 | } 57 | -------------------------------------------------------------------------------- /integration/fixtures/om_script.sh: -------------------------------------------------------------------------------- 1 | OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t 'https://pcf.manatee.cf-app.com' -k 'foo' 2 | -------------------------------------------------------------------------------- /integration/fixtures/om_script_json.sh: -------------------------------------------------------------------------------- 1 | OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t 'https://pcf.manatee.cf-app.com' -k 'configure-product' '--product-name' 'p-rabbitmq' '--product-properties' '{".rabbitmq-server.server_admin_credentials":{"value":{"identity":"admin","password":"admin"}}}' 2 | -------------------------------------------------------------------------------- /integration/fixtures/open_script.sh: -------------------------------------------------------------------------------- 1 | open "https://pcf.manatee.cf-app.com" 2 | echo "fakeClientSecret" | pbcopy 3 | -------------------------------------------------------------------------------- /integration/fixtures/pks_script.sh: -------------------------------------------------------------------------------- 1 | prods="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/staged/products)" 2 | guid="$(echo "$prods" | jq -r '.[] | select(.type == "pivotal-container-service") | .guid')" 3 | creds="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/deployed/products/"$guid"/credentials/.properties.uaa_admin_password)" 4 | pass="$(echo "$creds" | jq -r .credential.value.secret)" 5 | pks login -a https://api.pks.manatee.cf-app.com -u admin -p "$pass" --skip-ssl-validation 6 | -------------------------------------------------------------------------------- /integration/fixtures/ssh_director_script.sh: -------------------------------------------------------------------------------- 1 | ssh_key_path=$(mktemp temp.XXXXXX) 2 | echo "-----BEGIN RSA PRIVATE KEY-----fake 3 | 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 52 | -----END RSA PRIVATE KEY----- 53 | " >"$ssh_key_path" 54 | chmod 0600 "${ssh_key_path}" 55 | director_ssh_key="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/deployed/director/credentials/bbr_ssh_credentials | jq -r .credential.value.private_key_pem)" 56 | director_ssh_key_path=$(mktemp) 57 | echo -e "$director_ssh_key" > "$director_ssh_key_path" 58 | chmod 0600 "${director_ssh_key_path}" 59 | bosh_env="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/deployed/director/credentials/bosh_commandline_credentials | grep -o "BOSH_ENVIRONMENT=\S*" | cut -f2 -d=)" 60 | trap 'rm -f ${director_ssh_key_path}; rm -f ${ssh_key_path}' EXIT 61 | jumpbox_cmd="ubuntu@35.225.148.133 -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i ${ssh_key_path}" 62 | ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -J "$jumpbox_cmd" "bbr@${bosh_env}" -i "$director_ssh_key_path" 63 | -------------------------------------------------------------------------------- /integration/fixtures/ssh_opsman_script.sh: -------------------------------------------------------------------------------- 1 | ssh_key_path=$(mktemp) 2 | echo "-----BEGIN RSA PRIVATE KEY-----fake 3 | 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 52 | -----END RSA PRIVATE KEY----- 53 | " >"$ssh_key_path" 54 | trap 'rm -f ${ssh_key_path}' EXIT 55 | chmod 0600 "${ssh_key_path}" 56 | creds="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/deployed/director/credentials/bosh_commandline_credentials)" 57 | bosh="$(echo "$creds" | jq -r .credential | tr ' ' '\n' | grep '=')" 58 | echo "$bosh" 59 | shell="/usr/bin/env $(echo "$bosh" | tr '\n' ' ') bash -l" 60 | ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i "${ssh_key_path}" -t ubuntu@"35.225.148.133" "$shell" 61 | -------------------------------------------------------------------------------- /integration/fixtures/sshuttle_script.sh: -------------------------------------------------------------------------------- 1 | ssh_key_path=$(mktemp) 2 | echo "-----BEGIN RSA PRIVATE KEY-----fake 3 | MIIJKAIBAAKCAgEAxjyotdQfOKIMLhn+GZqvP1SY7wsYC3KUiw+cbi9BQ0dUwgNW 4 | njInW/T4vwyjH1Dka8tF26ZZYFkm7N2GyBBuhRSRCnuvM62v6SUshx/FBONXlCyx 5 | lj9Mm3QD7+U76uQ/4NWbWFJ19I8nt992pFXXTN6oedg7YcntPlSlbeDDh/qtlwSx 6 | UOuq4Q3elOuTCPWM1aaD1WptlmJ4BmNGvgJvnqPacMN+s5NP6LA8y6w+e4YNwNL/ 7 | jrX7e5fxIlenpU6iycR4PAaTtG8VgnQRcif4tL/rqX620oIjAD9UF42ZHEnQ3i4N 8 | G0uUBV/DXotf2Y7yB1kB8ElMQ3yIuu5cvgTZoEB7kdhTxXphJMexKonFe01rsXxH 9 | rzHNXnRRZHbcyr9bRvMkBLcT4V+rWps+EwZrLtmuX2fHq+O20yOMe7YAiXTRI4bA 10 | 1ZQNY7KlPNouyS431xYW7tYcHwVZilmmlknhmDMQvIQBihV4O+xG6PzXfO7qwQKZ 11 | sEzk4+bDhSqzs9gUqomR2Cf9p/DXh+/ggl4HWUJx67fruQ8jUqSUqK0JDQ4EMz7p 12 | 4fU5gFTodqH/IH6q8o4belGslssNYN0tQdSl4D+UZb358C9MoTq8iw5N6nKWor7A 13 | KhUAClTOl/ShyG5/N65YycUEO0pj0yek7EirXWcRZVmZJR8+Hv7Ji53JTs0CAwEA 14 | AQKCAgB5kfOo5rhq473ye9A/5YP5o6jSWAxb4N3F1kIJtIMiflk1ThTNmVZX54iw 15 | qVBUQSiEDrn9tSt0kdf0RPqGMMWGaZF1S1qrp6WMplaDW3FYyQ8JeMtDp/gUEXbk 16 | pME1ENs3x+enp5Jc83nZInrA1z/dDWNmrbvlAY+zvPdixgdDmfDg/2i6hnxZ3kaV 17 | L08RnHzM/Xw14jnokuSmjjVxC09mi3fH1awa9ol32rS99xiuagx/JybAX0wlhI3F 18 | kNnn36ynbgyKS0JD0ifvw7x7NJYqStR8MpBt0o8idLPeaeDu3znvN3CkP5o8HXlQ 19 | vsd+RVKaPPSaZmkVrgawFMPdaF4I+Wf3cZFpRr7yeoVZkddPmKE9ISJPr4gpQZRk 20 | +SJHpTlbVFYzKFB0LnBalCbmlQXHue9Rqyhm3DTETr+gW9a241IpzhEvj7gV9cyI 21 | 3W6+z5Ryb3NpMSlZxQj8Tea6zPIPJD7EB6cydWQzYiICdc7J3TlIyXdZs2H+XRdH 22 | qps0yPsPE2MT9KDnES01ck6kGF4MVqiVZlU/VQ9eSzKu4FeycrschpiQfsUODqb3 23 | wWLfK8vne4oaG8dV0PzbbwT2q3R5pw4FalFLg2XfiacSbL3/+bto3mtyzdzhLr2h 24 | FVBBpnuBrJ9tDg+lSzKT3j+KlwxiRjmh7EicsSL53fMDph3X5QKCAQEAxr3otq7H 25 | 2v72PrLcyVG3+/cPN/yk5biaWuIF961IYF2bMXj/7jG7SX/rOSOIysBtdKHjQVvI 26 | 9Nofx5c8IB+I7ehoQRgTEDrYXpHPoB5unWSuvQveGYknATLd9br+wRQXBalYb3lP 27 | FcZHrGOzshRyOUBTtLgBkad277TCjhS8jvVmJjhJ+ni/8jB9i7twdVeHJQsnHnVE 28 | U1x98oHsrg1BthbYcIxOHvpWnWikfWsHV6LOwo2eCAxu3/uPcrezMv65+H4zo73O 29 | YOSIN48SV+12llx5u2Ene3LKf1V27dwdexwCnaWJhaEhkFPKKrHizhXbCsAqF9x1 30 | bvQSs+qIQNXc3wKCAQEA/1mDPEBNIhRh2hXtjPr3pQtys+ROa8BtfMVcgzQDkyun 31 | 4sExxrN7Zsk9z9eai8tJWVW2VU0W4smqP1cCEkhmugzYpayly4ozVvbrJbjPAy0l 32 | opF9ywKvdtSkyshRrmP8SWlZ3Fg3P+9AA363wuwqfMcsPzLkyRBbJ7k7IAUbK7IV 33 | 8dE+e8H3jhzn4kS2JGt7vSAqx+fWNI8ErjG6cRJp/o7uKxfoCMkvxQ9bXFCTvLxV 34 | Z66DY1fZ2b1zP6pyH/k/FXadkWYguBKJFPhpkrX0DMRwqMsqDrJiPQmWARabODJ4 35 | +7l54gRrtFFuZJr6ga4OOFetMks0tnAhugIoHWEd0wKCAQBGIUF7JVXUy19tZIRQ 36 | bgu0V8wA/5/YiDm/lD5y1azShHV9EauOC/KAzyjb8tQUZ664F2Av8gmJjWblcVKA 37 | K2CQQxB2193yPjGXvXA8mkCCPcMDZqqq7CDdZWu5iJDY5RPypX1VaMMldeBYzp3c 38 | WyarWS2webXkBeoMUAbPSDX5ZFL1bat9PRrOhZnnsu7OGWLRCYx2TP8dQ2gWKQ6x 39 | pG/pp0oc/QWcHM5voTtaHmhq3kKDmKGJSYnec9B8tKIBfq11KGNkhlfLDRmcMVpR 40 | uLO9/0fdfT0HbMxT/n6HFMM6a/noUz+h7egAG/Ec+8SC4Gd49bJk1SCDVJE896e4 41 | 6T6VAoIBAQC9rZZoqTMiv5U4VNWSxdIzTV+09UCzlcs+xbRE2dxV9DXPoMcC3Mwr 42 | vA0oWPW6JhWkQKEimBMJ/9dxGYa2YgW86lH8L1Uw7sXYMa7W5IMRzvWH3x9cL/B7 43 | CtYms8rJCgib16Aip3zTBzu73ONP7fJZofHiOEHugTHVPikHTgOy36ShbldFwv3L 44 | GPm/AF90ikruX/inajWZ8Sr+4n2mBJGrwCx24llAPYI4lYG9/zTfG98tF1sN7nIq 45 | 5im3/zqrOZB8txGwsfZYBZzCcQyrKHHTEhDFsbTaGfQTdw1wXYSLqCJqiopZNAN1 46 | fI/ON7khn1N80tQa6faMFptW+sclm8B1AoIBAAv6Ltiw3zorxwz7L4t355RsMEy+ 47 | LIz+g2ypFKIFEVSLCO5x69xtXCqu8o2wtxCmsMrj6xS7gmb4ZgM8eukyauy0quaw 48 | o1rgwQvpc4VUwsubYsiDA34f2gDEwsWl2un44yJyinZWH962vDgbNcVgnFNsKq2H 49 | jAOMLwEoTAlv15+wl4r/Mzu7F+vWIiv2OAVkEdxknvXOc+F82G5PqOlsQ17vDu90 50 | ZLuM3MSg63owoj01309KLkd0K+jh50SRmAdYcMF2Rwp+pmCD1umxkowU+JAeWdYU 51 | 7FTejWnWFjLVFX3zNKWW8Qbq6TQyNPfwHqCqjj/Rc4Lkv//v1XVHCIbPsKw= 52 | -----END RSA PRIVATE KEY----- 53 | " >"$ssh_key_path" 54 | trap 'rm -f ${ssh_key_path}' EXIT 55 | chmod 0600 "${ssh_key_path}" 56 | cidrs="$(OM_CLIENT_ID='fakeClientID' OM_CLIENT_SECRET='fakeClientSecret' OM_USERNAME='pivotalcf' OM_PASSWORD='fakePassword' om -t https://pcf.manatee.cf-app.com -k curl -s -p /api/v0/staged/director/networks | jq -r .networks[].subnets[].cidr | xargs echo)" 57 | sshuttle --ssh-cmd "ssh -o IdentitiesOnly=yes -i ${ssh_key_path}" -r ubuntu@"35.225.148.133" "${cidrs}" 58 | -------------------------------------------------------------------------------- /integration/integration_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | . "github.com/onsi/gomega/gexec" 19 | ) 20 | 21 | func TestCommand(t *testing.T) { 22 | RegisterFailHandler(Fail) 23 | RunSpecs(t, "Integration Suite") 24 | } 25 | 26 | var pathToPcf string 27 | 28 | var _ = BeforeSuite(func() { 29 | var err error 30 | pathToPcf, err = Build("github.com/pivotal/hammer") 31 | Expect(err).NotTo(HaveOccurred()) 32 | }) 33 | 34 | var _ = AfterSuite(func() { 35 | CleanupBuildArtifacts() 36 | }) 37 | -------------------------------------------------------------------------------- /integration/om_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os" 15 | "os/exec" 16 | "strings" 17 | 18 | . "github.com/onsi/ginkgo/v2" 19 | . "github.com/onsi/gomega" 20 | . "github.com/onsi/gomega/gexec" 21 | ) 22 | 23 | var _ = Describe("OM", func() { 24 | When("run with parameters", func() { 25 | It("generates the correct script", func() { 26 | command := exec.Command(pathToPcf, "om", "-t", "fixtures/claim_manatee_response.json", "-f", "--", "foo") 27 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 28 | Expect(err).NotTo(HaveOccurred()) 29 | 30 | Eventually(session).Should(Exit(0)) 31 | Eventually(string(session.Err.Contents())).Should(Equal("")) 32 | 33 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 34 | pathToFile := LastLine(output) 35 | contents, err := os.ReadFile(pathToFile) 36 | Expect(err).NotTo(HaveOccurred()) 37 | 38 | Expect(string(contents)).To(Equal(LoadFixture("om_script.sh"))) 39 | }) 40 | }) 41 | 42 | When("run with parameters including JSON", func() { 43 | It("generates the correct script", func() { 44 | command := exec.Command(pathToPcf, "om", "-t", "fixtures/claim_manatee_response.json", "-f", "--", "configure-product", 45 | "--product-name", "p-rabbitmq", "--product-properties", 46 | `{".rabbitmq-server.server_admin_credentials":{"value":{"identity":"admin","password":"admin"}}}`) 47 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 48 | Expect(err).NotTo(HaveOccurred()) 49 | 50 | Eventually(session).Should(Exit(0)) 51 | Eventually(string(session.Err.Contents())).Should(Equal("")) 52 | 53 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 54 | pathToFile := LastLine(output) 55 | contents, err := os.ReadFile(pathToFile) 56 | Expect(err).NotTo(HaveOccurred()) 57 | 58 | Expect(string(contents)).To(Equal(LoadFixture("om_script_json.sh"))) 59 | }) 60 | }) 61 | 62 | When("run with no parameters", func() { 63 | It("generates the correct script", func() { 64 | command := exec.Command(pathToPcf, "om", "-t", "fixtures/claim_manatee_response.json") 65 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 66 | Expect(err).NotTo(HaveOccurred()) 67 | 68 | Eventually(session).Should(Exit(0)) 69 | Eventually(string(session.Err.Contents())).Should(Equal("")) 70 | Eventually(string(session.Out.Contents())).Should(Equal("export OM_TARGET='https://pcf.manatee.cf-app.com'\nexport OM_CLIENT_ID='fakeClientID'\nexport OM_CLIENT_SECRET='fakeClientSecret'\nexport OM_USERNAME='pivotalcf'\nexport OM_PASSWORD='fakePassword'\n")) 71 | }) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /integration/open_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os" 15 | "os/exec" 16 | "strings" 17 | 18 | . "github.com/onsi/ginkgo/v2" 19 | . "github.com/onsi/gomega" 20 | . "github.com/onsi/gomega/gbytes" 21 | . "github.com/onsi/gomega/gexec" 22 | ) 23 | 24 | var _ = Describe("Open", func() { 25 | It("generates the correct script", func() { 26 | command := exec.Command(pathToPcf, "open", "-t", "fixtures/claim_manatee_response.json", "-f") 27 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 28 | Expect(err).NotTo(HaveOccurred()) 29 | 30 | Eventually(session).Should(Exit(0)) 31 | Eventually(string(session.Err.Contents())).Should(Equal("")) 32 | Eventually(session.Out).Should(Say("Client ID is: fakeClientID")) 33 | Eventually(session.Out).Should(Say("Client Secret is in the clipboard")) 34 | 35 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 36 | pathToFile := LastLine(output) 37 | contents, err := os.ReadFile(pathToFile) 38 | Expect(err).NotTo(HaveOccurred()) 39 | 40 | Expect(string(contents)).To(Equal(LoadFixture("open_script.sh"))) 41 | }) 42 | 43 | When("run with the --show flag", func() { 44 | It("prints the credentials to the screen", func() { 45 | command := exec.Command(pathToPcf, "open", "-t", "fixtures/claim_manatee_response.json", "--show") 46 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 47 | Expect(err).NotTo(HaveOccurred()) 48 | 49 | Eventually(session).Should(Exit(0)) 50 | Eventually(string(session.Err.Contents())).Should(Equal("")) 51 | Eventually(session.Out).Should(Say("https://pcf.manatee.cf-app.com")) 52 | Eventually(session.Out).Should(Say("client id: fakeClientID")) 53 | Eventually(session.Out).Should(Say("client secret: fakeClientSecret")) 54 | }) 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /integration/pks_login_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os" 15 | "os/exec" 16 | "strings" 17 | 18 | . "github.com/onsi/ginkgo/v2" 19 | . "github.com/onsi/gomega" 20 | . "github.com/onsi/gomega/gbytes" 21 | . "github.com/onsi/gomega/gexec" 22 | ) 23 | 24 | var _ = Describe("pks", func() { 25 | It("generates the correct script", func() { 26 | command := exec.Command(pathToPcf, "pks-login", "-t", "fixtures/claim_manatee_response.json", "-f") 27 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 28 | Expect(err).NotTo(HaveOccurred()) 29 | 30 | Eventually(session).Should(Exit(0)) 31 | Eventually(string(session.Err.Contents())).Should(Equal("")) 32 | Eventually(session.Out).Should(Say("Logging in to PKS at: https://pcf.manatee.cf-app.com")) 33 | 34 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 35 | pathToFile := LastLine(output) 36 | contents, err := os.ReadFile(pathToFile) 37 | Expect(err).NotTo(HaveOccurred()) 38 | 39 | Expect(string(contents)).To(Equal(LoadFixture("pks_script.sh"))) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /integration/ssh_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os" 15 | "os/exec" 16 | "strings" 17 | 18 | . "github.com/onsi/ginkgo/v2" 19 | . "github.com/onsi/gomega" 20 | . "github.com/onsi/gomega/gbytes" 21 | . "github.com/onsi/gomega/gexec" 22 | ) 23 | 24 | var _ = Describe("SSH", func() { 25 | When("run with the opsman subcommand", func() { 26 | It("generates the correct script", func() { 27 | command := exec.Command(pathToPcf, "ssh", "opsman", "-t", "fixtures/claim_manatee_response.json", "-f") 28 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 29 | Expect(err).NotTo(HaveOccurred()) 30 | 31 | Eventually(session).Should(Exit(0)) 32 | Eventually(string(session.Err.Contents())).Should(Equal("")) 33 | Eventually(session.Out).Should(Say("Connecting to: manatee")) 34 | 35 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 36 | pathToFile := LastLine(output) 37 | contents, err := os.ReadFile(pathToFile) 38 | Expect(err).NotTo(HaveOccurred()) 39 | 40 | Expect(string(contents)).To(Equal(LoadFixture("ssh_opsman_script.sh"))) 41 | }) 42 | }) 43 | 44 | When("run with the director subcommand", func() { 45 | It("generates the correct script", func() { 46 | command := exec.Command(pathToPcf, "ssh", "director", "-t", "fixtures/claim_manatee_response.json", "-f") 47 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 48 | Expect(err).NotTo(HaveOccurred()) 49 | 50 | Eventually(session).Should(Exit(0)) 51 | Eventually(string(session.Err.Contents())).Should(Equal("")) 52 | Eventually(session.Out).Should(Say("Connecting to: manatee")) 53 | 54 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 55 | lines := strings.Split(output, "\n") 56 | pathToFile := lines[len(lines)-1] 57 | contents, err := os.ReadFile(pathToFile) 58 | Expect(err).NotTo(HaveOccurred()) 59 | 60 | Expect(string(contents)).To(Equal(LoadFixture("ssh_director_script.sh"))) 61 | }) 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /integration/sshuttle_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os" 15 | "os/exec" 16 | "strings" 17 | 18 | . "github.com/onsi/ginkgo/v2" 19 | . "github.com/onsi/gomega" 20 | . "github.com/onsi/gomega/gexec" 21 | ) 22 | 23 | var _ = Describe("sshuttle", func() { 24 | When("running shuttle", func() { 25 | It("generates the correct script", func() { 26 | command := exec.Command(pathToPcf, "sshuttle", "-t", "fixtures/claim_manatee_response.json", "-f") 27 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 28 | Expect(err).NotTo(HaveOccurred()) 29 | 30 | Eventually(session).Should(Exit(0)) 31 | Eventually(string(session.Err.Contents())).Should(Equal("")) 32 | 33 | output := strings.TrimSuffix(string(session.Out.Contents()), "\n") 34 | pathToFile := LastLine(output) 35 | contents, err := os.ReadFile(pathToFile) 36 | Expect(err).NotTo(HaveOccurred()) 37 | 38 | Expect(string(contents)).To(Equal(LoadFixture("sshuttle_script.sh"))) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /integration/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | "os" 15 | "path" 16 | "strings" 17 | 18 | . "github.com/onsi/gomega" 19 | ) 20 | 21 | func LoadFixture(name string) string { 22 | contents, err := os.ReadFile(path.Join("fixtures", name)) 23 | Expect(err).NotTo(HaveOccurred()) 24 | return string(contents) 25 | } 26 | 27 | func LastLine(output string) string { 28 | lines := strings.Split(output, "\n") 29 | return lines[len(lines)-1] 30 | } 31 | -------------------------------------------------------------------------------- /integration/version_command_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package integration 12 | 13 | import ( 14 | . "github.com/onsi/ginkgo/v2" 15 | . "github.com/onsi/gomega" 16 | . "github.com/onsi/gomega/gbytes" 17 | . "github.com/onsi/gomega/gexec" 18 | 19 | "os/exec" 20 | ) 21 | 22 | var _ = Describe("Version", func() { 23 | It("prints the version", func() { 24 | command := exec.Command(pathToPcf, "version") 25 | session, err := Start(command, GinkgoWriter, GinkgoWriter) 26 | Expect(err).NotTo(HaveOccurred()) 27 | 28 | Eventually(session).Should(Exit(0)) 29 | Eventually(string(session.Err.Contents())).Should(Equal("")) 30 | Eventually(session.Out).Should(Say("Version: development build \\(unknown date\\)")) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /om/om_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package om_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "OM Suite") 23 | } 24 | -------------------------------------------------------------------------------- /om/runner.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package om 12 | 13 | import ( 14 | "fmt" 15 | "strings" 16 | 17 | "github.com/pivotal/hammer/environment" 18 | "github.com/pivotal/hammer/scripting" 19 | ) 20 | 21 | type Runner struct { 22 | ScriptRunner scripting.ScriptRunner 23 | } 24 | 25 | func (r Runner) Run(data environment.Config, dryRun bool, omArgs ...string) error { 26 | var omCommandLines []string 27 | var omPrereqs []string 28 | 29 | if len(omArgs) > 0 { 30 | omCommandLines = []string{ 31 | fmt.Sprintf(`OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t '%s' -k %s`, 32 | data.OpsManager.ClientID, 33 | data.OpsManager.ClientSecret, 34 | data.OpsManager.Username, 35 | data.OpsManager.Password, 36 | data.OpsManager.URL.String(), 37 | quoteArgs(omArgs)), 38 | } 39 | omPrereqs = []string{"om"} 40 | } else { 41 | omCommandLines = []string{ 42 | fmt.Sprintf(`echo "export OM_TARGET='%s'"`, data.OpsManager.URL.String()), 43 | fmt.Sprintf(`echo "export OM_CLIENT_ID='%s'"`, data.OpsManager.ClientID), 44 | fmt.Sprintf(`echo "export OM_CLIENT_SECRET='%s'"`, data.OpsManager.ClientSecret), 45 | fmt.Sprintf(`echo "export OM_USERNAME='%s'"`, data.OpsManager.Username), 46 | fmt.Sprintf(`echo "export OM_PASSWORD='%s'"`, data.OpsManager.Password), 47 | } 48 | } 49 | 50 | return r.ScriptRunner.RunScript(omCommandLines, omPrereqs, dryRun) 51 | } 52 | 53 | func quoteArgs(args []string) string { 54 | quoted := make([]string, 0, len(args)) 55 | for _, arg := range args { 56 | quoted = append(quoted, fmt.Sprintf(`'%s'`, arg)) 57 | } 58 | return strings.Join(quoted, " ") 59 | } 60 | -------------------------------------------------------------------------------- /om/runner_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package om_test 12 | 13 | import ( 14 | "fmt" 15 | "net/url" 16 | 17 | "github.com/pivotal/hammer/om" 18 | 19 | . "github.com/onsi/ginkgo/v2" 20 | . "github.com/onsi/gomega" 21 | "github.com/pivotal/hammer/environment" 22 | "github.com/pivotal/hammer/scripting/scriptingfakes" 23 | ) 24 | 25 | var _ = Describe("om runner", func() { 26 | var ( 27 | err error 28 | omRunner om.Runner 29 | scriptRunner *scriptingfakes.FakeScriptRunner 30 | 31 | data environment.Config 32 | dryRun bool 33 | omArgs []string 34 | ) 35 | 36 | BeforeEach(func() { 37 | scriptRunner = new(scriptingfakes.FakeScriptRunner) 38 | 39 | url, _ := url.Parse("https://www.test-url.io") 40 | data = environment.Config{ 41 | OpsManager: environment.OpsManager{ 42 | URL: *url, 43 | Username: "username", 44 | Password: "password", 45 | ClientID: "client_id", 46 | ClientSecret: "client_secret", 47 | }, 48 | } 49 | 50 | omRunner = om.Runner{ 51 | ScriptRunner: scriptRunner, 52 | } 53 | }) 54 | 55 | JustBeforeEach(func() { 56 | err = omRunner.Run(data, dryRun, omArgs...) 57 | }) 58 | 59 | When("no om args are passed to the runner", func() { 60 | BeforeEach(func() { 61 | omArgs = []string{} 62 | }) 63 | 64 | It("runs the script with a series of om env var echos and no prerequisites", func() { 65 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 66 | 67 | lines, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) 68 | Expect(lines).To(Equal([]string{ 69 | `echo "export OM_TARGET='https://www.test-url.io'"`, 70 | `echo "export OM_CLIENT_ID='client_id'"`, 71 | `echo "export OM_CLIENT_SECRET='client_secret'"`, 72 | `echo "export OM_USERNAME='username'"`, 73 | `echo "export OM_PASSWORD='password'"`, 74 | })) 75 | Expect(prereqs).To(HaveLen(0)) 76 | }) 77 | 78 | It("doesn't error", func() { 79 | Expect(err).NotTo(HaveOccurred()) 80 | }) 81 | }) 82 | 83 | When("one or more om args are passed to the runner", func() { 84 | BeforeEach(func() { 85 | omArgs = []string{"arg1", "arg2", "arg3"} 86 | }) 87 | 88 | It("runs the script with an om command and specifying om as a prerequisite", func() { 89 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 90 | 91 | lines, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) 92 | Expect(lines).To(Equal([]string{ 93 | `OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t 'https://www.test-url.io' -k 'arg1' 'arg2' 'arg3'`, 94 | })) 95 | Expect(prereqs).To(ConsistOf("om")) 96 | }) 97 | 98 | It("doesn't error", func() { 99 | Expect(err).NotTo(HaveOccurred()) 100 | }) 101 | }) 102 | 103 | When("run with dry run set to false", func() { 104 | BeforeEach(func() { 105 | dryRun = false 106 | }) 107 | 108 | It("runs the script in dry run mode", func() { 109 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 110 | 111 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 112 | Expect(dryRun).To(Equal(false)) 113 | }) 114 | }) 115 | 116 | When("run with dry run set to true", func() { 117 | BeforeEach(func() { 118 | dryRun = true 119 | }) 120 | 121 | It("runs the script in dry run mode", func() { 122 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 123 | 124 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 125 | Expect(dryRun).To(Equal(true)) 126 | }) 127 | }) 128 | 129 | When("running the script errors", func() { 130 | BeforeEach(func() { 131 | scriptRunner.RunScriptReturns(fmt.Errorf("run-script-error")) 132 | }) 133 | 134 | It("propagates the error", func() { 135 | Expect(err).To(MatchError("run-script-error")) 136 | }) 137 | }) 138 | }) 139 | -------------------------------------------------------------------------------- /open/open_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package open_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "Open Suite") 23 | } 24 | -------------------------------------------------------------------------------- /open/runner.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package open 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/pivotal/hammer/scripting" 17 | 18 | "github.com/pivotal/hammer/environment" 19 | ) 20 | 21 | type Runner struct { 22 | ScriptRunner scripting.ScriptRunner 23 | } 24 | 25 | func (r Runner) Run(data environment.Config, dryRun bool, args ...string) error { 26 | openCommandLines := []string{fmt.Sprintf(`open "%s"`, data.OpsManager.URL.String())} 27 | 28 | if data.OpsManager.ClientID != "" { 29 | openCommandLines = append(openCommandLines, fmt.Sprintf(`echo "%s" | pbcopy`, data.OpsManager.ClientSecret)) 30 | } else { 31 | openCommandLines = append(openCommandLines, fmt.Sprintf(`echo "%s" | pbcopy`, data.OpsManager.Password)) 32 | } 33 | 34 | return r.ScriptRunner.RunScript(openCommandLines, []string{"open", "pbcopy"}, dryRun) 35 | } 36 | -------------------------------------------------------------------------------- /open/runner_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package open_test 12 | 13 | import ( 14 | "fmt" 15 | "net/url" 16 | 17 | "github.com/pivotal/hammer/open" 18 | 19 | . "github.com/onsi/ginkgo/v2" 20 | . "github.com/onsi/gomega" 21 | "github.com/pivotal/hammer/environment" 22 | "github.com/pivotal/hammer/scripting/scriptingfakes" 23 | ) 24 | 25 | var _ = Describe("open runner", func() { 26 | var ( 27 | err error 28 | openRunner open.Runner 29 | scriptRunner *scriptingfakes.FakeScriptRunner 30 | 31 | data environment.Config 32 | dryRun bool 33 | ) 34 | 35 | BeforeEach(func() { 36 | scriptRunner = new(scriptingfakes.FakeScriptRunner) 37 | 38 | url, _ := url.Parse("https://www.test-url.io") 39 | data = environment.Config{ 40 | OpsManager: environment.OpsManager{ 41 | URL: *url, 42 | Password: "password", 43 | }, 44 | } 45 | 46 | openRunner = open.Runner{ 47 | ScriptRunner: scriptRunner, 48 | } 49 | }) 50 | 51 | JustBeforeEach(func() { 52 | err = openRunner.Run(data, dryRun) 53 | }) 54 | 55 | It("runs the script with opsman url open and copying of the password into the clipboard", func() { 56 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 57 | 58 | lines, _, _ := scriptRunner.RunScriptArgsForCall(0) 59 | Expect(lines).To(Equal([]string{ 60 | `open "https://www.test-url.io"`, 61 | `echo "password" | pbcopy`, 62 | })) 63 | }) 64 | 65 | When("client credentials are specified", func() { 66 | BeforeEach(func() { 67 | url, _ := url.Parse("https://www.test-url.io") 68 | data = environment.Config{ 69 | OpsManager: environment.OpsManager{ 70 | URL: *url, 71 | Password: "password", 72 | ClientID: "client_id", 73 | ClientSecret: "client_secret", 74 | }, 75 | } 76 | }) 77 | 78 | It("runs the script with opsman url open and copying of the client secret into the clipboard", func() { 79 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 80 | 81 | lines, _, _ := scriptRunner.RunScriptArgsForCall(0) 82 | Expect(lines).To(Equal([]string{ 83 | `open "https://www.test-url.io"`, 84 | `echo "client_secret" | pbcopy`, 85 | })) 86 | }) 87 | }) 88 | 89 | It("specifies the appropriate prerequisites when running the script", func() { 90 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 91 | 92 | _, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) 93 | 94 | Expect(prereqs).To(ConsistOf("open", "pbcopy")) 95 | }) 96 | 97 | When("run with dry run set to false", func() { 98 | BeforeEach(func() { 99 | dryRun = false 100 | }) 101 | 102 | It("runs the script in dry run mode", func() { 103 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 104 | 105 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 106 | Expect(dryRun).To(Equal(false)) 107 | }) 108 | }) 109 | 110 | When("run with dry run set to true", func() { 111 | BeforeEach(func() { 112 | dryRun = true 113 | }) 114 | 115 | It("runs the script in dry run mode", func() { 116 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 117 | 118 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 119 | Expect(dryRun).To(Equal(true)) 120 | }) 121 | }) 122 | 123 | When("running the script succeeds", func() { 124 | It("doesn't error", func() { 125 | Expect(err).NotTo(HaveOccurred()) 126 | }) 127 | }) 128 | 129 | When("running the script errors", func() { 130 | BeforeEach(func() { 131 | scriptRunner.RunScriptReturns(fmt.Errorf("run-script-error")) 132 | }) 133 | 134 | It("propagates the error", func() { 135 | Expect(err).To(MatchError("run-script-error")) 136 | }) 137 | }) 138 | }) 139 | -------------------------------------------------------------------------------- /pks/login_runner.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package pks 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/pivotal/hammer/scripting" 17 | 18 | "github.com/pivotal/hammer/environment" 19 | ) 20 | 21 | type LoginRunner struct { 22 | ScriptRunner scripting.ScriptRunner 23 | } 24 | 25 | func (r LoginRunner) Run(data environment.Config, dryRun bool, args ...string) error { 26 | lines := []string{ 27 | fmt.Sprintf(`prods="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p /api/v0/staged/products)"`, 28 | data.OpsManager.ClientID, 29 | data.OpsManager.ClientSecret, 30 | data.OpsManager.Username, 31 | data.OpsManager.Password, 32 | data.OpsManager.URL.String()), 33 | `guid="$(echo "$prods" | jq -r '.[] | select(.type == "pivotal-container-service") | .guid')"`, 34 | fmt.Sprintf(`creds="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p /api/v0/deployed/products/"$guid"/credentials/.properties.uaa_admin_password)"`, 35 | data.OpsManager.ClientID, 36 | data.OpsManager.ClientSecret, 37 | data.OpsManager.Username, 38 | data.OpsManager.Password, 39 | data.OpsManager.URL.String()), 40 | `pass="$(echo "$creds" | jq -r .credential.value.secret)"`, 41 | fmt.Sprintf(`pks login -a %s -u admin -p "$pass" --skip-ssl-validation`, data.PKSApi.URL.String()), 42 | } 43 | 44 | return r.ScriptRunner.RunScript(lines, []string{"jq", "om", "pks"}, dryRun) 45 | } 46 | -------------------------------------------------------------------------------- /pks/login_runner_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package pks_test 12 | 13 | import ( 14 | "fmt" 15 | "net/url" 16 | 17 | "github.com/pivotal/hammer/pks" 18 | 19 | . "github.com/onsi/ginkgo/v2" 20 | . "github.com/onsi/gomega" 21 | "github.com/pivotal/hammer/environment" 22 | "github.com/pivotal/hammer/scripting/scriptingfakes" 23 | ) 24 | 25 | var _ = Describe("pks login runner", func() { 26 | var ( 27 | err error 28 | pksLoginRunner pks.LoginRunner 29 | scriptRunner *scriptingfakes.FakeScriptRunner 30 | 31 | data environment.Config 32 | dryRun bool 33 | ) 34 | 35 | BeforeEach(func() { 36 | scriptRunner = new(scriptingfakes.FakeScriptRunner) 37 | 38 | opsmanUrl, _ := url.Parse("https://www.test-url.io") 39 | pksApiUrl, _ := url.Parse("api.test-url.io") 40 | data = environment.Config{ 41 | OpsManager: environment.OpsManager{ 42 | Username: "username", 43 | Password: "password", 44 | ClientID: "client_id", 45 | ClientSecret: "client_secret", 46 | URL: *opsmanUrl, 47 | }, 48 | PKSApi: environment.PKSApi{ 49 | URL: *pksApiUrl, 50 | Username: "username", 51 | Password: "password", 52 | }, 53 | } 54 | 55 | pksLoginRunner = pks.LoginRunner{ 56 | ScriptRunner: scriptRunner, 57 | } 58 | }) 59 | 60 | JustBeforeEach(func() { 61 | err = pksLoginRunner.Run(data, dryRun) 62 | }) 63 | 64 | It("runs the script with a pks login", func() { 65 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 66 | 67 | lines, _, _ := scriptRunner.RunScriptArgsForCall(0) 68 | 69 | Expect(lines).To(Equal([]string{ 70 | `prods="$(OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t https://www.test-url.io -k curl -s -p /api/v0/staged/products)"`, 71 | `guid="$(echo "$prods" | jq -r '.[] | select(.type == "pivotal-container-service") | .guid')"`, 72 | `creds="$(OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t https://www.test-url.io -k curl -s -p /api/v0/deployed/products/"$guid"/credentials/.properties.uaa_admin_password)"`, 73 | `pass="$(echo "$creds" | jq -r .credential.value.secret)"`, 74 | `pks login -a api.test-url.io -u admin -p "$pass" --skip-ssl-validation`, 75 | })) 76 | }) 77 | 78 | It("specifies the appropriate prerequisites when running the script", func() { 79 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 80 | 81 | _, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) 82 | 83 | Expect(prereqs).To(ConsistOf("jq", "om", "pks")) 84 | }) 85 | 86 | When("run with dry run set to false", func() { 87 | BeforeEach(func() { 88 | dryRun = false 89 | }) 90 | 91 | It("runs the script in dry run mode", func() { 92 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 93 | 94 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 95 | Expect(dryRun).To(Equal(false)) 96 | }) 97 | }) 98 | 99 | When("run with dry run set to true", func() { 100 | BeforeEach(func() { 101 | dryRun = true 102 | }) 103 | 104 | It("runs the script in dry run mode", func() { 105 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 106 | 107 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 108 | Expect(dryRun).To(Equal(true)) 109 | }) 110 | }) 111 | 112 | When("running the script succeeds", func() { 113 | BeforeEach(func() { 114 | scriptRunner.RunScriptReturns(nil) 115 | }) 116 | 117 | It("doesn't error", func() { 118 | Expect(err).NotTo(HaveOccurred()) 119 | }) 120 | }) 121 | 122 | When("running the script errors", func() { 123 | BeforeEach(func() { 124 | scriptRunner.RunScriptReturns(fmt.Errorf("run-script-error")) 125 | }) 126 | 127 | It("propagates the error", func() { 128 | Expect(err).To(MatchError("run-script-error")) 129 | }) 130 | }) 131 | }) 132 | -------------------------------------------------------------------------------- /pks/pks_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package pks_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "PKS Suite") 23 | } 24 | -------------------------------------------------------------------------------- /scripting/prereqs.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package scripting 12 | 13 | import ( 14 | "fmt" 15 | "os/exec" 16 | ) 17 | 18 | func CheckPrereqs(prereqs []string) error { 19 | for _, v := range prereqs { 20 | _, err := exec.LookPath(v) 21 | if err != nil { 22 | return fmt.Errorf("Missing prerequisite '%s'. This must be installed first", v) 23 | } 24 | } 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /scripting/prereqs_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package scripting_test 12 | 13 | import ( 14 | . "github.com/onsi/ginkgo/v2" 15 | . "github.com/onsi/gomega" 16 | 17 | . "github.com/pivotal/hammer/scripting" 18 | ) 19 | 20 | var _ = Describe("CheckPrereqs", func() { 21 | It("finds ls", func() { 22 | err := CheckPrereqs([]string{"ls"}) 23 | Expect(err).NotTo(HaveOccurred()) 24 | }) 25 | 26 | It("finds multiple commands", func() { 27 | err := CheckPrereqs([]string{"ls", "cp"}) 28 | Expect(err).NotTo(HaveOccurred()) 29 | }) 30 | 31 | It("complains when a command is not found", func() { 32 | err := CheckPrereqs([]string{"ls", "does-not-exist-fahfdslakfhsklfhsdgf", "cp"}) 33 | Expect(err).To(MatchError("Missing prerequisite 'does-not-exist-fahfdslakfhsklfhsdgf'. This must be installed first")) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /scripting/print_file.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package scripting 12 | 13 | import ( 14 | "fmt" 15 | "os" 16 | ) 17 | 18 | func WriteTempFile(lines ...string) (string, error) { 19 | tempfile, err := os.CreateTemp("", "") 20 | if err != nil { 21 | return "", err 22 | } 23 | 24 | for _, l := range lines { 25 | if _, err = tempfile.Write([]byte(fmt.Sprintf("%s\n", l))); err != nil { 26 | return "", err 27 | } 28 | } 29 | 30 | if err = tempfile.Close(); err != nil { 31 | return "", err 32 | } 33 | 34 | return tempfile.Name(), nil 35 | } 36 | -------------------------------------------------------------------------------- /scripting/print_file_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package scripting_test 12 | 13 | import ( 14 | "os" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | 19 | . "github.com/pivotal/hammer/scripting" 20 | ) 21 | 22 | var _ = Describe("WriteTempFile", func() { 23 | It("returns an empty file if given no input lines", func() { 24 | path, err := WriteTempFile() 25 | Expect(err).NotTo(HaveOccurred()) 26 | defer os.Remove(path) 27 | 28 | contents, err := os.ReadFile(path) 29 | Expect(err).NotTo(HaveOccurred()) 30 | Expect(string(contents)).To(BeEmpty()) 31 | }) 32 | 33 | It("returns file containing input lines", func() { 34 | path, err := WriteTempFile("line1", "line2", "line3") 35 | Expect(err).NotTo(HaveOccurred()) 36 | defer os.Remove(path) 37 | 38 | contents, err := os.ReadFile(path) 39 | Expect(err).NotTo(HaveOccurred()) 40 | Expect(string(contents)).To(Equal("line1\nline2\nline3\n")) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /scripting/run_script.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package scripting 12 | 13 | import ( 14 | "fmt" 15 | "os" 16 | "os/exec" 17 | ) 18 | 19 | //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . ScriptRunner 20 | 21 | type ScriptRunner interface { 22 | RunScript(lines []string, prereqs []string, onlyWriteFile bool) error 23 | } 24 | 25 | type scriptRunner struct{} 26 | 27 | func NewScriptRunner() ScriptRunner { 28 | return scriptRunner{} 29 | } 30 | 31 | func (s scriptRunner) RunScript(lines []string, prereqs []string, onlyWriteFile bool) error { 32 | path, err := WriteTempFile(lines...) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | if onlyWriteFile { 38 | fmt.Println(path) 39 | return nil 40 | } 41 | 42 | if err := CheckPrereqs(prereqs); err != nil { 43 | return err 44 | } 45 | 46 | defer os.Remove(path) 47 | 48 | debug := "+x" 49 | if len(os.Getenv("DEBUG")) > 0 { 50 | debug = "-x" 51 | } 52 | 53 | command := exec.Command("/bin/bash", debug, path) 54 | command.Stdout = os.Stdout 55 | command.Stdin = os.Stdin 56 | command.Stderr = os.Stderr 57 | return command.Run() 58 | } 59 | -------------------------------------------------------------------------------- /scripting/scripting_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package scripting_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestScripting(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "Scripting Suite") 23 | } 24 | -------------------------------------------------------------------------------- /scripting/scriptingfakes/fake_script_runner.go: -------------------------------------------------------------------------------- 1 | // Code generated by counterfeiter. DO NOT EDIT. 2 | package scriptingfakes 3 | 4 | import ( 5 | "sync" 6 | 7 | "github.com/pivotal/hammer/scripting" 8 | ) 9 | 10 | type FakeScriptRunner struct { 11 | RunScriptStub func([]string, []string, bool) error 12 | runScriptMutex sync.RWMutex 13 | runScriptArgsForCall []struct { 14 | arg1 []string 15 | arg2 []string 16 | arg3 bool 17 | } 18 | runScriptReturns struct { 19 | result1 error 20 | } 21 | runScriptReturnsOnCall map[int]struct { 22 | result1 error 23 | } 24 | invocations map[string][][]interface{} 25 | invocationsMutex sync.RWMutex 26 | } 27 | 28 | func (fake *FakeScriptRunner) RunScript(arg1 []string, arg2 []string, arg3 bool) error { 29 | var arg1Copy []string 30 | if arg1 != nil { 31 | arg1Copy = make([]string, len(arg1)) 32 | copy(arg1Copy, arg1) 33 | } 34 | var arg2Copy []string 35 | if arg2 != nil { 36 | arg2Copy = make([]string, len(arg2)) 37 | copy(arg2Copy, arg2) 38 | } 39 | fake.runScriptMutex.Lock() 40 | ret, specificReturn := fake.runScriptReturnsOnCall[len(fake.runScriptArgsForCall)] 41 | fake.runScriptArgsForCall = append(fake.runScriptArgsForCall, struct { 42 | arg1 []string 43 | arg2 []string 44 | arg3 bool 45 | }{arg1Copy, arg2Copy, arg3}) 46 | stub := fake.RunScriptStub 47 | fakeReturns := fake.runScriptReturns 48 | fake.recordInvocation("RunScript", []interface{}{arg1Copy, arg2Copy, arg3}) 49 | fake.runScriptMutex.Unlock() 50 | if stub != nil { 51 | return stub(arg1, arg2, arg3) 52 | } 53 | if specificReturn { 54 | return ret.result1 55 | } 56 | return fakeReturns.result1 57 | } 58 | 59 | func (fake *FakeScriptRunner) RunScriptCallCount() int { 60 | fake.runScriptMutex.RLock() 61 | defer fake.runScriptMutex.RUnlock() 62 | return len(fake.runScriptArgsForCall) 63 | } 64 | 65 | func (fake *FakeScriptRunner) RunScriptCalls(stub func([]string, []string, bool) error) { 66 | fake.runScriptMutex.Lock() 67 | defer fake.runScriptMutex.Unlock() 68 | fake.RunScriptStub = stub 69 | } 70 | 71 | func (fake *FakeScriptRunner) RunScriptArgsForCall(i int) ([]string, []string, bool) { 72 | fake.runScriptMutex.RLock() 73 | defer fake.runScriptMutex.RUnlock() 74 | argsForCall := fake.runScriptArgsForCall[i] 75 | return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 76 | } 77 | 78 | func (fake *FakeScriptRunner) RunScriptReturns(result1 error) { 79 | fake.runScriptMutex.Lock() 80 | defer fake.runScriptMutex.Unlock() 81 | fake.RunScriptStub = nil 82 | fake.runScriptReturns = struct { 83 | result1 error 84 | }{result1} 85 | } 86 | 87 | func (fake *FakeScriptRunner) RunScriptReturnsOnCall(i int, result1 error) { 88 | fake.runScriptMutex.Lock() 89 | defer fake.runScriptMutex.Unlock() 90 | fake.RunScriptStub = nil 91 | if fake.runScriptReturnsOnCall == nil { 92 | fake.runScriptReturnsOnCall = make(map[int]struct { 93 | result1 error 94 | }) 95 | } 96 | fake.runScriptReturnsOnCall[i] = struct { 97 | result1 error 98 | }{result1} 99 | } 100 | 101 | func (fake *FakeScriptRunner) Invocations() map[string][][]interface{} { 102 | fake.invocationsMutex.RLock() 103 | defer fake.invocationsMutex.RUnlock() 104 | fake.runScriptMutex.RLock() 105 | defer fake.runScriptMutex.RUnlock() 106 | copiedInvocations := map[string][][]interface{}{} 107 | for key, value := range fake.invocations { 108 | copiedInvocations[key] = value 109 | } 110 | return copiedInvocations 111 | } 112 | 113 | func (fake *FakeScriptRunner) recordInvocation(key string, args []interface{}) { 114 | fake.invocationsMutex.Lock() 115 | defer fake.invocationsMutex.Unlock() 116 | if fake.invocations == nil { 117 | fake.invocations = map[string][][]interface{}{} 118 | } 119 | if fake.invocations[key] == nil { 120 | fake.invocations[key] = [][]interface{}{} 121 | } 122 | fake.invocations[key] = append(fake.invocations[key], args) 123 | } 124 | 125 | var _ scripting.ScriptRunner = new(FakeScriptRunner) 126 | -------------------------------------------------------------------------------- /ssh/director_runner.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package ssh 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/pivotal/hammer/environment" 17 | "github.com/pivotal/hammer/scripting" 18 | ) 19 | 20 | type DirectorRunner struct { 21 | ScriptRunner scripting.ScriptRunner 22 | } 23 | 24 | func (b DirectorRunner) Run(data environment.Config, dryRun bool, args ...string) error { 25 | boshCreds := "/api/v0/deployed/director/credentials/bosh_commandline_credentials" 26 | bbrCredsPath := "/api/v0/deployed/director/credentials/bbr_ssh_credentials" 27 | privateKeyPath := ".credential.value.private_key_pem" 28 | 29 | sshCommandLines := []string{ 30 | `ssh_key_path=$(mktemp temp.XXXXXX)`, 31 | fmt.Sprintf(`echo "%s" >"$ssh_key_path"`, data.OpsManager.PrivateKey), 32 | `chmod 0600 "${ssh_key_path}"`, 33 | 34 | fmt.Sprintf(`director_ssh_key="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p %s | jq -r %s)"`, 35 | data.OpsManager.ClientID, 36 | data.OpsManager.ClientSecret, 37 | data.OpsManager.Username, 38 | data.OpsManager.Password, 39 | data.OpsManager.URL.String(), 40 | bbrCredsPath, 41 | privateKeyPath), 42 | `director_ssh_key_path=$(mktemp)`, 43 | `echo -e "$director_ssh_key" > "$director_ssh_key_path"`, 44 | `chmod 0600 "${director_ssh_key_path}"`, 45 | 46 | fmt.Sprintf(`bosh_env="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p %s | grep -o "BOSH_ENVIRONMENT=\S*" | cut -f2 -d=)"`, 47 | data.OpsManager.ClientID, 48 | data.OpsManager.ClientSecret, 49 | data.OpsManager.Username, 50 | data.OpsManager.Password, 51 | data.OpsManager.URL.String(), 52 | boshCreds), 53 | 54 | `trap 'rm -f ${director_ssh_key_path}; rm -f ${ssh_key_path}' EXIT`, 55 | 56 | fmt.Sprintf(`jumpbox_cmd="%s@%s -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i ${ssh_key_path}"`, data.OpsManager.SshUser, data.OpsManager.IP.String()), 57 | `ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -J "$jumpbox_cmd" "bbr@${bosh_env}" -i "$director_ssh_key_path"`, 58 | } 59 | 60 | return b.ScriptRunner.RunScript(sshCommandLines, []string{"ssh", "om"}, dryRun) 61 | } 62 | -------------------------------------------------------------------------------- /ssh/director_runner_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package ssh_test 12 | 13 | import ( 14 | "fmt" 15 | "net" 16 | "net/url" 17 | 18 | "github.com/pivotal/hammer/ssh" 19 | 20 | . "github.com/onsi/ginkgo/v2" 21 | . "github.com/onsi/gomega" 22 | "github.com/pivotal/hammer/environment" 23 | "github.com/pivotal/hammer/scripting/scriptingfakes" 24 | ) 25 | 26 | var _ = Describe("director ssh runner", func() { 27 | var ( 28 | err error 29 | sshRunner ssh.DirectorRunner 30 | scriptRunner *scriptingfakes.FakeScriptRunner 31 | 32 | data environment.Config 33 | dryRun bool 34 | ) 35 | 36 | BeforeEach(func() { 37 | scriptRunner = new(scriptingfakes.FakeScriptRunner) 38 | 39 | url, _ := url.Parse("https://www.test-url.io") 40 | data = environment.Config{ 41 | OpsManager: environment.OpsManager{ 42 | SshUser: "ssh_user", 43 | PrivateKey: "private-key-contents", 44 | IP: net.ParseIP("10.0.0.6"), 45 | URL: *url, 46 | Username: "username", 47 | Password: "password", 48 | ClientID: "client_id", 49 | ClientSecret: "client_secret", 50 | }, 51 | } 52 | 53 | sshRunner = ssh.DirectorRunner{ 54 | ScriptRunner: scriptRunner, 55 | } 56 | }) 57 | 58 | JustBeforeEach(func() { 59 | err = sshRunner.Run(data, dryRun) 60 | }) 61 | 62 | It("runs the script with an ssh to the director vm", func() { 63 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 64 | 65 | lines, _, _ := scriptRunner.RunScriptArgsForCall(0) 66 | Expect(lines).To(Equal([]string{ 67 | `ssh_key_path=$(mktemp temp.XXXXXX)`, 68 | `echo "private-key-contents" >"$ssh_key_path"`, 69 | `chmod 0600 "${ssh_key_path}"`, 70 | `director_ssh_key="$(OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t https://www.test-url.io -k curl -s -p /api/v0/deployed/director/credentials/bbr_ssh_credentials | jq -r .credential.value.private_key_pem)"`, 71 | `director_ssh_key_path=$(mktemp)`, 72 | `echo -e "$director_ssh_key" > "$director_ssh_key_path"`, 73 | `chmod 0600 "${director_ssh_key_path}"`, 74 | `bosh_env="$(OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t https://www.test-url.io -k curl -s -p /api/v0/deployed/director/credentials/bosh_commandline_credentials | grep -o "BOSH_ENVIRONMENT=\S*" | cut -f2 -d=)"`, 75 | `trap 'rm -f ${director_ssh_key_path}; rm -f ${ssh_key_path}' EXIT`, 76 | `jumpbox_cmd="ssh_user@10.0.0.6 -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i ${ssh_key_path}"`, 77 | `ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -J "$jumpbox_cmd" "bbr@${bosh_env}" -i "$director_ssh_key_path"`, 78 | })) 79 | }) 80 | 81 | It("specifies the appropriate prerequisites when running the script", func() { 82 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 83 | 84 | _, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) 85 | 86 | Expect(prereqs).To(ConsistOf("ssh", "om")) 87 | }) 88 | 89 | When("run with dry run set to false", func() { 90 | BeforeEach(func() { 91 | dryRun = false 92 | }) 93 | 94 | It("runs the script in dry run mode", func() { 95 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 96 | 97 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 98 | Expect(dryRun).To(Equal(false)) 99 | }) 100 | }) 101 | 102 | When("run with dry run set to true", func() { 103 | BeforeEach(func() { 104 | dryRun = true 105 | }) 106 | 107 | It("runs the script in dry run mode", func() { 108 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 109 | 110 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 111 | Expect(dryRun).To(Equal(true)) 112 | }) 113 | }) 114 | 115 | When("running the script succeeds", func() { 116 | It("doesn't error", func() { 117 | Expect(err).NotTo(HaveOccurred()) 118 | }) 119 | }) 120 | 121 | When("running the script errors", func() { 122 | BeforeEach(func() { 123 | scriptRunner.RunScriptReturns(fmt.Errorf("run-script-error")) 124 | }) 125 | 126 | It("propagates the error", func() { 127 | Expect(err).To(MatchError("run-script-error")) 128 | }) 129 | }) 130 | }) 131 | -------------------------------------------------------------------------------- /ssh/opsmanager_runner.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package ssh 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/pivotal/hammer/environment" 17 | "github.com/pivotal/hammer/scripting" 18 | ) 19 | 20 | type OpsManagerRunner struct { 21 | ScriptRunner scripting.ScriptRunner 22 | } 23 | 24 | func (b OpsManagerRunner) Run(data environment.Config, dryRun bool, args ...string) error { 25 | sshCommand := fmt.Sprintf(`ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i "${ssh_key_path}" -t %s@"%s"`, data.OpsManager.SshUser, data.OpsManager.IP.String()) 26 | 27 | sshCommandLines := []string{ 28 | `ssh_key_path=$(mktemp)`, 29 | fmt.Sprintf(`echo "%s" >"$ssh_key_path"`, data.OpsManager.PrivateKey), 30 | `trap 'rm -f ${ssh_key_path}' EXIT`, 31 | `chmod 0600 "${ssh_key_path}"`, 32 | fmt.Sprintf(`creds="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p %s)"`, 33 | data.OpsManager.ClientID, 34 | data.OpsManager.ClientSecret, 35 | data.OpsManager.Username, 36 | data.OpsManager.Password, 37 | data.OpsManager.URL.String(), 38 | "/api/v0/deployed/director/credentials/bosh_commandline_credentials"), 39 | `bosh="$(echo "$creds" | jq -r .credential | tr ' ' '\n' | grep '=')"`, 40 | `echo "$bosh"`, 41 | `shell="/usr/bin/env $(echo "$bosh" | tr '\n' ' ') bash -l"`, 42 | fmt.Sprintf(`%s "$shell"`, sshCommand), 43 | } 44 | 45 | return b.ScriptRunner.RunScript(sshCommandLines, []string{"ssh", "om"}, dryRun) 46 | } 47 | -------------------------------------------------------------------------------- /ssh/opsmanager_runner_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package ssh_test 12 | 13 | import ( 14 | "fmt" 15 | "net" 16 | "net/url" 17 | 18 | "github.com/pivotal/hammer/ssh" 19 | 20 | . "github.com/onsi/ginkgo/v2" 21 | . "github.com/onsi/gomega" 22 | "github.com/pivotal/hammer/environment" 23 | "github.com/pivotal/hammer/scripting/scriptingfakes" 24 | ) 25 | 26 | var _ = Describe("ops manager ssh runner", func() { 27 | var ( 28 | err error 29 | sshRunner ssh.OpsManagerRunner 30 | scriptRunner *scriptingfakes.FakeScriptRunner 31 | 32 | data environment.Config 33 | dryRun bool 34 | ) 35 | 36 | BeforeEach(func() { 37 | scriptRunner = new(scriptingfakes.FakeScriptRunner) 38 | 39 | url, _ := url.Parse("https://www.test-url.io") 40 | data = environment.Config{ 41 | OpsManager: environment.OpsManager{ 42 | SshUser: "ssh_user", 43 | PrivateKey: "private-key-contents", 44 | IP: net.ParseIP("10.0.0.6"), 45 | URL: *url, 46 | Username: "username", 47 | Password: "password", 48 | ClientID: "client_id", 49 | ClientSecret: "client_secret", 50 | }, 51 | } 52 | 53 | sshRunner = ssh.OpsManagerRunner{ 54 | ScriptRunner: scriptRunner, 55 | } 56 | }) 57 | 58 | JustBeforeEach(func() { 59 | err = sshRunner.Run(data, dryRun) 60 | }) 61 | 62 | It("runs the script with an ssh to the opsman vm", func() { 63 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 64 | 65 | lines, _, _ := scriptRunner.RunScriptArgsForCall(0) 66 | Expect(lines).To(Equal([]string{ 67 | `ssh_key_path=$(mktemp)`, 68 | `echo "private-key-contents" >"$ssh_key_path"`, 69 | `trap 'rm -f ${ssh_key_path}' EXIT`, 70 | `chmod 0600 "${ssh_key_path}"`, 71 | `creds="$(OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t https://www.test-url.io -k curl -s -p /api/v0/deployed/director/credentials/bosh_commandline_credentials)"`, 72 | `bosh="$(echo "$creds" | jq -r .credential | tr ' ' '\n' | grep '=')"`, 73 | `echo "$bosh"`, 74 | `shell="/usr/bin/env $(echo "$bosh" | tr '\n' ' ') bash -l"`, 75 | `ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i "${ssh_key_path}" -t ssh_user@"10.0.0.6" "$shell"`, 76 | })) 77 | }) 78 | 79 | It("specifies the appropriate prerequisites when running the script", func() { 80 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 81 | 82 | _, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) 83 | 84 | Expect(prereqs).To(ConsistOf("ssh", "om")) 85 | }) 86 | 87 | When("run with dry run set to false", func() { 88 | BeforeEach(func() { 89 | dryRun = false 90 | }) 91 | 92 | It("runs the script in dry run mode", func() { 93 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 94 | 95 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 96 | Expect(dryRun).To(Equal(false)) 97 | }) 98 | }) 99 | 100 | When("run with dry run set to true", func() { 101 | BeforeEach(func() { 102 | dryRun = true 103 | }) 104 | 105 | It("runs the script in dry run mode", func() { 106 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 107 | 108 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 109 | Expect(dryRun).To(Equal(true)) 110 | }) 111 | }) 112 | 113 | When("running the script succeeds", func() { 114 | It("doesn't error", func() { 115 | Expect(err).NotTo(HaveOccurred()) 116 | }) 117 | }) 118 | 119 | When("running the script errors", func() { 120 | BeforeEach(func() { 121 | scriptRunner.RunScriptReturns(fmt.Errorf("run-script-error")) 122 | }) 123 | 124 | It("propagates the error", func() { 125 | Expect(err).To(MatchError("run-script-error")) 126 | }) 127 | }) 128 | }) 129 | -------------------------------------------------------------------------------- /ssh/ssh_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package ssh_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "SSH Suite") 23 | } 24 | -------------------------------------------------------------------------------- /sshuttle/runner.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package sshuttle 12 | 13 | import ( 14 | "fmt" 15 | 16 | "github.com/pivotal/hammer/environment" 17 | "github.com/pivotal/hammer/scripting" 18 | ) 19 | 20 | type Runner struct { 21 | ScriptRunner scripting.ScriptRunner 22 | } 23 | 24 | func (b Runner) Run(data environment.Config, dryRun bool, args ...string) error { 25 | networksPath := "/api/v0/staged/director/networks" 26 | cidrPath := ".networks[].subnets[].cidr" 27 | 28 | sshuttleCommandLines := []string{ 29 | `ssh_key_path=$(mktemp)`, 30 | fmt.Sprintf(`echo "%s" >"$ssh_key_path"`, data.OpsManager.PrivateKey), 31 | `trap 'rm -f ${ssh_key_path}' EXIT`, 32 | `chmod 0600 "${ssh_key_path}"`, 33 | fmt.Sprintf(`cidrs="$(OM_CLIENT_ID='%s' OM_CLIENT_SECRET='%s' OM_USERNAME='%s' OM_PASSWORD='%s' om -t %s -k curl -s -p %s | jq -r %s | xargs echo)"`, 34 | data.OpsManager.ClientID, 35 | data.OpsManager.ClientSecret, 36 | data.OpsManager.Username, 37 | data.OpsManager.Password, 38 | data.OpsManager.URL.String(), 39 | networksPath, 40 | cidrPath), 41 | 42 | fmt.Sprintf(`sshuttle --ssh-cmd "ssh -o IdentitiesOnly=yes -i ${ssh_key_path}" -r %s@"%s" "${cidrs}"`, 43 | data.OpsManager.SshUser, 44 | data.OpsManager.IP.String()), 45 | } 46 | 47 | return b.ScriptRunner.RunScript(sshuttleCommandLines, []string{"jq", "om", "sshuttle"}, dryRun) 48 | } 49 | -------------------------------------------------------------------------------- /sshuttle/runner_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package sshuttle_test 12 | 13 | import ( 14 | "fmt" 15 | "net" 16 | "net/url" 17 | 18 | "github.com/pivotal/hammer/sshuttle" 19 | 20 | . "github.com/onsi/ginkgo/v2" 21 | . "github.com/onsi/gomega" 22 | "github.com/pivotal/hammer/environment" 23 | "github.com/pivotal/hammer/scripting/scriptingfakes" 24 | ) 25 | 26 | var _ = Describe("sshuttle runner", func() { 27 | var ( 28 | err error 29 | sshuttleRunner sshuttle.Runner 30 | scriptRunner *scriptingfakes.FakeScriptRunner 31 | 32 | data environment.Config 33 | dryRun bool 34 | ) 35 | 36 | BeforeEach(func() { 37 | scriptRunner = new(scriptingfakes.FakeScriptRunner) 38 | 39 | url, _ := url.Parse("https://www.test-url.io") 40 | data = environment.Config{ 41 | OpsManager: environment.OpsManager{ 42 | SshUser: "ssh_user", 43 | PrivateKey: "private-key-contents", 44 | IP: net.ParseIP("10.0.0.6"), 45 | URL: *url, 46 | Username: "username", 47 | Password: "password", 48 | ClientID: "client_id", 49 | ClientSecret: "client_secret", 50 | }, 51 | } 52 | 53 | sshuttleRunner = sshuttle.Runner{ 54 | ScriptRunner: scriptRunner, 55 | } 56 | }) 57 | 58 | JustBeforeEach(func() { 59 | err = sshuttleRunner.Run(data, dryRun) 60 | }) 61 | 62 | It("runs the script with a sshuttle to the opsman vm", func() { 63 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 64 | 65 | lines, _, _ := scriptRunner.RunScriptArgsForCall(0) 66 | Expect(lines).To(Equal([]string{ 67 | `ssh_key_path=$(mktemp)`, 68 | `echo "private-key-contents" >"$ssh_key_path"`, 69 | `trap 'rm -f ${ssh_key_path}' EXIT`, 70 | `chmod 0600 "${ssh_key_path}"`, 71 | `cidrs="$(OM_CLIENT_ID='client_id' OM_CLIENT_SECRET='client_secret' OM_USERNAME='username' OM_PASSWORD='password' om -t https://www.test-url.io -k curl -s -p /api/v0/staged/director/networks | jq -r .networks[].subnets[].cidr | xargs echo)"`, 72 | `sshuttle --ssh-cmd "ssh -o IdentitiesOnly=yes -i ${ssh_key_path}" -r ssh_user@"10.0.0.6" "${cidrs}"`, 73 | })) 74 | }) 75 | 76 | It("specifies the appropriate prerequisites when running the script", func() { 77 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 78 | 79 | _, prereqs, _ := scriptRunner.RunScriptArgsForCall(0) 80 | 81 | Expect(prereqs).To(ConsistOf("jq", "om", "sshuttle")) 82 | }) 83 | 84 | When("run with dry run set to false", func() { 85 | BeforeEach(func() { 86 | dryRun = false 87 | }) 88 | 89 | It("runs the script in dry run mode", func() { 90 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 91 | 92 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 93 | Expect(dryRun).To(Equal(false)) 94 | }) 95 | }) 96 | 97 | When("run with dry run set to true", func() { 98 | BeforeEach(func() { 99 | dryRun = true 100 | }) 101 | 102 | It("runs the script in dry run mode", func() { 103 | Expect(scriptRunner.RunScriptCallCount()).To(Equal(1)) 104 | 105 | _, _, dryRun := scriptRunner.RunScriptArgsForCall(0) 106 | Expect(dryRun).To(Equal(true)) 107 | }) 108 | }) 109 | 110 | When("running the script succeeds", func() { 111 | It("doesn't error", func() { 112 | Expect(err).NotTo(HaveOccurred()) 113 | }) 114 | }) 115 | 116 | When("running the script errors", func() { 117 | BeforeEach(func() { 118 | scriptRunner.RunScriptReturns(fmt.Errorf("run-script-error")) 119 | }) 120 | 121 | It("propagates the error", func() { 122 | Expect(err).To(MatchError("run-script-error")) 123 | }) 124 | }) 125 | }) 126 | -------------------------------------------------------------------------------- /sshuttle/sshuttle_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package sshuttle_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "Sshuttle Suite") 23 | } 24 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/maxbrunsfeld/counterfeiter/v6" 7 | ) 8 | 9 | // This file imports packages that are used when running go generate, or used 10 | // during the development process but not otherwise depended on by built code. 11 | -------------------------------------------------------------------------------- /ui/ui.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package ui 12 | 13 | import ( 14 | "fmt" 15 | "io" 16 | ) 17 | 18 | type UI struct { 19 | Out io.Writer 20 | Err io.Writer 21 | } 22 | 23 | func (ui *UI) DisplayText(text string) { 24 | fmt.Fprint(ui.Out, text) 25 | } 26 | 27 | func (ui *UI) DisplayError(err error) { 28 | fmt.Fprint(ui.Err, err.Error()) 29 | } 30 | -------------------------------------------------------------------------------- /ui/ui_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package ui_test 12 | 13 | import ( 14 | "testing" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | ) 19 | 20 | func TestCommand(t *testing.T) { 21 | RegisterFailHandler(Fail) 22 | RunSpecs(t, "UI Suite") 23 | } 24 | -------------------------------------------------------------------------------- /ui/ui_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019-Present Pivotal Software, Inc. All rights reserved. 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | package ui_test 12 | 13 | import ( 14 | "fmt" 15 | 16 | . "github.com/onsi/ginkgo/v2" 17 | . "github.com/onsi/gomega" 18 | "github.com/onsi/gomega/gbytes" 19 | 20 | . "github.com/pivotal/hammer/ui" 21 | ) 22 | 23 | var _ = Describe("ui", func() { 24 | var ui UI 25 | 26 | BeforeEach(func() { 27 | ui = UI{ 28 | Out: gbytes.NewBuffer(), 29 | Err: gbytes.NewBuffer(), 30 | } 31 | }) 32 | 33 | Context("DisplayText", func() { 34 | It("prints text to the out buffer", func() { 35 | ui.DisplayText("Test text output") 36 | Expect(ui.Out).To(gbytes.Say("Test text output")) 37 | }) 38 | }) 39 | 40 | Context("DisplayError", func() { 41 | It("prints error text to the err buffer", func() { 42 | ui.DisplayError(fmt.Errorf("test error contents")) 43 | Expect(ui.Err).To(gbytes.Say("test error contents")) 44 | }) 45 | }) 46 | }) 47 | --------------------------------------------------------------------------------