Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add force-latest-variables flag to apply-changes command #608

Merged
merged 2 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion acceptance/apply_changes_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package acceptance

import (
"github.com/onsi/gomega/ghttp"
"net/http"
"os/exec"

"github.com/onsi/gomega/ghttp"

"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"

Expand Down Expand Up @@ -49,6 +50,7 @@ var _ = Describe("apply-changes command", func() {
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{
"ignore_warnings": "false",
"force_latest_variables": false,
"deploy_products": "all"
}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id": 42}}`),
Expand Down Expand Up @@ -130,6 +132,7 @@ var _ = Describe("apply-changes command", func() {
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{
"ignore_warnings": "false",
"force_latest_variables": false,
"deploy_products": "all"
}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id": 42}}`),
Expand Down
16 changes: 9 additions & 7 deletions api/installations_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (a Api) ListInstallations() ([]InstallationsServiceOutput, error) {
return responseStruct.Installations, nil
}

func (a Api) CreateInstallation(ignoreWarnings bool, deployProducts bool, productNames []string, errands ApplyErrandChanges) (InstallationsServiceOutput, error) {
func (a Api) CreateInstallation(ignoreWarnings bool, deployProducts bool, forceLatestVariables bool, productNames []string, errands ApplyErrandChanges) (InstallationsServiceOutput, error) {
productGuidMapping, err := a.fetchProductGUID()
if err != nil {
return InstallationsServiceOutput{}, fmt.Errorf("failed to list staged and/or deployed products: %w", err)
Expand Down Expand Up @@ -110,13 +110,15 @@ func (a Api) CreateInstallation(ignoreWarnings bool, deployProducts bool, produc
}

data, err := json.Marshal(&struct {
IgnoreWarnings string `json:"ignore_warnings"`
DeployProducts interface{} `json:"deploy_products"`
Errands map[string]ProductErrand `json:"errands,omitempty"`
IgnoreWarnings string `json:"ignore_warnings"`
ForceLatestVariables bool `json:"force_latest_variables"`
DeployProducts interface{} `json:"deploy_products"`
Errands map[string]ProductErrand `json:"errands,omitempty"`
}{
IgnoreWarnings: fmt.Sprintf("%t", ignoreWarnings),
DeployProducts: deployProductsVal,
Errands: errandsPayload,
IgnoreWarnings: fmt.Sprintf("%t", ignoreWarnings),
ForceLatestVariables: forceLatestVariables,
DeployProducts: deployProductsVal,
Errands: errandsPayload,
})
if err != nil {
return InstallationsServiceOutput{}, err
Expand Down
62 changes: 36 additions & 26 deletions api/installations_service_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package api_test

import (
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/ghttp"
"log"
"net/http"
"time"

"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/ghttp"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

"github.com/pivotal-cf/om/api"
)

Expand Down Expand Up @@ -108,12 +110,12 @@ var _ = Describe("InstallationsService", func() {
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings":"false", "deploy_products":"all"}`),
ghttp.VerifyJSON(`{"ignore_warnings":"false","force_latest_variables":false, "deploy_products":"all"}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id":1}}`),
),
)

output, err := service.CreateInstallation(false, true, nil, api.ApplyErrandChanges{})
output, err := service.CreateInstallation(false, true, false, nil, api.ApplyErrandChanges{})

Expect(err).ToNot(HaveOccurred())
Expect(output.ID).To(Equal(1))
Expand All @@ -133,12 +135,12 @@ var _ = Describe("InstallationsService", func() {
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings":"false", "deploy_products":"none"}`),
ghttp.VerifyJSON(`{"ignore_warnings":"false","force_latest_variables":false, "deploy_products":"none"}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id":1}}`),
),
)

output, err := service.CreateInstallation(false, false, nil, api.ApplyErrandChanges{})
output, err := service.CreateInstallation(false, false, false, nil, api.ApplyErrandChanges{})

Expect(err).ToNot(HaveOccurred())
Expect(output.ID).To(Equal(1))
Expand All @@ -158,30 +160,38 @@ var _ = Describe("InstallationsService", func() {
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings":"false","deploy_products":["guid2"]}`),
ghttp.VerifyJSON(`{"ignore_warnings":"false","force_latest_variables":false,"deploy_products":["guid2"]}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id":1}}`),
),
)

output, err := service.CreateInstallation(false, true, []string{"product2"}, api.ApplyErrandChanges{})
output, err := service.CreateInstallation(false, true, false, []string{"product2"}, api.ApplyErrandChanges{})
Expect(err).ToNot(HaveOccurred())
Expect(output.ID).To(Equal(1))
})
})

It("errors when the product does not exist", func() {
When("forcing latest variables", func() {
It("triggers an installation on an Ops Manager, forcing latest variables", func() {
client.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/api/v0/staged/products"),
ghttp.RespondWith(http.StatusOK, `[{"guid": "guid1", "type": "product1"}]`),
ghttp.RespondWith(http.StatusOK, `[{"guid": "guid1", "type": "product1"}, {"guid": "guid2", "type": "product2"}]`),
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/api/v0/deployed/products"),
ghttp.RespondWith(http.StatusOK, `[{"guid": "guid1", "type": "product1"}]`),
ghttp.RespondWith(http.StatusOK, `[{"guid": "guid1", "type": "product1"}, {"guid": "guid2", "type": "product2"}]`),
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings":"false","force_latest_variables":true,"deploy_products":["guid2"]}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id":1}}`),
),
)

_, err := service.CreateInstallation(false, true, []string{"product2"}, api.ApplyErrandChanges{})
Expect(err).To(HaveOccurred())
output, err := service.CreateInstallation(false, true, true, []string{"product2"}, api.ApplyErrandChanges{})
Expect(err).ToNot(HaveOccurred())
Expect(output.ID).To(Equal(1))
})
})

Expand All @@ -199,12 +209,12 @@ var _ = Describe("InstallationsService", func() {
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings": "false", "deploy_products": ["guid1"], "errands": {"guid1": {"run_post_deploy": {"errand1": "default"}}}}`),
ghttp.VerifyJSON(`{"ignore_warnings": "false", "force_latest_variables": false, "deploy_products": ["guid1"], "errands": {"guid1": {"run_post_deploy": {"errand1": "default"}}}}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id":1}}`),
),
)

output, err := service.CreateInstallation(false, true, []string{"product1"}, api.ApplyErrandChanges{
output, err := service.CreateInstallation(false, true, false, []string{"product1"}, api.ApplyErrandChanges{
Errands: map[string]api.ProductErrand{
"product1": {
RunPostDeploy: map[string]interface{}{
Expand All @@ -229,12 +239,12 @@ var _ = Describe("InstallationsService", func() {
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings": "false", "deploy_products": ["guid2"]}`),
ghttp.VerifyJSON(`{"ignore_warnings": "false", "force_latest_variables": false, "deploy_products": ["guid2"]}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id":1}}`),
),
)

_, err := service.CreateInstallation(false, true, []string{"product2"}, api.ApplyErrandChanges{
_, err := service.CreateInstallation(false, true, false, []string{"product2"}, api.ApplyErrandChanges{
Errands: map[string]api.ProductErrand{
"product3": {
RunPostDeploy: map[string]interface{}{
Expand All @@ -261,12 +271,12 @@ var _ = Describe("InstallationsService", func() {
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings": "false", "deploy_products": "all", "errands": {"guid1": {"run_post_deploy": {"errand1": "default"}}}}`),
ghttp.VerifyJSON(`{"ignore_warnings": "false", "force_latest_variables": false, "deploy_products": "all", "errands": {"guid1": {"run_post_deploy": {"errand1": "default"}}}}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id":1}}`),
),
)

output, err := service.CreateInstallation(false, true, []string{}, api.ApplyErrandChanges{
output, err := service.CreateInstallation(false, true, false, []string{}, api.ApplyErrandChanges{
Errands: map[string]api.ProductErrand{
"product1": {
RunPostDeploy: map[string]interface{}{
Expand All @@ -291,12 +301,12 @@ var _ = Describe("InstallationsService", func() {
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings": "false", "deploy_products": ["guid2"], "errands": {"guid1": {"run_post_deploy": {"errand1": "default"}}}}`),
ghttp.VerifyJSON(`{"ignore_warnings": "false", "force_latest_variables": false, "deploy_products": ["guid2"], "errands": {"guid1": {"run_post_deploy": {"errand1": "default"}}}}`),
ghttp.RespondWith(http.StatusOK, `{"install": {"id":1}}`),
),
)

_, err := service.CreateInstallation(false, true, []string{}, api.ApplyErrandChanges{
_, err := service.CreateInstallation(false, true, false, []string{}, api.ApplyErrandChanges{
Errands: map[string]api.ProductErrand{
"product1": {
RunPostDeploy: map[string]interface{}{
Expand Down Expand Up @@ -330,7 +340,7 @@ var _ = Describe("InstallationsService", func() {
),
)

_, err := service.CreateInstallation(false, true, nil, api.ApplyErrandChanges{})
_, err := service.CreateInstallation(false, true, false, nil, api.ApplyErrandChanges{})
Expect(err).To(MatchError(ContainSubstring("could not make api request to installations endpoint: could not send api request to POST /api/v0/installations")))
})
})
Expand All @@ -348,7 +358,7 @@ var _ = Describe("InstallationsService", func() {
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v0/installations"),
ghttp.VerifyJSON(`{"ignore_warnings":"false","deploy_products":["guid2"]}`),
ghttp.VerifyJSON(`{"ignore_warnings":"false","force_latest_variables":false,"deploy_products":["guid2"]}`),
ghttp.RespondWith(http.StatusUnprocessableEntity, `{
"errors": ["'Some IAAS Error', type: SomeVerifier""],
"deployment_errors": {
Expand All @@ -365,7 +375,7 @@ var _ = Describe("InstallationsService", func() {
),
)

_, err := service.CreateInstallation(false, true, []string{"product2"}, api.ApplyErrandChanges{})
_, err := service.CreateInstallation(false, true, false, []string{"product2"}, api.ApplyErrandChanges{})
Expect(err).To(MatchError(ContainSubstring("request failed: unexpected response")))
Expect(err).To(MatchError(ContainSubstring("Tip: In Ops Manager 2.6 or newer, you can use `om pre-deploy-check` to get a complete list of failed verifiers and om commands to disable them.")))
})
Expand All @@ -380,7 +390,7 @@ var _ = Describe("InstallationsService", func() {
),
)

_, err := service.CreateInstallation(false, true, nil, api.ApplyErrandChanges{})
_, err := service.CreateInstallation(false, true, false, nil, api.ApplyErrandChanges{})
Expect(err).To(MatchError(ContainSubstring("request failed: unexpected response")))
})
})
Expand All @@ -402,7 +412,7 @@ var _ = Describe("InstallationsService", func() {
),
)

_, err := service.CreateInstallation(false, true, nil, api.ApplyErrandChanges{})
_, err := service.CreateInstallation(false, true, false, nil, api.ApplyErrandChanges{})
Expect(err).To(MatchError(ContainSubstring("failed to decode response: invalid character")))
})
})
Expand Down
17 changes: 9 additions & 8 deletions commands/apply_changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@ type ApplyChanges struct {
logWriter logWriter
waitDuration time.Duration
Options struct {
Config string `short:"c" long:"config" description:"path to yml file containing errand configuration (see docs/apply-changes/README.md for format)"`
IgnoreWarnings bool `short:"i" long:"ignore-warnings" description:"For convenience. Use other commands to disable particular verifiers if they are inappropriate."`
Reattach bool `long:"reattach" description:"reattach to an already running apply changes (if available)"`
RecreateVMs bool `long:"recreate-vms" description:"recreate all vms"`
SkipDeployProducts bool `short:"s" long:"skip-deploy-products" description:"skip deploying products when applying changes - just update the director"`
ProductNames []string `short:"n" long:"product-name" description:"name of the product(s) to deploy, cannot be used in conjunction with --skip-deploy-products (OM 2.2+)"`
Config string `short:"c" long:"config" description:"path to yml file containing errand configuration (see docs/apply-changes/README.md for format)"`
IgnoreWarnings bool `short:"i" long:"ignore-warnings" description:"For convenience. Use other commands to disable particular verifiers if they are inappropriate."`
Reattach bool `long:"reattach" description:"reattach to an already running apply changes (if available)"`
RecreateVMs bool `long:"recreate-vms" description:"recreate all vms"`
SkipDeployProducts bool `short:"s" long:"skip-deploy-products" description:"skip deploying products when applying changes - just update the director"`
ForceLatestVariables bool `long:"force-latest-variables" description:"force any certificates or other BOSH variables to use their latest version even when a stemcell is not being upgraded"`
ProductNames []string `short:"n" long:"product-name" description:"name of the product(s) to deploy, cannot be used in conjunction with --skip-deploy-products (OM 2.2+)"`
}
}

//counterfeiter:generate -o ./fakes/apply_changes_service.go --fake-name ApplyChangesService . applyChangesService
type applyChangesService interface {
CreateInstallation(bool, bool, []string, api.ApplyErrandChanges) (api.InstallationsServiceOutput, error)
CreateInstallation(bool, bool, bool, []string, api.ApplyErrandChanges) (api.InstallationsServiceOutput, error)
GetInstallation(id int) (api.InstallationsServiceOutput, error)
GetInstallationLogs(id int) (api.InstallationsServiceOutput, error)
Info() (api.Info, error)
Expand Down Expand Up @@ -158,7 +159,7 @@ func (ac ApplyChanges) Execute(args []string) error {
}

ac.logger.Printf("attempting to apply changes to the targeted Ops Manager")
installation, err = ac.service.CreateInstallation(ac.Options.IgnoreWarnings, !ac.Options.SkipDeployProducts, changedProducts, errands)
installation, err = ac.service.CreateInstallation(ac.Options.IgnoreWarnings, !ac.Options.SkipDeployProducts, ac.Options.ForceLatestVariables, changedProducts, errands)
if err != nil {
return fmt.Errorf("installation failed to trigger: %s", err)
}
Expand Down
33 changes: 25 additions & 8 deletions commands/apply_changes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package commands_test
import (
"errors"
"fmt"
"github.com/onsi/gomega/gbytes"
"gopkg.in/yaml.v2"
"io/ioutil"
"log"
"os"
"regexp"
"time"

"github.com/onsi/gomega/gbytes"
"gopkg.in/yaml.v2"

"github.com/pivotal-cf/om/api"
"github.com/pivotal-cf/om/commands"
"github.com/pivotal-cf/om/commands/fakes"
Expand Down Expand Up @@ -60,9 +61,10 @@ var _ = Describe("ApplyChanges", func() {

Expect(service.CreateInstallationCallCount()).To(Equal(1))

ignoreWarnings, deployProducts, _, _ := service.CreateInstallationArgsForCall(0)
ignoreWarnings, deployProducts, forceLatestVariables, _, _ := service.CreateInstallationArgsForCall(0)
Expect(ignoreWarnings).To(Equal(false))
Expect(deployProducts).To(Equal(true))
Expect(forceLatestVariables).To(Equal(false))

Expect(stderr).To(gbytes.Say("attempting to apply changes to the targeted Ops Manager"))

Expand Down Expand Up @@ -90,7 +92,7 @@ var _ = Describe("ApplyChanges", func() {

Expect(service.CreateInstallationCallCount()).To(Equal(1))

ignoreWarnings, deployProducts, _, _ := service.CreateInstallationArgsForCall(0)
ignoreWarnings, deployProducts, _, _, _ := service.CreateInstallationArgsForCall(0)
Expect(ignoreWarnings).To(Equal(false))
Expect(deployProducts).To(Equal(true))

Expand All @@ -116,19 +118,33 @@ var _ = Describe("ApplyChanges", func() {
err := executeCommand(command, []string{"--ignore-warnings"})
Expect(err).ToNot(HaveOccurred())

ignoreWarnings, _, _, _ := service.CreateInstallationArgsForCall(0)
ignoreWarnings, _, _, _, _ := service.CreateInstallationArgsForCall(0)
Expect(ignoreWarnings).To(Equal(true))
})
})

When("passed the force-latest-variables flag", func() {
It("applies changes while forcing the latest variable versions to be used", func() {
service.InfoReturns(api.Info{Version: "2.3-build43"}, nil)

command := commands.NewApplyChanges(service, pendingService, writer, logger, 1)

err := executeCommand(command, []string{"--force-latest-variables"})
Expect(err).ToNot(HaveOccurred())

_, _, forceLatestVariables, _, _ := service.CreateInstallationArgsForCall(0)
Expect(forceLatestVariables).To(Equal(true))
})
})

When("passed the skip-deploy-products flag", func() {
It("applies changes while not deploying products", func() {
command := commands.NewApplyChanges(service, pendingService, writer, logger, 1)

err := executeCommand(command, []string{"--skip-deploy-products"})
Expect(err).ToNot(HaveOccurred())

_, deployProducts, _, _ := service.CreateInstallationArgsForCall(0)
_, _, deployProducts, _, _ := service.CreateInstallationArgsForCall(0)
Expect(deployProducts).To(Equal(false))
})

Expand All @@ -149,7 +165,7 @@ var _ = Describe("ApplyChanges", func() {
err := executeCommand(command, []string{"--product-name", "product1", "--product-name", "product2"})
Expect(err).To(HaveOccurred())

_, _, productNames, _ := service.CreateInstallationArgsForCall(0)
_, _, _, productNames, _ := service.CreateInstallationArgsForCall(0)
Expect(productNames).To(ConsistOf("product1", "product2"))
})
})
Expand Down Expand Up @@ -382,9 +398,10 @@ errands:

Expect(service.CreateInstallationCallCount()).To(Equal(1))

ignoreWarnings, deployProducts, _, errands := service.CreateInstallationArgsForCall(0)
ignoreWarnings, deployProducts, forceLatestVariables, _, errands := service.CreateInstallationArgsForCall(0)
Expect(ignoreWarnings).To(Equal(false))
Expect(deployProducts).To(Equal(true))
Expect(forceLatestVariables).To(Equal(false))
Expect(errands).To(Equal(api.ApplyErrandChanges{
Errands: map[string]api.ProductErrand{
"product1_name": {
Expand Down
Loading
Loading