<commit_before>package recipebuilder_test

import (
	"time"

	. "github.com/cloudfoundry-incubator/nsync/recipebuilder"

	"github.com/cloudfoundry-incubator/nsync/recipebuilder"
	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/runtime-schema/cc_messages"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/pivotal-golang/lager"

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

var _ = Describe("Recipe Builder", func() {
	var (
		builder       *recipebuilder.RecipeBuilder
		err           error
		desiredAppReq cc_messages.DesireAppRequestFromCC
		desiredLRP    *receptor.DesiredLRPCreateRequest
		circuses      map[string]string
	)

	defaultNofile := DefaultFileDescriptorLimit

	BeforeEach(func() {
		logger := lager.NewLogger("fakelogger")

		circuses = map[string]string{
			"some-stack": "some-circus.tgz",
		}

		builder = recipebuilder.New(circuses, "the/docker/circus/path.tgz", "http://file-server.com", logger)

		desiredAppReq = cc_messages.DesireAppRequestFromCC{
			ProcessGuid:       "the-app-guid-the-app-version",
			DropletUri:        "http://the-droplet.uri.com",
			Stack:             "some-stack",
			StartCommand:      "the-start-command with-arguments",
			ExecutionMetadata: "the-execution-metadata",
			Environment: cc_messages.Environment{
				{Name: "foo", Value: "bar"},
			},
			MemoryMB:        128,
			DiskMB:          512,
			FileDescriptors: 32,
			NumInstances:    23,
			Routes:          []string{"route1", "route2"},
			LogGuid:         "the-log-id",

			HealthCheckType:             cc_messages.PortHealthCheckType,
			HealthCheckTimeoutInSeconds: 123456,

			ETag: "etag-updated-at",
		}
	})

	JustBeforeEach(func() {
		desiredLRP, err = builder.Build(&desiredAppReq)
	})

	Describe("CPU weight calculation", func() {
		Context("when the memory limit is below the minimum value", func() {
			BeforeEach(func() {
				desiredAppReq.MemoryMB = recipebuilder.MinCpuProxy - 9999
			})
			It("returns 1", func() {
				Ω(desiredLRP.CPUWeight).Should(Equal(uint(1)))
			})
		})

		Context("when the memory limit is above the maximum value", func() {
			BeforeEach(func() {
				desiredAppReq.MemoryMB = recipebuilder.MaxCpuProxy + 9999
			})
			It("returns 100", func() {
				Ω(desiredLRP.CPUWeight).Should(Equal(uint(100)))
			})
		})

		Context("when the memory limit is in between the minimum and maximum value", func() {
			BeforeEach(func() {
				desiredAppReq.MemoryMB = (recipebuilder.MinCpuProxy + recipebuilder.MaxCpuProxy) / 2
			})
			It("returns 50", func() {
				Ω(desiredLRP.CPUWeight).Should(Equal(uint(50)))
			})
		})
	})

	Context("when everything is correct", func() {
		It("does not error", func() {
			Ω(err).ShouldNot(HaveOccurred())
		})

		It("builds a valid DesiredLRP", func() {
			Ω(desiredLRP.ProcessGuid).Should(Equal("the-app-guid-the-app-version"))
			Ω(desiredLRP.Instances).Should(Equal(23))
			Ω(desiredLRP.Routes).Should(Equal([]string{"route1", "route2"}))
			Ω(desiredLRP.Annotation).Should(Equal("etag-updated-at"))
			Ω(desiredLRP.Stack).Should(Equal("some-stack"))
			Ω(desiredLRP.MemoryMB).Should(Equal(128))
			Ω(desiredLRP.DiskMB).Should(Equal(512))
			Ω(desiredLRP.Ports).Should(Equal([]uint32{8080}))
			Ω(desiredLRP.Privileged).Should(BeTrue())
			Ω(desiredLRP.StartTimeout).Should(Equal(uint(123456)))

			Ω(desiredLRP.LogGuid).Should(Equal("the-log-id"))
			Ω(desiredLRP.LogSource).Should(Equal(recipebuilder.LRPLogSource))

			expectedSetup := models.Serial([]models.Action{
				&models.DownloadAction{
					From: "http://file-server.com/v1/static/some-circus.tgz",
					To:   "/tmp/circus",
				},
				&models.DownloadAction{
					From:     "http://the-droplet.uri.com",
					To:       ".",
					CacheKey: "droplets-the-app-guid-the-app-version",
				},
			}...)
			Ω(desiredLRP.Setup).Should(Equal(expectedSetup))

			runAction, ok := desiredLRP.Action.(*models.RunAction)
			Ω(ok).Should(BeTrue())

			Ω(desiredLRP.Monitor).Should(Equal(&models.TimeoutAction{
				Timeout: 30 * time.Second,
				Action: &models.RunAction{
					Path:      "/tmp/circus/spy",
					Args:      []string{"-addr=:8080"},
					LogSource: HealthLogSource,
					ResourceLimits: models.ResourceLimits{
						Nofile: &defaultNofile,
					},
				},
			}))

			Ω(runAction.Path).Should(Equal("/tmp/circus/soldier"))
			Ω(runAction.Args).Should(Equal([]string{
				"/app",
				"the-start-command with-arguments",
				"the-execution-metadata",
			}))
			Ω(runAction.LogSource).Should(Equal(recipebuilder.AppLogSource))

			numFiles := uint64(32)
			Ω(runAction.ResourceLimits).Should(Equal(models.ResourceLimits{
				Nofile: &numFiles,
			}))

			Ω(runAction.Env).Should(ContainElement(models.EnvironmentVariable{
				Name:  "foo",
				Value: "bar",
			}))

			Ω(runAction.Env).Should(ContainElement(models.EnvironmentVariable{
				Name:  "PORT",
				Value: "8080",
			}))
		})

		Context("when no health check is specified", func() {
			BeforeEach(func() {
				desiredAppReq.HealthCheckType = cc_messages.UnspecifiedHealthCheckType
			})

			It("sets up the port check for backwards compatibility", func() {
				downloadDestinations := []string{}
				for _, action := range desiredLRP.Setup.(*models.SerialAction).Actions {
					switch a := action.(type) {
					case *models.DownloadAction:
						downloadDestinations = append(downloadDestinations, a.To)
					}
				}

				Ω(downloadDestinations).Should(ContainElement("/tmp/circus"))

				Ω(desiredLRP.Monitor).Should(Equal(&models.TimeoutAction{
					Timeout: 30 * time.Second,
					Action: &models.RunAction{
						Path:           "/tmp/circus/spy",
						Args:           []string{"-addr=:8080"},
						LogSource:      HealthLogSource,
						ResourceLimits: models.ResourceLimits{Nofile: &defaultNofile},
					},
				}))
			})
		})

		Context("when the 'none' health check is specified", func() {
			BeforeEach(func() {
				desiredAppReq.HealthCheckType = cc_messages.NoneHealthCheckType
			})

			It("does not populate the monitor action", func() {
				Ω(desiredLRP.Monitor).Should(BeNil())
			})

			It("still downloads the circus, since we need it for the soldier", func() {
				downloadDestinations := []string{}
				for _, action := range desiredLRP.Setup.(*models.SerialAction).Actions {
					switch a := action.(type) {
					case *models.DownloadAction:
						downloadDestinations = append(downloadDestinations, a.To)
					}
				}

				Ω(downloadDestinations).Should(ContainElement("/tmp/circus"))
			})
		})
	})

	Context("when there is a docker image url instead of a droplet uri", func() {
		BeforeEach(func() {
			desiredAppReq.DockerImageUrl = "user/repo:tag"
			desiredAppReq.DropletUri = ""
		})

		It("does not error", func() {
			Ω(err).ShouldNot(HaveOccurred())
		})

		It("uses an unprivileged container", func() {
			Ω(desiredLRP.Privileged).Should(BeFalse())
		})

		It("converts the docker image url into a root fs path", func() {
			Ω(desiredLRP.RootFSPath).Should(Equal("docker:///user/repo#tag"))
		})

		It("uses the docker circus", func() {
			Ω(desiredLRP.Setup.(*models.SerialAction).Actions[0]).Should(Equal(&models.DownloadAction{
				From: "http://file-server.com/v1/static/the/docker/circus/path.tgz",
				To:   "/tmp/circus",
			}))
		})

		Context("and the docker image url has no tag", func() {
			BeforeEach(func() {
				desiredAppReq.DockerImageUrl = "user/repo"
			})

			It("does not specify a url fragment for the tag, assumes garden-linux sets a default", func() {
				Ω(desiredLRP.RootFSPath).Should(Equal("docker:///user/repo"))
			})
		})
	})

	Context("when there is a docker image url AND a droplet uri", func() {
		BeforeEach(func() {
			desiredAppReq.DockerImageUrl = "user/repo:tag"
			desiredAppReq.DropletUri = "http://the-droplet.uri.com"
		})

		It("should error", func() {
			Ω(err).Should(MatchError(recipebuilder.ErrMultipleAppSources))
		})
	})

	Context("when there is NEITHER a docker image url NOR a droplet uri", func() {
		BeforeEach(func() {
			desiredAppReq.DockerImageUrl = ""
			desiredAppReq.DropletUri = ""
		})

		It("should error", func() {
			Ω(err).Should(MatchError(recipebuilder.ErrAppSourceMissing))
		})
	})

	Context("when there is no file descriptor limit", func() {
		BeforeEach(func() {
			desiredAppReq.FileDescriptors = 0
		})

		It("does not error", func() {
			Ω(err).ShouldNot(HaveOccurred())
		})

		It("sets a default FD limit on the run action", func() {
			runAction, ok := desiredLRP.Action.(*models.RunAction)
			Ω(ok).Should(BeTrue())

			Ω(runAction.ResourceLimits.Nofile).ShouldNot(BeNil())
			Ω(*runAction.ResourceLimits.Nofile).Should(Equal(DefaultFileDescriptorLimit))
		})
	})

	Context("when requesting a stack with no associated health-check", func() {
		BeforeEach(func() {
			desiredAppReq.Stack = "some-other-stack"
		})

		It("should error", func() {
			Ω(err).Should(MatchError(recipebuilder.ErrNoCircusDefined))
		})
	})
})
<commit_msg>Test log source text explicitly in recipe builder<commit_after>package recipebuilder_test

import (
	"time"

	"github.com/cloudfoundry-incubator/nsync/recipebuilder"
	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/runtime-schema/cc_messages"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/pivotal-golang/lager"

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

var _ = Describe("Recipe Builder", func() {
	var (
		builder       *recipebuilder.RecipeBuilder
		err           error
		desiredAppReq cc_messages.DesireAppRequestFromCC
		desiredLRP    *receptor.DesiredLRPCreateRequest
		circuses      map[string]string
	)

	defaultNofile := recipebuilder.DefaultFileDescriptorLimit

	BeforeEach(func() {
		logger := lager.NewLogger("fakelogger")

		circuses = map[string]string{
			"some-stack": "some-circus.tgz",
		}

		builder = recipebuilder.New(circuses, "the/docker/circus/path.tgz", "http://file-server.com", logger)

		desiredAppReq = cc_messages.DesireAppRequestFromCC{
			ProcessGuid:       "the-app-guid-the-app-version",
			DropletUri:        "http://the-droplet.uri.com",
			Stack:             "some-stack",
			StartCommand:      "the-start-command with-arguments",
			ExecutionMetadata: "the-execution-metadata",
			Environment: cc_messages.Environment{
				{Name: "foo", Value: "bar"},
			},
			MemoryMB:        128,
			DiskMB:          512,
			FileDescriptors: 32,
			NumInstances:    23,
			Routes:          []string{"route1", "route2"},
			LogGuid:         "the-log-id",

			HealthCheckType:             cc_messages.PortHealthCheckType,
			HealthCheckTimeoutInSeconds: 123456,

			ETag: "etag-updated-at",
		}
	})

	JustBeforeEach(func() {
		desiredLRP, err = builder.Build(&desiredAppReq)
	})

	Describe("CPU weight calculation", func() {
		Context("when the memory limit is below the minimum value", func() {
			BeforeEach(func() {
				desiredAppReq.MemoryMB = recipebuilder.MinCpuProxy - 9999
			})
			It("returns 1", func() {
				Ω(desiredLRP.CPUWeight).Should(Equal(uint(1)))
			})
		})

		Context("when the memory limit is above the maximum value", func() {
			BeforeEach(func() {
				desiredAppReq.MemoryMB = recipebuilder.MaxCpuProxy + 9999
			})
			It("returns 100", func() {
				Ω(desiredLRP.CPUWeight).Should(Equal(uint(100)))
			})
		})

		Context("when the memory limit is in between the minimum and maximum value", func() {
			BeforeEach(func() {
				desiredAppReq.MemoryMB = (recipebuilder.MinCpuProxy + recipebuilder.MaxCpuProxy) / 2
			})
			It("returns 50", func() {
				Ω(desiredLRP.CPUWeight).Should(Equal(uint(50)))
			})
		})
	})

	Context("when everything is correct", func() {
		It("does not error", func() {
			Ω(err).ShouldNot(HaveOccurred())
		})

		It("builds a valid DesiredLRP", func() {
			Ω(desiredLRP.ProcessGuid).Should(Equal("the-app-guid-the-app-version"))
			Ω(desiredLRP.Instances).Should(Equal(23))
			Ω(desiredLRP.Routes).Should(Equal([]string{"route1", "route2"}))
			Ω(desiredLRP.Annotation).Should(Equal("etag-updated-at"))
			Ω(desiredLRP.Stack).Should(Equal("some-stack"))
			Ω(desiredLRP.MemoryMB).Should(Equal(128))
			Ω(desiredLRP.DiskMB).Should(Equal(512))
			Ω(desiredLRP.Ports).Should(Equal([]uint32{8080}))
			Ω(desiredLRP.Privileged).Should(BeTrue())
			Ω(desiredLRP.StartTimeout).Should(Equal(uint(123456)))

			Ω(desiredLRP.LogGuid).Should(Equal("the-log-id"))
			Ω(desiredLRP.LogSource).Should(Equal("CELL"))

			expectedSetup := models.Serial([]models.Action{
				&models.DownloadAction{
					From: "http://file-server.com/v1/static/some-circus.tgz",
					To:   "/tmp/circus",
				},
				&models.DownloadAction{
					From:     "http://the-droplet.uri.com",
					To:       ".",
					CacheKey: "droplets-the-app-guid-the-app-version",
				},
			}...)
			Ω(desiredLRP.Setup).Should(Equal(expectedSetup))

			runAction, ok := desiredLRP.Action.(*models.RunAction)
			Ω(ok).Should(BeTrue())

			Ω(desiredLRP.Monitor).Should(Equal(&models.TimeoutAction{
				Timeout: 30 * time.Second,
				Action: &models.RunAction{
					Path:      "/tmp/circus/spy",
					Args:      []string{"-addr=:8080"},
					LogSource: "HEALTH",
					ResourceLimits: models.ResourceLimits{
						Nofile: &defaultNofile,
					},
				},
			}))

			Ω(runAction.Path).Should(Equal("/tmp/circus/soldier"))
			Ω(runAction.Args).Should(Equal([]string{
				"/app",
				"the-start-command with-arguments",
				"the-execution-metadata",
			}))
			Ω(runAction.LogSource).Should(Equal("APP"))

			numFiles := uint64(32)
			Ω(runAction.ResourceLimits).Should(Equal(models.ResourceLimits{
				Nofile: &numFiles,
			}))

			Ω(runAction.Env).Should(ContainElement(models.EnvironmentVariable{
				Name:  "foo",
				Value: "bar",
			}))

			Ω(runAction.Env).Should(ContainElement(models.EnvironmentVariable{
				Name:  "PORT",
				Value: "8080",
			}))
		})

		Context("when no health check is specified", func() {
			BeforeEach(func() {
				desiredAppReq.HealthCheckType = cc_messages.UnspecifiedHealthCheckType
			})

			It("sets up the port check for backwards compatibility", func() {
				downloadDestinations := []string{}
				for _, action := range desiredLRP.Setup.(*models.SerialAction).Actions {
					switch a := action.(type) {
					case *models.DownloadAction:
						downloadDestinations = append(downloadDestinations, a.To)
					}
				}

				Ω(downloadDestinations).Should(ContainElement("/tmp/circus"))

				Ω(desiredLRP.Monitor).Should(Equal(&models.TimeoutAction{
					Timeout: 30 * time.Second,
					Action: &models.RunAction{
						Path:           "/tmp/circus/spy",
						Args:           []string{"-addr=:8080"},
						LogSource:      "HEALTH",
						ResourceLimits: models.ResourceLimits{Nofile: &defaultNofile},
					},
				}))
			})
		})

		Context("when the 'none' health check is specified", func() {
			BeforeEach(func() {
				desiredAppReq.HealthCheckType = cc_messages.NoneHealthCheckType
			})

			It("does not populate the monitor action", func() {
				Ω(desiredLRP.Monitor).Should(BeNil())
			})

			It("still downloads the circus, since we need it for the soldier", func() {
				downloadDestinations := []string{}
				for _, action := range desiredLRP.Setup.(*models.SerialAction).Actions {
					switch a := action.(type) {
					case *models.DownloadAction:
						downloadDestinations = append(downloadDestinations, a.To)
					}
				}

				Ω(downloadDestinations).Should(ContainElement("/tmp/circus"))
			})
		})
	})

	Context("when there is a docker image url instead of a droplet uri", func() {
		BeforeEach(func() {
			desiredAppReq.DockerImageUrl = "user/repo:tag"
			desiredAppReq.DropletUri = ""
		})

		It("does not error", func() {
			Ω(err).ShouldNot(HaveOccurred())
		})

		It("uses an unprivileged container", func() {
			Ω(desiredLRP.Privileged).Should(BeFalse())
		})

		It("converts the docker image url into a root fs path", func() {
			Ω(desiredLRP.RootFSPath).Should(Equal("docker:///user/repo#tag"))
		})

		It("uses the docker circus", func() {
			Ω(desiredLRP.Setup.(*models.SerialAction).Actions[0]).Should(Equal(&models.DownloadAction{
				From: "http://file-server.com/v1/static/the/docker/circus/path.tgz",
				To:   "/tmp/circus",
			}))
		})

		Context("and the docker image url has no tag", func() {
			BeforeEach(func() {
				desiredAppReq.DockerImageUrl = "user/repo"
			})

			It("does not specify a url fragment for the tag, assumes garden-linux sets a default", func() {
				Ω(desiredLRP.RootFSPath).Should(Equal("docker:///user/repo"))
			})
		})
	})

	Context("when there is a docker image url AND a droplet uri", func() {
		BeforeEach(func() {
			desiredAppReq.DockerImageUrl = "user/repo:tag"
			desiredAppReq.DropletUri = "http://the-droplet.uri.com"
		})

		It("should error", func() {
			Ω(err).Should(MatchError(recipebuilder.ErrMultipleAppSources))
		})
	})

	Context("when there is NEITHER a docker image url NOR a droplet uri", func() {
		BeforeEach(func() {
			desiredAppReq.DockerImageUrl = ""
			desiredAppReq.DropletUri = ""
		})

		It("should error", func() {
			Ω(err).Should(MatchError(recipebuilder.ErrAppSourceMissing))
		})
	})

	Context("when there is no file descriptor limit", func() {
		BeforeEach(func() {
			desiredAppReq.FileDescriptors = 0
		})

		It("does not error", func() {
			Ω(err).ShouldNot(HaveOccurred())
		})

		It("sets a default FD limit on the run action", func() {
			runAction, ok := desiredLRP.Action.(*models.RunAction)
			Ω(ok).Should(BeTrue())

			Ω(runAction.ResourceLimits.Nofile).ShouldNot(BeNil())
			Ω(*runAction.ResourceLimits.Nofile).Should(Equal(recipebuilder.DefaultFileDescriptorLimit))
		})
	})

	Context("when requesting a stack with no associated health-check", func() {
		BeforeEach(func() {
			desiredAppReq.Stack = "some-other-stack"
		})

		It("should error", func() {
			Ω(err).Should(MatchError(recipebuilder.ErrNoCircusDefined))
		})
	})
})
<|endoftext|>


<commit_before>package commands_test

import (
	"cf"
	. "cf/commands"
	"github.com/stretchr/testify/assert"
	testapi "testhelpers/api"
	testassert "testhelpers/assert"
	testcmd "testhelpers/commands"
	testconfig "testhelpers/configuration"
	testreq "testhelpers/requirements"
	testterm "testhelpers/terminal"
	"testing"
)

func TestPasswordRequiresValidAccessToken(t *testing.T) {
	reqFactory := &testreq.FakeReqFactory{ValidAccessTokenSuccess: false}
	configRepo := &testconfig.FakeConfigRepository{}
	callPassword([]string{}, reqFactory, &testapi.FakePasswordRepo{}, configRepo)
	assert.False(t, testcmd.CommandDidPassRequirements)

	reqFactory = &testreq.FakeReqFactory{ValidAccessTokenSuccess: true}
	callPassword([]string{"", "", ""}, reqFactory, &testapi.FakePasswordRepo{}, configRepo)
	assert.True(t, testcmd.CommandDidPassRequirements)
}

func TestPasswordCanBeChanged(t *testing.T) {
	reqFactory := &testreq.FakeReqFactory{ValidAccessTokenSuccess: true}
	pwdRepo := &testapi.FakePasswordRepo{}
	configRepo := &testconfig.FakeConfigRepository{}
	ui := callPassword([]string{"old-password", "new-password", "new-password"}, reqFactory, pwdRepo, configRepo)

	testassert.SliceContains(t, ui.PasswordPrompts, testassert.Lines{
		{"Current Password"},
		{"New Password"},
		{"Verify Password"},
	})

	testassert.SliceContains(t, ui.Outputs, testassert.Lines{
		{"Changing password..."},
		{"OK"},
		{"Please log in again"},
	})

	assert.Equal(t, pwdRepo.UpdateNewPassword, "new-password")
	assert.Equal(t, pwdRepo.UpdateOldPassword, "old-password")

	updatedConfig, err := configRepo.Get()
	assert.NoError(t, err)
	assert.Empty(t, updatedConfig.AccessToken)
	assert.Equal(t, updatedConfig.OrganizationFields, cf.OrganizationFields{})
	assert.Equal(t, updatedConfig.SpaceFields, cf.SpaceFields{})
}

func TestPasswordVerification(t *testing.T) {
	reqFactory := &testreq.FakeReqFactory{ValidAccessTokenSuccess: true}
	pwdRepo := &testapi.FakePasswordRepo{}
	configRepo := &testconfig.FakeConfigRepository{}
	ui := callPassword([]string{"old-password", "new-password", "new-password-with-error"}, reqFactory, pwdRepo, configRepo)

	testassert.SliceContains(t, ui.PasswordPrompts, testassert.Lines{
		{"Current Password"},
		{"New Password"},
		{"Verify Password"},
	})
	testassert.SliceContains(t, ui.Outputs, testassert.Lines{
		{"FAILED"},
		{"Password verification does not match"},
	})

	assert.Equal(t, pwdRepo.UpdateNewPassword, "")
}

func TestWhenCurrentPasswordDoesNotMatch(t *testing.T) {
	reqFactory := &testreq.FakeReqFactory{ValidAccessTokenSuccess: true}
	pwdRepo := &testapi.FakePasswordRepo{UpdateUnauthorized: true}
	configRepo := &testconfig.FakeConfigRepository{}
	ui := callPassword([]string{"old-password", "new-password", "new-password"}, reqFactory, pwdRepo, configRepo)

	testassert.SliceContains(t, ui.PasswordPrompts, testassert.Lines{
		{"Current Password"},
		{"New Password"},
		{"Verify Password"},
	})

	testassert.SliceContains(t, ui.Outputs, testassert.Lines{
		{"Changing password..."},
		{"FAILED"},
		{"Current password did not match"},
	})

	assert.Equal(t, pwdRepo.UpdateNewPassword, "new-password")
	assert.Equal(t, pwdRepo.UpdateOldPassword, "old-password")
}

func callPassword(inputs []string, reqFactory *testreq.FakeReqFactory, pwdRepo *testapi.FakePasswordRepo, configRepo *testconfig.FakeConfigRepository) (ui *testterm.FakeUI) {
	ui = &testterm.FakeUI{Inputs: inputs}

	ctxt := testcmd.NewContext("passwd", []string{})
	cmd := NewPassword(ui, pwdRepo, configRepo)
	testcmd.RunCommand(cmd, ctxt, reqFactory)

	return
}
<commit_msg>Clean up password tests by introducing a dependencies obj<commit_after>package commands_test

import (
	"cf"
	. "cf/commands"
	"github.com/stretchr/testify/assert"
	testapi "testhelpers/api"
	testassert "testhelpers/assert"
	testcmd "testhelpers/commands"
	testconfig "testhelpers/configuration"
	testreq "testhelpers/requirements"
	testterm "testhelpers/terminal"
	"testing"
)

func TestPasswordRequiresValidAccessToken(t *testing.T) {
	deps := getPasswordDeps()
	deps.ReqFactory.ValidAccessTokenSuccess = false
	callPassword([]string{}, deps)
	assert.False(t, testcmd.CommandDidPassRequirements)

	deps.ReqFactory.ValidAccessTokenSuccess = true
	callPassword([]string{"", "", ""}, deps)
	assert.True(t, testcmd.CommandDidPassRequirements)
}

func TestPasswordCanBeChanged(t *testing.T) {
	deps := getPasswordDeps()
	deps.ReqFactory.ValidAccessTokenSuccess = true
	deps.PwdRepo.UpdateUnauthorized = false
	ui := callPassword([]string{"old-password", "new-password", "new-password"}, deps)

	testassert.SliceContains(t, ui.PasswordPrompts, testassert.Lines{
		{"Current Password"},
		{"New Password"},
		{"Verify Password"},
	})

	testassert.SliceContains(t, ui.Outputs, testassert.Lines{
		{"Changing password..."},
		{"OK"},
		{"Please log in again"},
	})

	assert.Equal(t, deps.PwdRepo.UpdateNewPassword, "new-password")
	assert.Equal(t, deps.PwdRepo.UpdateOldPassword, "old-password")

	updatedConfig, err := deps.ConfigRepo.Get()
	assert.NoError(t, err)
	assert.Empty(t, updatedConfig.AccessToken)
	assert.Equal(t, updatedConfig.OrganizationFields, cf.OrganizationFields{})
	assert.Equal(t, updatedConfig.SpaceFields, cf.SpaceFields{})
}

func TestPasswordVerification(t *testing.T) {
	deps := getPasswordDeps()
	deps.ReqFactory.ValidAccessTokenSuccess = true
	ui := callPassword([]string{"old-password", "new-password", "new-password-with-error"}, deps)

	testassert.SliceContains(t, ui.PasswordPrompts, testassert.Lines{
		{"Current Password"},
		{"New Password"},
		{"Verify Password"},
	})
	testassert.SliceContains(t, ui.Outputs, testassert.Lines{
		{"FAILED"},
		{"Password verification does not match"},
	})

	assert.Equal(t, deps.PwdRepo.UpdateNewPassword, "")
}

func TestWhenCurrentPasswordDoesNotMatch(t *testing.T) {
	deps := getPasswordDeps()
	deps.ReqFactory.ValidAccessTokenSuccess = true
	deps.PwdRepo.UpdateUnauthorized = true
	ui := callPassword([]string{"old-password", "new-password", "new-password"}, deps)

	testassert.SliceContains(t, ui.PasswordPrompts, testassert.Lines{
		{"Current Password"},
		{"New Password"},
		{"Verify Password"},
	})

	testassert.SliceContains(t, ui.Outputs, testassert.Lines{
		{"Changing password..."},
		{"FAILED"},
		{"Current password did not match"},
	})

	assert.Equal(t, deps.PwdRepo.UpdateNewPassword, "new-password")
	assert.Equal(t, deps.PwdRepo.UpdateOldPassword, "old-password")
}

type passwordDeps struct {
	ReqFactory *testreq.FakeReqFactory
	PwdRepo    *testapi.FakePasswordRepo
	ConfigRepo *testconfig.FakeConfigRepository
}

func getPasswordDeps() passwordDeps {
	return passwordDeps{
		ReqFactory: &testreq.FakeReqFactory{ValidAccessTokenSuccess: true},
		PwdRepo:    &testapi.FakePasswordRepo{UpdateUnauthorized: true},
		ConfigRepo: &testconfig.FakeConfigRepository{},
	}
}

func callPassword(inputs []string, deps passwordDeps) (ui *testterm.FakeUI) {
	ui = &testterm.FakeUI{Inputs: inputs}

	ctxt := testcmd.NewContext("passwd", []string{})
	cmd := NewPassword(ui, deps.PwdRepo, deps.ConfigRepo)
	testcmd.RunCommand(cmd, ctxt, deps.ReqFactory)

	return
}
<|endoftext|>


<commit_before>package client

import (
	opClient "github.com/coreos-inc/operator-client/pkg/client"
	"github.com/pkg/errors"
	corev1 "k8s.io/api/core/v1"
	v1beta1extensions "k8s.io/api/extensions/v1beta1"
	v1beta1rbac "k8s.io/api/rbac/v1beta1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type InstallStrategyDeploymentInterface interface {
	CreateRole(role *v1beta1rbac.Role) (*v1beta1rbac.Role, error)
	CreateRoleBinding(roleBinding *v1beta1rbac.RoleBinding) (*v1beta1rbac.RoleBinding, error)
	EnsureServiceAccount(serviceAccount *corev1.ServiceAccount) (*corev1.ServiceAccount, error)
	CreateDeployment(deployment *v1beta1extensions.Deployment) (*v1beta1extensions.Deployment, error)
	GetServiceAccountByName(serviceAccountName string) (*corev1.ServiceAccount, error)
	GetOwnedDeployments(owner metav1.ObjectMeta) (*v1beta1extensions.DeploymentList, error)
}

type InstallStrategyDeploymentClientForNamespace struct {
	opClient  opClient.Interface
	Namespace string
}

var _ InstallStrategyDeploymentInterface = &InstallStrategyDeploymentClientForNamespace{}

func NewInstallStrategyDeploymentClient(opClient opClient.Interface, namespace string) InstallStrategyDeploymentInterface {
	return &InstallStrategyDeploymentClientForNamespace{
		opClient:  opClient,
		Namespace: namespace,
	}
}

func (c *InstallStrategyDeploymentClientForNamespace) CreateRole(role *v1beta1rbac.Role) (*v1beta1rbac.Role, error) {
	return c.opClient.KubernetesInterface().RbacV1beta1().Roles(c.Namespace).Create(role)
}

func (c *InstallStrategyDeploymentClientForNamespace) CreateRoleBinding(roleBinding *v1beta1rbac.RoleBinding) (*v1beta1rbac.RoleBinding, error) {
	return c.opClient.KubernetesInterface().RbacV1beta1().RoleBindings(c.Namespace).Create(roleBinding)
}

func (c *InstallStrategyDeploymentClientForNamespace) EnsureServiceAccount(serviceAccount *corev1.ServiceAccount) (*corev1.ServiceAccount, error) {
	foundAccount, err := c.opClient.KubernetesInterface().CoreV1().ServiceAccounts(c.Namespace).Get(serviceAccount.Name, metav1.GetOptions{})
	if err == nil {
		return foundAccount, nil
	}
	if !apierrors.IsNotFound(err) {
		return nil, errors.Wrap(err, "checking for existing serviceacccount failed")
	}

	createdAccount, err := c.opClient.KubernetesInterface().CoreV1().ServiceAccounts(c.Namespace).Create(serviceAccount)
	if err != nil && !apierrors.IsAlreadyExists(err) {
		return nil, errors.Wrap(err, "creating serviceacccount failed")
	}
	if apierrors.IsAlreadyExists(err) {
		return serviceAccount, nil
	}
	return createdAccount, nil
}

func (c *InstallStrategyDeploymentClientForNamespace) CreateDeployment(deployment *v1beta1extensions.Deployment) (*v1beta1extensions.Deployment, error) {
	return c.opClient.CreateDeployment(deployment)
}

func (c *InstallStrategyDeploymentClientForNamespace) GetServiceAccountByName(serviceAccountName string) (*corev1.ServiceAccount, error) {
	return c.opClient.KubernetesInterface().CoreV1().ServiceAccounts(c.Namespace).Get(serviceAccountName, metav1.GetOptions{})
}

func (c *InstallStrategyDeploymentClientForNamespace) GetOwnedDeployments(owner metav1.ObjectMeta) (*v1beta1extensions.DeploymentList, error) {
	return c.opClient.ListDeploymentsWithLabels(c.Namespace, map[string]string{
		"alm-owner-name":      owner.Name,
		"alm-owner-namespace": owner.Namespace,
	})
}
<commit_msg>feat(client/deployment_install): create OR update deployment by name<commit_after>package client

import (
	opClient "github.com/coreos-inc/operator-client/pkg/client"
	"github.com/pkg/errors"
	corev1 "k8s.io/api/core/v1"
	v1beta1extensions "k8s.io/api/extensions/v1beta1"
	v1beta1rbac "k8s.io/api/rbac/v1beta1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type InstallStrategyDeploymentInterface interface {
	CreateRole(role *v1beta1rbac.Role) (*v1beta1rbac.Role, error)
	CreateRoleBinding(roleBinding *v1beta1rbac.RoleBinding) (*v1beta1rbac.RoleBinding, error)
	EnsureServiceAccount(serviceAccount *corev1.ServiceAccount) (*corev1.ServiceAccount, error)
	CreateDeployment(deployment *v1beta1extensions.Deployment) (*v1beta1extensions.Deployment, error)
	GetServiceAccountByName(serviceAccountName string) (*corev1.ServiceAccount, error)
	GetOwnedDeployments(owner metav1.ObjectMeta) (*v1beta1extensions.DeploymentList, error)
}

type InstallStrategyDeploymentClientForNamespace struct {
	opClient  opClient.Interface
	Namespace string
}

var _ InstallStrategyDeploymentInterface = &InstallStrategyDeploymentClientForNamespace{}

func NewInstallStrategyDeploymentClient(opClient opClient.Interface, namespace string) InstallStrategyDeploymentInterface {
	return &InstallStrategyDeploymentClientForNamespace{
		opClient:  opClient,
		Namespace: namespace,
	}
}

func (c *InstallStrategyDeploymentClientForNamespace) CreateRole(role *v1beta1rbac.Role) (*v1beta1rbac.Role, error) {
	return c.opClient.KubernetesInterface().RbacV1beta1().Roles(c.Namespace).Create(role)
}

func (c *InstallStrategyDeploymentClientForNamespace) CreateRoleBinding(roleBinding *v1beta1rbac.RoleBinding) (*v1beta1rbac.RoleBinding, error) {
	return c.opClient.KubernetesInterface().RbacV1beta1().RoleBindings(c.Namespace).Create(roleBinding)
}

func (c *InstallStrategyDeploymentClientForNamespace) EnsureServiceAccount(serviceAccount *corev1.ServiceAccount) (*corev1.ServiceAccount, error) {
	foundAccount, err := c.opClient.KubernetesInterface().CoreV1().ServiceAccounts(c.Namespace).Get(serviceAccount.Name, metav1.GetOptions{})
	if err == nil {
		return foundAccount, nil
	}
	if !apierrors.IsNotFound(err) {
		return nil, errors.Wrap(err, "checking for existing serviceacccount failed")
	}

	createdAccount, err := c.opClient.KubernetesInterface().CoreV1().ServiceAccounts(c.Namespace).Create(serviceAccount)
	if err != nil && !apierrors.IsAlreadyExists(err) {
		return nil, errors.Wrap(err, "creating serviceacccount failed")
	}
	if apierrors.IsAlreadyExists(err) {
		return serviceAccount, nil
	}
	return createdAccount, nil
}

func (c *InstallStrategyDeploymentClientForNamespace) CreateDeployment(deployment *v1beta1extensions.Deployment) (*v1beta1extensions.Deployment, error) {
	_, d, err := c.opClient.CreateOrRollingUpdateDeployment(deployment)
	return d, err
}

func (c *InstallStrategyDeploymentClientForNamespace) GetServiceAccountByName(serviceAccountName string) (*corev1.ServiceAccount, error) {
	return c.opClient.KubernetesInterface().CoreV1().ServiceAccounts(c.Namespace).Get(serviceAccountName, metav1.GetOptions{})
}

func (c *InstallStrategyDeploymentClientForNamespace) GetOwnedDeployments(owner metav1.ObjectMeta) (*v1beta1extensions.DeploymentList, error) {
	return c.opClient.ListDeploymentsWithLabels(c.Namespace, map[string]string{
		"alm-owner-name":      owner.Name,
		"alm-owner-namespace": owner.Namespace,
	})
}
<|endoftext|>


<commit_before>package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"net/http"
	"net/url"
	"strings"

	"github.com/rancher/norman/httperror"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/proxy"
	"k8s.io/client-go/rest"
)

type RemoteService struct {
	cluster   *v3.Cluster
	transport http.RoundTripper
	url       urlGetter
	auth      string
}

var (
	er = &errorResponder{}
)

type urlGetter func() (url.URL, error)

type errorResponder struct {
}

func (e *errorResponder) Error(w http.ResponseWriter, req *http.Request, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte(err.Error()))
}

func prefix(cluster *v3.Cluster) string {
	return "/k8s/clusters/" + cluster.Name
}

func New(localConfig *rest.Config, cluster *v3.Cluster, clusterLister v3.ClusterLister, factory dialer.Factory) (*RemoteService, error) {
	if cluster.Spec.Internal {
		return NewLocal(localConfig, cluster)
	}
	return NewRemote(cluster, clusterLister, factory)
}

func NewLocal(localConfig *rest.Config, cluster *v3.Cluster) (*RemoteService, error) {
	// the gvk is ignored by us, so just pass in any gvk
	hostURL, _, err := rest.DefaultServerURL(localConfig.Host, localConfig.APIPath, schema.GroupVersion{}, true)
	if err != nil {
		return nil, err
	}

	transport, err := rest.TransportFor(localConfig)
	if err != nil {
		return nil, err
	}

	rs := &RemoteService{
		cluster: cluster,
		url: func() (url.URL, error) {
			return *hostURL, nil
		},
		transport: transport,
	}
	if localConfig.BearerToken != "" {
		rs.auth = "Bearer " + localConfig.BearerToken
	}

	return rs, nil
}

func NewRemote(cluster *v3.Cluster, clusterLister v3.ClusterLister, factory dialer.Factory) (*RemoteService, error) {
	if !v3.ClusterConditionProvisioned.IsTrue(cluster) {
		return nil, httperror.NewAPIError(httperror.ClusterUnavailable, "cluster not provisioned")
	}

	transport := &http.Transport{}

	if factory != nil {
		d, err := factory.ClusterDialer(cluster.Name)
		if err != nil {
			return nil, err
		}
		transport.Dial = d
	}

	if cluster.Status.CACert != "" {
		certBytes, err := base64.StdEncoding.DecodeString(cluster.Status.CACert)
		if err != nil {
			return nil, err
		}
		certs := x509.NewCertPool()
		certs.AppendCertsFromPEM(certBytes)
		transport.TLSClientConfig = &tls.Config{
			RootCAs: certs,
		}
	}

	urlGetter := func() (url.URL, error) {
		newCluster, err := clusterLister.Get("", cluster.Name)
		if err != nil {
			return url.URL{}, err
		}

		u, err := url.Parse(newCluster.Status.APIEndpoint)
		if err != nil {
			return url.URL{}, err
		}
		return *u, nil
	}

	return &RemoteService{
		cluster:   cluster,
		transport: transport,
		url:       urlGetter,
		auth:      "Bearer " + cluster.Status.ServiceAccountToken,
	}, nil
}

func (r *RemoteService) Close() {
}

func (r *RemoteService) Handler() http.Handler {
	return r
}

func (r *RemoteService) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u, err := r.url()
	if err != nil {
		er.Error(rw, req, err)
		return
	}

	u.Path = strings.TrimPrefix(req.URL.Path, prefix(r.cluster))
	u.RawQuery = req.URL.RawQuery

	proto := req.Header.Get("X-Forwarded-Proto")
	if proto != "" {
		req.URL.Scheme = proto
	} else if req.TLS == nil {
		req.URL.Scheme = "http"
	} else {
		req.URL.Scheme = "https"
	}

	req.URL.Host = req.Host
	if r.auth != "" {
		req.Header.Set("Authorization", r.auth)
	}

	httpProxy := proxy.NewUpgradeAwareHandler(&u, r.transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)
}

func (r *RemoteService) Cluster() *v3.Cluster {
	return r.cluster
}

type SimpleProxy struct {
	url       *url.URL
	transport http.RoundTripper
}

func NewSimpleProxy(host string, caData []byte) (*SimpleProxy, error) {
	hostURL, _, err := rest.DefaultServerURL(host, "", schema.GroupVersion{}, true)
	if err != nil {
		return nil, err
	}

	ht := &http.Transport{}
	if len(caData) > 0 {
		certPool := x509.NewCertPool()
		certPool.AppendCertsFromPEM(caData)
		ht.TLSClientConfig = &tls.Config{
			RootCAs: certPool,
		}
	}

	return &SimpleProxy{
		url:       hostURL,
		transport: ht,
	}, nil
}

func (s *SimpleProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u := *s.url
	u.Path = req.URL.Path
	u.RawQuery = req.URL.RawQuery
	req.URL.Scheme = "https"
	req.URL.Host = req.Host
	httpProxy := proxy.NewUpgradeAwareHandler(&u, s.transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)

}
<commit_msg>Add Basic password support<commit_after>package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/rancher/norman/httperror"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/proxy"
	"k8s.io/client-go/rest"
)

type RemoteService struct {
	cluster   *v3.Cluster
	transport http.RoundTripper
	url       urlGetter
	auth      string
}

var (
	er = &errorResponder{}
)

type urlGetter func() (url.URL, error)

type errorResponder struct {
}

func (e *errorResponder) Error(w http.ResponseWriter, req *http.Request, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte(err.Error()))
}

func prefix(cluster *v3.Cluster) string {
	return "/k8s/clusters/" + cluster.Name
}

func New(localConfig *rest.Config, cluster *v3.Cluster, clusterLister v3.ClusterLister, factory dialer.Factory) (*RemoteService, error) {
	if cluster.Spec.Internal {
		return NewLocal(localConfig, cluster)
	}
	return NewRemote(cluster, clusterLister, factory)
}

func NewLocal(localConfig *rest.Config, cluster *v3.Cluster) (*RemoteService, error) {
	// the gvk is ignored by us, so just pass in any gvk
	hostURL, _, err := rest.DefaultServerURL(localConfig.Host, localConfig.APIPath, schema.GroupVersion{}, true)
	if err != nil {
		return nil, err
	}

	transport, err := rest.TransportFor(localConfig)
	if err != nil {
		return nil, err
	}

	rs := &RemoteService{
		cluster: cluster,
		url: func() (url.URL, error) {
			return *hostURL, nil
		},
		transport: transport,
	}
	if localConfig.BearerToken != "" {
		rs.auth = "Bearer " + localConfig.BearerToken
	} else if localConfig.Password != "" {
		rs.auth = "Basic " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", localConfig.Username, localConfig.Password)))
	}

	return rs, nil
}

func NewRemote(cluster *v3.Cluster, clusterLister v3.ClusterLister, factory dialer.Factory) (*RemoteService, error) {
	if !v3.ClusterConditionProvisioned.IsTrue(cluster) {
		return nil, httperror.NewAPIError(httperror.ClusterUnavailable, "cluster not provisioned")
	}

	transport := &http.Transport{}

	if factory != nil {
		d, err := factory.ClusterDialer(cluster.Name)
		if err != nil {
			return nil, err
		}
		transport.Dial = d
	}

	if cluster.Status.CACert != "" {
		certBytes, err := base64.StdEncoding.DecodeString(cluster.Status.CACert)
		if err != nil {
			return nil, err
		}
		certs := x509.NewCertPool()
		certs.AppendCertsFromPEM(certBytes)
		transport.TLSClientConfig = &tls.Config{
			RootCAs: certs,
		}
	}

	urlGetter := func() (url.URL, error) {
		newCluster, err := clusterLister.Get("", cluster.Name)
		if err != nil {
			return url.URL{}, err
		}

		u, err := url.Parse(newCluster.Status.APIEndpoint)
		if err != nil {
			return url.URL{}, err
		}
		return *u, nil
	}

	return &RemoteService{
		cluster:   cluster,
		transport: transport,
		url:       urlGetter,
		auth:      "Bearer " + cluster.Status.ServiceAccountToken,
	}, nil
}

func (r *RemoteService) Close() {
}

func (r *RemoteService) Handler() http.Handler {
	return r
}

func (r *RemoteService) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u, err := r.url()
	if err != nil {
		er.Error(rw, req, err)
		return
	}

	u.Path = strings.TrimPrefix(req.URL.Path, prefix(r.cluster))
	u.RawQuery = req.URL.RawQuery

	proto := req.Header.Get("X-Forwarded-Proto")
	if proto != "" {
		req.URL.Scheme = proto
	} else if req.TLS == nil {
		req.URL.Scheme = "http"
	} else {
		req.URL.Scheme = "https"
	}

	req.URL.Host = req.Host
	if r.auth != "" {
		req.Header.Set("Authorization", r.auth)
	}

	httpProxy := proxy.NewUpgradeAwareHandler(&u, r.transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)
}

func (r *RemoteService) Cluster() *v3.Cluster {
	return r.cluster
}

type SimpleProxy struct {
	url       *url.URL
	transport http.RoundTripper
}

func NewSimpleProxy(host string, caData []byte) (*SimpleProxy, error) {
	hostURL, _, err := rest.DefaultServerURL(host, "", schema.GroupVersion{}, true)
	if err != nil {
		return nil, err
	}

	ht := &http.Transport{}
	if len(caData) > 0 {
		certPool := x509.NewCertPool()
		certPool.AppendCertsFromPEM(caData)
		ht.TLSClientConfig = &tls.Config{
			RootCAs: certPool,
		}
	}

	return &SimpleProxy{
		url:       hostURL,
		transport: ht,
	}, nil
}

func (s *SimpleProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u := *s.url
	u.Path = req.URL.Path
	u.RawQuery = req.URL.RawQuery
	req.URL.Scheme = "https"
	req.URL.Host = req.Host
	httpProxy := proxy.NewUpgradeAwareHandler(&u, s.transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)

}
<|endoftext|>


<commit_before>// Package sendtables contains sendtable specific magic and should really be better documented (TODO).
package sendtables

import (
	"bytes"
	"fmt"
	"strings"

	bit "github.com/markus-wa/demoinfocs-golang/v2/internal/bitread"
)

// sendPropertyFlags stores multiple send property flags.
type sendPropertyFlags int

// hasFlagSet returns true if the given flag is set
func (spf sendPropertyFlags) hasFlagSet(flag sendPropertyFlags) bool {
	return int(spf)&int(flag) == int(flag)
}

type sendTable struct {
	properties []sendTableProperty
	name       string
	isEnd      bool
}

type sendTableProperty struct {
	flags            sendPropertyFlags
	name             string
	dataTableName    string
	lowValue         float32
	highValue        float32
	numberOfBits     int
	numberOfElements int
	priority         int
	rawType          int
}

// Stores meta information about a property of an Entity.
type flattenedPropEntry struct {
	prop             *sendTableProperty
	arrayElementProp *sendTableProperty
	name             string
}

// ServerClass stores meta information about Entity types (e.g. palyers, teams etc.).
type ServerClass struct {
	id              int
	name            string
	dataTableID     int
	dataTableName   string
	baseClasses     []*ServerClass
	flattenedProps  []flattenedPropEntry
	propNameToIndex map[string]int

	createdHandlers      []EntityCreatedHandler
	instanceBaseline     []byte                // Raw baseline
	preprocessedBaseline map[int]PropertyValue // Preprocessed baseline
}

// ID returns the server-class's ID.
func (sc *ServerClass) ID() int {
	return sc.id
}

// Name returns the server-class's name.
func (sc *ServerClass) Name() string {
	return sc.name
}

// DataTableID returns the data-table ID.
func (sc *ServerClass) DataTableID() int {
	return sc.dataTableID
}

// DataTableName returns the data-table name.
func (sc *ServerClass) DataTableName() string {
	return sc.dataTableName
}

// BaseClasses returns the base-classes of this server-class.
func (sc *ServerClass) BaseClasses() []*ServerClass {
	return sc.baseClasses
}

// PropertyEntries returns the names of all property-entries on this server-class.
func (sc *ServerClass) PropertyEntries() []string {
	propEntryCount := len(sc.flattenedProps)
	names := make([]string, propEntryCount)

	for i := 0; i < propEntryCount; i++ {
		names[i] = sc.flattenedProps[i].name
	}

	return names
}

func (sc *ServerClass) newEntity(entityDataReader *bit.BitReader, entityID int) *entity {
	props := make([]property, len(sc.flattenedProps))

	for i := range sc.flattenedProps {
		props[i] = property{entry: &sc.flattenedProps[i]}
	}

	entity := &entity{serverClass: sc, id: entityID, props: props}

	entity.initialize()

	if sc.preprocessedBaseline != nil {
		entity.applyBaseline(sc.preprocessedBaseline)
	} else if sc.instanceBaseline != nil {
		r := bit.NewSmallBitReader(bytes.NewReader(sc.instanceBaseline))
		sc.preprocessedBaseline = entity.initializeBaseline(r)
		r.Pool()
	} else {
		sc.preprocessedBaseline = make(map[int]PropertyValue)
	}

	entity.ApplyUpdate(entityDataReader)

	// Fire created-handlers so update-handlers can be registered
	for _, h := range sc.createdHandlers {
		h(entity)
	}

	// Fire all post-creation actions
	for _, f := range entity.onCreateFinished {
		f()
	}

	return entity
}

// OnEntityCreated registers a function to be called when a new entity is created from this ServerClass.
func (sc *ServerClass) OnEntityCreated(handler EntityCreatedHandler) {
	sc.createdHandlers = append(sc.createdHandlers, handler)
}

// EntityCreatedHandler is the interface for handlers that are interested in EntityCreatedEvents.
type EntityCreatedHandler func(Entity)

var serverClassStringFormat = `ServerClass: id=%d name=%s
	dataTableId=%d
	dataTableName=%s
	baseClasses:
		%s
	properties:
		%s`

func (sc *ServerClass) String() string {
	baseClasses := make([]string, len(sc.baseClasses))
	for i, bc := range sc.baseClasses {
		baseClasses[i] = bc.name
	}

	props := make([]string, len(sc.flattenedProps))
	for i, fProp := range sc.flattenedProps {
		props[i] = fProp.name
	}

	baseClassesStr := "-"
	if len(baseClasses) > 0 {
		baseClassesStr = strings.Join(baseClasses, "\n\t\t")
	}

	propsStr := "-"
	if len(props) > 0 {
		propsStr = strings.Join(props, "\n\t\t")
	}

	return fmt.Sprintf(serverClassStringFormat, sc.id, sc.name, sc.dataTableID, sc.dataTableName, baseClassesStr, propsStr)
}
<commit_msg>sendtables: improve package doc<commit_after>// Package sendtables contains code related to decoding sendtables.
// Mostly used internally but can be interesting for direct access to server-classes and entities.
package sendtables

import (
	"bytes"
	"fmt"
	"strings"

	bit "github.com/markus-wa/demoinfocs-golang/v2/internal/bitread"
)

// sendPropertyFlags stores multiple send property flags.
type sendPropertyFlags int

// hasFlagSet returns true if the given flag is set
func (spf sendPropertyFlags) hasFlagSet(flag sendPropertyFlags) bool {
	return int(spf)&int(flag) == int(flag)
}

type sendTable struct {
	properties []sendTableProperty
	name       string
	isEnd      bool
}

type sendTableProperty struct {
	flags            sendPropertyFlags
	name             string
	dataTableName    string
	lowValue         float32
	highValue        float32
	numberOfBits     int
	numberOfElements int
	priority         int
	rawType          int
}

// Stores meta information about a property of an Entity.
type flattenedPropEntry struct {
	prop             *sendTableProperty
	arrayElementProp *sendTableProperty
	name             string
}

// ServerClass stores meta information about Entity types (e.g. palyers, teams etc.).
type ServerClass struct {
	id              int
	name            string
	dataTableID     int
	dataTableName   string
	baseClasses     []*ServerClass
	flattenedProps  []flattenedPropEntry
	propNameToIndex map[string]int

	createdHandlers      []EntityCreatedHandler
	instanceBaseline     []byte                // Raw baseline
	preprocessedBaseline map[int]PropertyValue // Preprocessed baseline
}

// ID returns the server-class's ID.
func (sc *ServerClass) ID() int {
	return sc.id
}

// Name returns the server-class's name.
func (sc *ServerClass) Name() string {
	return sc.name
}

// DataTableID returns the data-table ID.
func (sc *ServerClass) DataTableID() int {
	return sc.dataTableID
}

// DataTableName returns the data-table name.
func (sc *ServerClass) DataTableName() string {
	return sc.dataTableName
}

// BaseClasses returns the base-classes of this server-class.
func (sc *ServerClass) BaseClasses() []*ServerClass {
	return sc.baseClasses
}

// PropertyEntries returns the names of all property-entries on this server-class.
func (sc *ServerClass) PropertyEntries() []string {
	propEntryCount := len(sc.flattenedProps)
	names := make([]string, propEntryCount)

	for i := 0; i < propEntryCount; i++ {
		names[i] = sc.flattenedProps[i].name
	}

	return names
}

func (sc *ServerClass) newEntity(entityDataReader *bit.BitReader, entityID int) *entity {
	props := make([]property, len(sc.flattenedProps))

	for i := range sc.flattenedProps {
		props[i] = property{entry: &sc.flattenedProps[i]}
	}

	entity := &entity{serverClass: sc, id: entityID, props: props}

	entity.initialize()

	if sc.preprocessedBaseline != nil {
		entity.applyBaseline(sc.preprocessedBaseline)
	} else if sc.instanceBaseline != nil {
		r := bit.NewSmallBitReader(bytes.NewReader(sc.instanceBaseline))
		sc.preprocessedBaseline = entity.initializeBaseline(r)
		r.Pool()
	} else {
		sc.preprocessedBaseline = make(map[int]PropertyValue)
	}

	entity.ApplyUpdate(entityDataReader)

	// Fire created-handlers so update-handlers can be registered
	for _, h := range sc.createdHandlers {
		h(entity)
	}

	// Fire all post-creation actions
	for _, f := range entity.onCreateFinished {
		f()
	}

	return entity
}

// OnEntityCreated registers a function to be called when a new entity is created from this ServerClass.
func (sc *ServerClass) OnEntityCreated(handler EntityCreatedHandler) {
	sc.createdHandlers = append(sc.createdHandlers, handler)
}

// EntityCreatedHandler is the interface for handlers that are interested in EntityCreatedEvents.
type EntityCreatedHandler func(Entity)

var serverClassStringFormat = `ServerClass: id=%d name=%s
	dataTableId=%d
	dataTableName=%s
	baseClasses:
		%s
	properties:
		%s`

func (sc *ServerClass) String() string {
	baseClasses := make([]string, len(sc.baseClasses))
	for i, bc := range sc.baseClasses {
		baseClasses[i] = bc.name
	}

	props := make([]string, len(sc.flattenedProps))
	for i, fProp := range sc.flattenedProps {
		props[i] = fProp.name
	}

	baseClassesStr := "-"
	if len(baseClasses) > 0 {
		baseClassesStr = strings.Join(baseClasses, "\n\t\t")
	}

	propsStr := "-"
	if len(props) > 0 {
		propsStr = strings.Join(props, "\n\t\t")
	}

	return fmt.Sprintf(serverClassStringFormat, sc.id, sc.name, sc.dataTableID, sc.dataTableName, baseClassesStr, propsStr)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package csi

import (
	"encoding/json"
	"fmt"
	"os"
	"sync"

	"github.com/coreos/pkg/capnslog"
	cephconfig "github.com/rook/rook/pkg/daemon/ceph/config"
	"github.com/rook/rook/pkg/operator/k8sutil"
	v1 "k8s.io/api/core/v1"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

var (
	logger = capnslog.NewPackageLogger("github.com/rook/rook", "ceph-csi")
)

type csiClusterConfigEntry struct {
	ClusterID string   `json:"clusterID"`
	Monitors  []string `json:"monitors"`
}

type csiClusterConfig []csiClusterConfigEntry

// FormatCsiClusterConfig returns a json-formatted string containing
// the cluster-to-mon mapping required to configure ceph csi.
func FormatCsiClusterConfig(
	clusterKey string, mons map[string]*cephconfig.MonInfo) (string, error) {

	cc := make(csiClusterConfig, 1)
	cc[0].ClusterID = clusterKey
	cc[0].Monitors = []string{}
	for _, m := range mons {
		cc[0].Monitors = append(cc[0].Monitors, m.Endpoint)
	}

	ccJson, err := json.Marshal(cc)
	if err != nil {
		return "", fmt.Errorf("failed to marshal csi cluster config. %+v", err)
	}
	return string(ccJson), nil
}

func parseCsiClusterConfig(c string) (csiClusterConfig, error) {
	var cc csiClusterConfig
	err := json.Unmarshal([]byte(c), &cc)
	if err != nil {
		return cc, fmt.Errorf("failed to parse csi cluster config. %+v", err)
	}
	return cc, nil
}

func formatCsiClusterConfig(cc csiClusterConfig) (string, error) {
	ccJson, err := json.Marshal(cc)
	if err != nil {
		return "", fmt.Errorf("failed to marshal csi cluster config. %+v", err)
	}
	return string(ccJson), nil
}

func monEndpoints(mons map[string]*cephconfig.MonInfo) []string {
	endpoints := make([]string, 0)
	for _, m := range mons {
		endpoints = append(endpoints, m.Endpoint)
	}
	return endpoints
}

// UpdateCsiClusterConfig returns a json-formatted string containing
// the cluster-to-mon mapping required to configure ceph csi.
func UpdateCsiClusterConfig(
	curr, clusterKey string, mons map[string]*cephconfig.MonInfo) (string, error) {

	var (
		cc     csiClusterConfig
		centry csiClusterConfigEntry
		found  bool
	)
	cc, err := parseCsiClusterConfig(curr)
	if err != nil {
		return "", err
	}

	for i, centry := range cc {
		if centry.ClusterID == clusterKey {
			centry.Monitors = monEndpoints(mons)
			found = true
			cc[i] = centry
			break
		}
	}
	if !found {
		centry.ClusterID = clusterKey
		centry.Monitors = monEndpoints(mons)
		cc = append(cc, centry)
	}
	return formatCsiClusterConfig(cc)
}

// CreateCsiConfigMap creates an empty config map that will be later used
// to provide cluster configuration to ceph-csi.
func CreateCsiConfigMap(namespace string, clientset kubernetes.Interface) error {
	configMap := &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      ConfigName,
			Namespace: namespace,
		},
	}
	configMap.Data = map[string]string{
		ConfigKey: "[]",
	}

	if _, err := clientset.CoreV1().ConfigMaps(namespace).Create(configMap); err != nil {
		if !k8serrors.IsAlreadyExists(err) {
			return err
		}
	}
	return nil
}

// SaveClusterConfig updates the config map used to provide ceph-csi with
// basic cluster configuration. The clusterNamespace and clusterInfo are
// used to determine what "cluster" in the config map will be updated and
// and the clusterNamespace value is epxected to match the clusterID
// value that is provided to ceph-csi uses in the storage class.
// The locker l is typically a mutex and is used to prevent the config
// map from being updated for multiple clusters simultaneously.
func SaveClusterConfig(
	clientset kubernetes.Interface, clusterNamespace string,
	clusterInfo *cephconfig.ClusterInfo, l sync.Locker) error {

	if !CSIEnabled() {
		return nil
	}
	l.Lock()
	defer l.Unlock()
	// csi is deployed into the same namespace as the operator
	csiNamespace := os.Getenv(k8sutil.PodNamespaceEnvVar)
	logger.Debug("Using %+v for CSI ConfigMap Namespace", csiNamespace)

	// fetch current ConfigMap contents
	configMap, err := clientset.CoreV1().ConfigMaps(csiNamespace).Get(
		ConfigName, metav1.GetOptions{})
	if err != nil {
		return fmt.Errorf("failed to fetch current csi config map: %+v", err)
	}

	// update ConfigMap contents for current cluster
	currData := configMap.Data[ConfigKey]
	if currData == "" {
		currData = "[]"
	}
	newData, err := UpdateCsiClusterConfig(
		currData, clusterNamespace, clusterInfo.Monitors)
	if err != nil {
		return fmt.Errorf("failed to update csi config map data: %+v", err)
	}
	configMap.Data[ConfigKey] = newData

	// update ConfigMap with new contents
	if _, err := clientset.CoreV1().ConfigMaps(csiNamespace).Update(configMap); err != nil {
		return fmt.Errorf("failed to update csi config map: %+v", err)
	}
	return nil
}
<commit_msg>ceph csi: ensure the required namespace env var is set<commit_after>/*
Copyright 2019 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package csi

import (
	"encoding/json"
	"fmt"
	"os"
	"sync"

	"github.com/coreos/pkg/capnslog"
	cephconfig "github.com/rook/rook/pkg/daemon/ceph/config"
	"github.com/rook/rook/pkg/operator/k8sutil"
	v1 "k8s.io/api/core/v1"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

var (
	logger = capnslog.NewPackageLogger("github.com/rook/rook", "ceph-csi")
)

type csiClusterConfigEntry struct {
	ClusterID string   `json:"clusterID"`
	Monitors  []string `json:"monitors"`
}

type csiClusterConfig []csiClusterConfigEntry

// FormatCsiClusterConfig returns a json-formatted string containing
// the cluster-to-mon mapping required to configure ceph csi.
func FormatCsiClusterConfig(
	clusterKey string, mons map[string]*cephconfig.MonInfo) (string, error) {

	cc := make(csiClusterConfig, 1)
	cc[0].ClusterID = clusterKey
	cc[0].Monitors = []string{}
	for _, m := range mons {
		cc[0].Monitors = append(cc[0].Monitors, m.Endpoint)
	}

	ccJson, err := json.Marshal(cc)
	if err != nil {
		return "", fmt.Errorf("failed to marshal csi cluster config. %+v", err)
	}
	return string(ccJson), nil
}

func parseCsiClusterConfig(c string) (csiClusterConfig, error) {
	var cc csiClusterConfig
	err := json.Unmarshal([]byte(c), &cc)
	if err != nil {
		return cc, fmt.Errorf("failed to parse csi cluster config. %+v", err)
	}
	return cc, nil
}

func formatCsiClusterConfig(cc csiClusterConfig) (string, error) {
	ccJson, err := json.Marshal(cc)
	if err != nil {
		return "", fmt.Errorf("failed to marshal csi cluster config. %+v", err)
	}
	return string(ccJson), nil
}

func monEndpoints(mons map[string]*cephconfig.MonInfo) []string {
	endpoints := make([]string, 0)
	for _, m := range mons {
		endpoints = append(endpoints, m.Endpoint)
	}
	return endpoints
}

// UpdateCsiClusterConfig returns a json-formatted string containing
// the cluster-to-mon mapping required to configure ceph csi.
func UpdateCsiClusterConfig(
	curr, clusterKey string, mons map[string]*cephconfig.MonInfo) (string, error) {

	var (
		cc     csiClusterConfig
		centry csiClusterConfigEntry
		found  bool
	)
	cc, err := parseCsiClusterConfig(curr)
	if err != nil {
		return "", err
	}

	for i, centry := range cc {
		if centry.ClusterID == clusterKey {
			centry.Monitors = monEndpoints(mons)
			found = true
			cc[i] = centry
			break
		}
	}
	if !found {
		centry.ClusterID = clusterKey
		centry.Monitors = monEndpoints(mons)
		cc = append(cc, centry)
	}
	return formatCsiClusterConfig(cc)
}

// CreateCsiConfigMap creates an empty config map that will be later used
// to provide cluster configuration to ceph-csi.
func CreateCsiConfigMap(namespace string, clientset kubernetes.Interface) error {
	configMap := &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      ConfigName,
			Namespace: namespace,
		},
	}
	configMap.Data = map[string]string{
		ConfigKey: "[]",
	}

	if _, err := clientset.CoreV1().ConfigMaps(namespace).Create(configMap); err != nil {
		if !k8serrors.IsAlreadyExists(err) {
			return err
		}
	}
	return nil
}

// SaveClusterConfig updates the config map used to provide ceph-csi with
// basic cluster configuration. The clusterNamespace and clusterInfo are
// used to determine what "cluster" in the config map will be updated and
// and the clusterNamespace value is epxected to match the clusterID
// value that is provided to ceph-csi uses in the storage class.
// The locker l is typically a mutex and is used to prevent the config
// map from being updated for multiple clusters simultaneously.
func SaveClusterConfig(
	clientset kubernetes.Interface, clusterNamespace string,
	clusterInfo *cephconfig.ClusterInfo, l sync.Locker) error {

	if !CSIEnabled() {
		return nil
	}
	l.Lock()
	defer l.Unlock()
	// csi is deployed into the same namespace as the operator
	csiNamespace := os.Getenv(k8sutil.PodNamespaceEnvVar)
	if csiNamespace == "" {
		return fmt.Errorf("namespace value missing (for %+v)",
			k8sutil.PodNamespaceEnvVar)
	}
	logger.Debug("Using %+v for CSI ConfigMap Namespace", csiNamespace)

	// fetch current ConfigMap contents
	configMap, err := clientset.CoreV1().ConfigMaps(csiNamespace).Get(
		ConfigName, metav1.GetOptions{})
	if err != nil {
		return fmt.Errorf("failed to fetch current csi config map: %+v", err)
	}

	// update ConfigMap contents for current cluster
	currData := configMap.Data[ConfigKey]
	if currData == "" {
		currData = "[]"
	}
	newData, err := UpdateCsiClusterConfig(
		currData, clusterNamespace, clusterInfo.Monitors)
	if err != nil {
		return fmt.Errorf("failed to update csi config map data: %+v", err)
	}
	configMap.Data[ConfigKey] = newData

	// update ConfigMap with new contents
	if _, err := clientset.CoreV1().ConfigMaps(csiNamespace).Update(configMap); err != nil {
		return fmt.Errorf("failed to update csi config map: %+v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package annotations

import "github.com/grafana/grafana/pkg/components/simplejson"

type Repository interface {
	Save(item *Item) error
	Find(query *ItemQuery) ([]*Item, error)
	Delete(params *DeleteParams) error
}

type ItemQuery struct {
	OrgId       int64    `json:"orgId"`
	From        int64    `json:"from"`
	To          int64    `json:"from"`
	Type        ItemType `json:"type"`
	AlertId     int64    `json:"alertId"`
	DashboardId int64    `json:"dashboardId"`
	PanelId     int64    `json:"panelId"`
	NewState    []string `json:"newState"`

	Limit int64 `json:"alertId"`
}

type DeleteParams struct {
	AlertId     int64 `json:"alertId"`
	DashboardId int64 `json:"dashboardId"`
	PanelId     int64 `json:"panelId"`
}

var repositoryInstance Repository

func GetRepository() Repository {
	return repositoryInstance
}

func SetRepository(rep Repository) {
	repositoryInstance = rep
}

type ItemType string

const (
	AlertType ItemType = "alert"
)

type Item struct {
	Id          int64    `json:"id"`
	OrgId       int64    `json:"orgId"`
	DashboardId int64    `json:"dashboardId"`
	PanelId     int64    `json:"panelId"`
	CategoryId  int64    `json:"panelId"`
	Type        ItemType `json:"type"`
	Title       string   `json:"title"`
	Text        string   `json:"text"`
	Metric      string   `json:"metric"`
	AlertId     int64    `json:"alertId"`
	UserId      int64    `json:"userId"`
	PrevState   string   `json:"prevState"`
	NewState    string   `json:"newState"`
	Epoch       int64    `json:"epoch"`

	Data *simplejson.Json `json:"data"`
}
<commit_msg>annotations: go vet fixes<commit_after>package annotations

import "github.com/grafana/grafana/pkg/components/simplejson"

type Repository interface {
	Save(item *Item) error
	Find(query *ItemQuery) ([]*Item, error)
	Delete(params *DeleteParams) error
}

type ItemQuery struct {
	OrgId       int64    `json:"orgId"`
	From        int64    `json:"from"`
	To          int64    `json:"to"`
	Type        ItemType `json:"type"`
	AlertId     int64    `json:"alertId"`
	DashboardId int64    `json:"dashboardId"`
	PanelId     int64    `json:"panelId"`
	NewState    []string `json:"newState"`

	Limit int64 `json:"limit"`
}

type DeleteParams struct {
	AlertId     int64 `json:"alertId"`
	DashboardId int64 `json:"dashboardId"`
	PanelId     int64 `json:"panelId"`
}

var repositoryInstance Repository

func GetRepository() Repository {
	return repositoryInstance
}

func SetRepository(rep Repository) {
	repositoryInstance = rep
}

type ItemType string

const (
	AlertType ItemType = "alert"
)

type Item struct {
	Id          int64    `json:"id"`
	OrgId       int64    `json:"orgId"`
	DashboardId int64    `json:"dashboardId"`
	PanelId     int64    `json:"panelId"`
	CategoryId  int64    `json:"categoryId"`
	Type        ItemType `json:"type"`
	Title       string   `json:"title"`
	Text        string   `json:"text"`
	Metric      string   `json:"metric"`
	AlertId     int64    `json:"alertId"`
	UserId      int64    `json:"userId"`
	PrevState   string   `json:"prevState"`
	NewState    string   `json:"newState"`
	Epoch       int64    `json:"epoch"`

	Data *simplejson.Json `json:"data"`
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Skaffold Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package lint

import (
	"context"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"

	"sigs.k8s.io/kustomize/kyaml/yaml"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/parser"
	v1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringslice"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"
	"github.com/GoogleContainerTools/skaffold/testutil"
)

var testSkaffoldYaml = `apiVersion: skaffold/v2beta21
kind: Config
build:
  artifacts:
    - image: test-app
`

var invalidSkaffoldYaml = `invalid{\{\Yaml}`

func TestGetSkaffoldYamlsLintResults(t *testing.T) {
	tests := []struct {
		description            string
		moduleAndSkaffoldYamls map[string]string
		profiles               []string
		module                 []string
		shouldErr              bool
		err                    error
		expected               map[string]*[]Result
	}{
		{
			description:            "get all skaffold yaml lint rules for 2 skaffold yaml files",
			moduleAndSkaffoldYamls: map[string]string{"cfg0": testSkaffoldYaml, "cfg1": testSkaffoldYaml},
			expected: map[string]*[]Result{
				"cfg0": {
					{
						Rule: &Rule{
							Filter: YamlFieldFilter{
								Filter: yaml.FieldMatcher{Name: "apiVersion", StringRegexValue: fmt.Sprintf("[^%s]", version.Get().ConfigVersion)},
							},
							RuleID:   SkaffoldYamlAPIVersionOutOfDate,
							RuleType: YamlFieldLintRule,
							Explanation: fmt.Sprintf("Found 'apiVersion' field with value that is not the latest skaffold apiVersion. Modify the apiVersion to the latest version: `apiVersion: %s` "+
								"or run the 'skaffold fix' command to have skaffold upgrade this for you.", version.Get().ConfigVersion),
						},
						Line:   1,
						Column: 12,
					},
				},
				"cfg1": {
					{
						Rule: &Rule{
							Filter: YamlFieldFilter{
								Filter: yaml.FieldMatcher{Name: "apiVersion", StringRegexValue: fmt.Sprintf("[^%s]", version.Get().ConfigVersion)},
							},
							RuleID:   SkaffoldYamlAPIVersionOutOfDate,
							RuleType: YamlFieldLintRule,
							Explanation: fmt.Sprintf("Found 'apiVersion' field with value that is not the latest skaffold apiVersion. Modify the apiVersion to the latest version: `apiVersion: %s` "+
								"or run the 'skaffold fix' command to have skaffold upgrade this for you.", version.Get().ConfigVersion),
						},
						Line:   1,
						Column: 12,
					},
				},
			},
		},
		{
			description:            "get all skaffold yaml lint rules for one module",
			moduleAndSkaffoldYamls: map[string]string{"cfg0": testSkaffoldYaml, "cfg1": testSkaffoldYaml},
			module:                 []string{"cfg0"},
			expected: map[string]*[]Result{
				"cfg0": {
					{
						Rule: &Rule{
							Filter: YamlFieldFilter{
								Filter: yaml.FieldMatcher{Name: "apiVersion", StringRegexValue: fmt.Sprintf("[^%s]", version.Get().ConfigVersion)},
							},
							RuleID:   SkaffoldYamlAPIVersionOutOfDate,
							RuleType: YamlFieldLintRule,
							Explanation: fmt.Sprintf("Found 'apiVersion' field with value that is not the latest skaffold apiVersion. Modify the apiVersion to the latest version: `apiVersion: %s` "+
								"or run the 'skaffold fix' command to have skaffold upgrade this for you.", version.Get().ConfigVersion),
						},
						Line:   1,
						Column: 12,
					},
				},
			},
		},
		{
			description:            "invalid skaffold yaml file",
			moduleAndSkaffoldYamls: map[string]string{"cfg0": invalidSkaffoldYaml},
			shouldErr:              true,
		},
	}

	for _, test := range tests {
		testutil.Run(t, test.description, func(t *testutil.T) {
			tmpdir := t.TempDir()
			configSet := parser.SkaffoldConfigSet{}
			for module, skaffoldyamlText := range test.moduleAndSkaffoldYamls {
				fp := filepath.Join(tmpdir, fmt.Sprintf("%s.yaml", module))
				err := ioutil.WriteFile(fp, []byte(skaffoldyamlText), 0644)
				if err != nil {
					t.Fatalf("error creating skaffold.yaml file with name %s: %v", fp, err)
				}
				configSet = append(configSet, &parser.SkaffoldConfigEntry{SkaffoldConfig: &v1.SkaffoldConfig{
					Metadata: v1.Metadata{Name: module},
				},
					SourceFile: fp,
				})
				// test overwrites file paths for expected SkaffoldYamlRules as they are made dynamically
				results := test.expected[module]
				if results == nil {
					continue
				}
				for i := range *results {
					(*results)[i].AbsFilePath = configSet[len(configSet)-1].SourceFile
					(*results)[i].RelFilePath = configSet[len(configSet)-1].SourceFile
				}
			}
			t.Override(&realWorkDir, func() (string, error) {
				return "", nil
			})
			t.Override(&getConfigSet, func(_ context.Context, opts config.SkaffoldOptions) (parser.SkaffoldConfigSet, error) {
				// mock profile activation
				var set parser.SkaffoldConfigSet
				for _, c := range configSet {
					if len(opts.ConfigurationFilter) > 0 && !stringslice.Contains(opts.ConfigurationFilter, c.Metadata.Name) {
						continue
					}
					for _, pName := range opts.Profiles {
						for _, profile := range c.Profiles {
							if profile.Name != pName {
								continue
							}
							c.Test = profile.Test
						}
					}
					set = append(set, c)
				}
				return set, test.err
			})
			results, err := GetSkaffoldYamlsLintResults(context.Background(), Options{
				OutFormat: "json", Modules: test.module, Profiles: test.profiles})
			t.CheckError(test.shouldErr, err)
			expectedResults := &[]Result{}

			// this is done to enforce result order
			for i := 0; i < len(test.expected); i++ {
				*expectedResults = append(*expectedResults, *test.expected[fmt.Sprintf("cfg%d", i)]...)
			}
			if !test.shouldErr {
				t.CheckDeepEqual(expectedResults, results)
			}
		})
	}
}
<commit_msg>fix: fix non-determinism in skaffoldyamls_test.go (#6757)<commit_after>/*
Copyright 2021 The Skaffold Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package lint

import (
	"context"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"

	"sigs.k8s.io/kustomize/kyaml/yaml"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/parser"
	v1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringslice"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"
	"github.com/GoogleContainerTools/skaffold/testutil"
)

var testSkaffoldYaml = `apiVersion: skaffold/v2beta21
kind: Config
build:
  artifacts:
    - image: test-app
`

var invalidSkaffoldYaml = `invalid{\{\Yaml}`

func TestGetSkaffoldYamlsLintResults(t *testing.T) {
	tests := []struct {
		description            string
		moduleAndSkaffoldYamls map[string]string
		profiles               []string
		module                 []string
		shouldErr              bool
		err                    error
		expected               map[string]*[]Result
	}{
		{
			description:            "get all skaffold yaml lint rules for 2 skaffold yaml files",
			moduleAndSkaffoldYamls: map[string]string{"cfg0": testSkaffoldYaml, "cfg1": testSkaffoldYaml},
			expected: map[string]*[]Result{
				"cfg0": {
					{
						Rule: &Rule{
							Filter: YamlFieldFilter{
								Filter: yaml.FieldMatcher{Name: "apiVersion", StringRegexValue: fmt.Sprintf("[^%s]", version.Get().ConfigVersion)},
							},
							RuleID:   SkaffoldYamlAPIVersionOutOfDate,
							RuleType: YamlFieldLintRule,
							Explanation: fmt.Sprintf("Found 'apiVersion' field with value that is not the latest skaffold apiVersion. Modify the apiVersion to the latest version: `apiVersion: %s` "+
								"or run the 'skaffold fix' command to have skaffold upgrade this for you.", version.Get().ConfigVersion),
						},
						Line:   1,
						Column: 12,
					},
				},
				"cfg1": {
					{
						Rule: &Rule{
							Filter: YamlFieldFilter{
								Filter: yaml.FieldMatcher{Name: "apiVersion", StringRegexValue: fmt.Sprintf("[^%s]", version.Get().ConfigVersion)},
							},
							RuleID:   SkaffoldYamlAPIVersionOutOfDate,
							RuleType: YamlFieldLintRule,
							Explanation: fmt.Sprintf("Found 'apiVersion' field with value that is not the latest skaffold apiVersion. Modify the apiVersion to the latest version: `apiVersion: %s` "+
								"or run the 'skaffold fix' command to have skaffold upgrade this for you.", version.Get().ConfigVersion),
						},
						Line:   1,
						Column: 12,
					},
				},
			},
		},
		{
			description:            "get all skaffold yaml lint rules for one module",
			moduleAndSkaffoldYamls: map[string]string{"cfg0": testSkaffoldYaml, "cfg1": testSkaffoldYaml},
			module:                 []string{"cfg0"},
			expected: map[string]*[]Result{
				"cfg0": {
					{
						Rule: &Rule{
							Filter: YamlFieldFilter{
								Filter: yaml.FieldMatcher{Name: "apiVersion", StringRegexValue: fmt.Sprintf("[^%s]", version.Get().ConfigVersion)},
							},
							RuleID:   SkaffoldYamlAPIVersionOutOfDate,
							RuleType: YamlFieldLintRule,
							Explanation: fmt.Sprintf("Found 'apiVersion' field with value that is not the latest skaffold apiVersion. Modify the apiVersion to the latest version: `apiVersion: %s` "+
								"or run the 'skaffold fix' command to have skaffold upgrade this for you.", version.Get().ConfigVersion),
						},
						Line:   1,
						Column: 12,
					},
				},
			},
		},
		{
			description:            "invalid skaffold yaml file",
			moduleAndSkaffoldYamls: map[string]string{"cfg0": invalidSkaffoldYaml},
			shouldErr:              true,
		},
	}

	for _, test := range tests {
		testutil.Run(t, test.description, func(t *testutil.T) {
			tmpdir := t.TempDir()
			configSet := parser.SkaffoldConfigSet{}
			// iteration done to enforce result order
			for i := 0; i < len(test.moduleAndSkaffoldYamls); i++ {
				module := fmt.Sprintf("cfg%d", i)
				skaffoldyamlText := test.moduleAndSkaffoldYamls[module]
				fp := filepath.Join(tmpdir, fmt.Sprintf("%s.yaml", module))
				err := ioutil.WriteFile(fp, []byte(skaffoldyamlText), 0644)
				if err != nil {
					t.Fatalf("error creating skaffold.yaml file with name %s: %v", fp, err)
				}
				configSet = append(configSet, &parser.SkaffoldConfigEntry{SkaffoldConfig: &v1.SkaffoldConfig{
					Metadata: v1.Metadata{Name: module},
				},
					SourceFile: fp,
				})
				// test overwrites file paths for expected SkaffoldYamlRules as they are made dynamically
				results := test.expected[module]
				if results == nil {
					continue
				}
				for i := range *results {
					(*results)[i].AbsFilePath = configSet[len(configSet)-1].SourceFile
					(*results)[i].RelFilePath = configSet[len(configSet)-1].SourceFile
				}
			}
			t.Override(&realWorkDir, func() (string, error) {
				return "", nil
			})
			t.Override(&getConfigSet, func(_ context.Context, opts config.SkaffoldOptions) (parser.SkaffoldConfigSet, error) {
				// mock profile activation
				var set parser.SkaffoldConfigSet
				for _, c := range configSet {
					if len(opts.ConfigurationFilter) > 0 && !stringslice.Contains(opts.ConfigurationFilter, c.Metadata.Name) {
						continue
					}
					for _, pName := range opts.Profiles {
						for _, profile := range c.Profiles {
							if profile.Name != pName {
								continue
							}
							c.Test = profile.Test
						}
					}
					set = append(set, c)
				}
				return set, test.err
			})
			results, err := GetSkaffoldYamlsLintResults(context.Background(), Options{
				OutFormat: "json", Modules: test.module, Profiles: test.profiles})
			t.CheckError(test.shouldErr, err)
			expectedResults := &[]Result{}

			// this is done to enforce result order
			for i := 0; i < len(test.expected); i++ {
				*expectedResults = append(*expectedResults, *test.expected[fmt.Sprintf("cfg%d", i)]...)
			}
			if !test.shouldErr {
				t.CheckDeepEqual(expectedResults, results)
			}
		})
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"time"

	"k8s.io/contrib/cluster-autoscaler/config"
	"k8s.io/contrib/cluster-autoscaler/simulator"
	"k8s.io/contrib/cluster-autoscaler/utils/gce"
	kube_api "k8s.io/kubernetes/pkg/api"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"

	"github.com/golang/glog"
)

// ScaleDownResult represents the state of scale down.
type ScaleDownResult int

const (
	// ScaleDownError - scale down finished with error.
	ScaleDownError ScaleDownResult = iota
	// ScaleDownNoUnderutilized - no underutilized nodedes and no errors.
	ScaleDownNoUnderutilized ScaleDownResult = iota
	// ScaleDownNoNodeDeleted - underutilized nodes present but not available for deletion.
	ScaleDownNoNodeDeleted ScaleDownResult = iota
	// ScaleDownNodeDeleted - a node was deleted.
	ScaleDownNodeDeleted ScaleDownResult = iota
)

// CalculateUnderutilizedNodes calculates which nodes are underutilized.
func CalculateUnderutilizedNodes(nodes []*kube_api.Node,
	underutilizedNodes map[string]time.Time,
	utilizationThreshold float64,
	pods []*kube_api.Pod,
	client *kube_client.Client,
	predicateChecker *simulator.PredicateChecker) map[string]time.Time {

	currentlyUnderutilizedNodes := make([]*kube_api.Node, 0)
	nodeNameToNodeInfo := schedulercache.CreateNodeNameToInfoMap(pods)

	// Phase1 - look at the nodes reservation.
	for _, node := range nodes {
		nodeInfo, found := nodeNameToNodeInfo[node.Name]
		if !found {
			glog.Errorf("Node info for %s not found", node.Name)
			continue
		}
		reservation, err := simulator.CalculateReservation(node, nodeInfo)

		if err != nil {
			glog.Warningf("Failed to calculate reservation for %s: %v", node.Name, err)
		}
		glog.V(4).Infof("Node %s - reservation %f", node.Name, reservation)

		if reservation >= utilizationThreshold {
			glog.V(4).Infof("Node %s is not suitable for removal - reservation to big (%f)", node.Name, reservation)
			continue
		}
		currentlyUnderutilizedNodes = append(currentlyUnderutilizedNodes, node)
	}

	// Phase2 - check which nodes can be probably removed using fast drain.
	nodesToRemove, err := simulator.FindNodesToRemove(currentlyUnderutilizedNodes, nodes, pods,
		client, predicateChecker,
		len(currentlyUnderutilizedNodes), true)
	if err != nil {
		glog.Errorf("Error while evaluating node utilization: %v", err)
		return map[string]time.Time{}
	}

	// Update the timestamp map.
	now := time.Now()
	result := make(map[string]time.Time)
	for _, node := range nodesToRemove {
		name := node.Name
		if val, found := underutilizedNodes[name]; !found {
			result[name] = now
		} else {
			result[name] = val
		}
	}
	return result
}

// ScaleDown tries to scale down the cluster. It returns ScaleDownResult indicating if any node was
// removed and error if such occured.
func ScaleDown(
	nodes []*kube_api.Node,
	underutilizedNodes map[string]time.Time,
	underutilizationTime time.Duration,
	pods []*kube_api.Pod,
	gceManager *gce.GceManager,
	client *kube_client.Client,
	predicateChecker *simulator.PredicateChecker) (ScaleDownResult, error) {

	now := time.Now()
	candidates := make([]*kube_api.Node, 0)
	for _, node := range nodes {
		if val, found := underutilizedNodes[node.Name]; found {
			if val.Add(underutilizationTime).Before(now) {
				candidates = append(candidates, node)
			}
		}
	}
	if len(candidates) == 0 {
		glog.Infof("No candidates for scale down")
		return ScaleDownNoUnderutilized, nil
	}

	nodesToRemove, err := simulator.FindNodesToRemove(candidates, nodes, pods, client, predicateChecker, 1, false)
	if err != nil {
		return ScaleDownError, fmt.Errorf("Find node to remove failed: %v", err)
	}
	if len(nodesToRemove) == 0 {
		glog.V(1).Infof("No node to remove")
		return ScaleDownNoNodeDeleted, nil
	}
	nodeToRemove := nodesToRemove[0]
	glog.Infof("Removing %s", nodeToRemove.Name)
	instanceConfig, err := config.InstanceConfigFromProviderId(nodeToRemove.Spec.ProviderID)
	if err != nil {
		return ScaleDownError, fmt.Errorf("Failed to get instance config for %s: %v", nodeToRemove.Name, err)
	}

	err = gceManager.DeleteInstances([]*config.InstanceConfig{instanceConfig})
	if err != nil {
		return ScaleDownError, fmt.Errorf("Failed to delete %v: %v", instanceConfig, err)
	}

	return ScaleDownNodeDeleted, nil
}
<commit_msg>Cluster-autoscaler: enforce min mig size in scale-down<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"time"

	"k8s.io/contrib/cluster-autoscaler/config"
	"k8s.io/contrib/cluster-autoscaler/simulator"
	"k8s.io/contrib/cluster-autoscaler/utils/gce"
	kube_api "k8s.io/kubernetes/pkg/api"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"

	"github.com/golang/glog"
)

// ScaleDownResult represents the state of scale down.
type ScaleDownResult int

const (
	// ScaleDownError - scale down finished with error.
	ScaleDownError ScaleDownResult = iota
	// ScaleDownNoUnderutilized - no underutilized nodedes and no errors.
	ScaleDownNoUnderutilized ScaleDownResult = iota
	// ScaleDownNoNodeDeleted - underutilized nodes present but not available for deletion.
	ScaleDownNoNodeDeleted ScaleDownResult = iota
	// ScaleDownNodeDeleted - a node was deleted.
	ScaleDownNodeDeleted ScaleDownResult = iota
)

// CalculateUnderutilizedNodes calculates which nodes are underutilized.
func CalculateUnderutilizedNodes(nodes []*kube_api.Node,
	underutilizedNodes map[string]time.Time,
	utilizationThreshold float64,
	pods []*kube_api.Pod,
	client *kube_client.Client,
	predicateChecker *simulator.PredicateChecker) map[string]time.Time {

	currentlyUnderutilizedNodes := make([]*kube_api.Node, 0)
	nodeNameToNodeInfo := schedulercache.CreateNodeNameToInfoMap(pods)

	// Phase1 - look at the nodes reservation.
	for _, node := range nodes {
		nodeInfo, found := nodeNameToNodeInfo[node.Name]
		if !found {
			glog.Errorf("Node info for %s not found", node.Name)
			continue
		}
		reservation, err := simulator.CalculateReservation(node, nodeInfo)

		if err != nil {
			glog.Warningf("Failed to calculate reservation for %s: %v", node.Name, err)
		}
		glog.V(4).Infof("Node %s - reservation %f", node.Name, reservation)

		if reservation >= utilizationThreshold {
			glog.V(4).Infof("Node %s is not suitable for removal - reservation to big (%f)", node.Name, reservation)
			continue
		}
		currentlyUnderutilizedNodes = append(currentlyUnderutilizedNodes, node)
	}

	// Phase2 - check which nodes can be probably removed using fast drain.
	nodesToRemove, err := simulator.FindNodesToRemove(currentlyUnderutilizedNodes, nodes, pods,
		client, predicateChecker,
		len(currentlyUnderutilizedNodes), true)
	if err != nil {
		glog.Errorf("Error while evaluating node utilization: %v", err)
		return map[string]time.Time{}
	}

	// Update the timestamp map.
	now := time.Now()
	result := make(map[string]time.Time)
	for _, node := range nodesToRemove {
		name := node.Name
		if val, found := underutilizedNodes[name]; !found {
			result[name] = now
		} else {
			result[name] = val
		}
	}
	return result
}

// ScaleDown tries to scale down the cluster. It returns ScaleDownResult indicating if any node was
// removed and error if such occured.
func ScaleDown(
	nodes []*kube_api.Node,
	underutilizedNodes map[string]time.Time,
	underutilizationTime time.Duration,
	pods []*kube_api.Pod,
	gceManager *gce.GceManager,
	client *kube_client.Client,
	predicateChecker *simulator.PredicateChecker) (ScaleDownResult, error) {

	now := time.Now()
	candidates := make([]*kube_api.Node, 0)
	for _, node := range nodes {
		if val, found := underutilizedNodes[node.Name]; found {

			// Check how long the node was underutilized.
			if !val.Add(underutilizationTime).Before(now) {
				continue
			}

			// Check mig size.
			instance, err := config.InstanceConfigFromProviderId(node.Spec.ProviderID)
			if err != nil {
				glog.Errorf("Error while parsing providerid of %s: %v", node.Name, err)
				continue
			}
			migConfig, err := gceManager.GetMigForInstance(instance)
			if err != nil {
				glog.Errorf("Error while checking mig config for instance %v: %v", instance, err)
				continue
			}
			size, err := gceManager.GetMigSize(migConfig)
			if err != nil {
				glog.Errorf("Error while checking mig size for instance %v: %v", instance, err)
				continue
			}

			if size <= int64(migConfig.MinSize) {
				glog.V(1).Infof("Skipping %s - mig min size reached", node.Name)
				continue
			}

			candidates = append(candidates, node)
		}
	}
	if len(candidates) == 0 {
		glog.Infof("No candidates for scale down")
		return ScaleDownNoUnderutilized, nil
	}

	nodesToRemove, err := simulator.FindNodesToRemove(candidates, nodes, pods, client, predicateChecker, 1, false)
	if err != nil {
		return ScaleDownError, fmt.Errorf("Find node to remove failed: %v", err)
	}
	if len(nodesToRemove) == 0 {
		glog.V(1).Infof("No node to remove")
		return ScaleDownNoNodeDeleted, nil
	}
	nodeToRemove := nodesToRemove[0]
	glog.Infof("Removing %s", nodeToRemove.Name)
	instanceConfig, err := config.InstanceConfigFromProviderId(nodeToRemove.Spec.ProviderID)
	if err != nil {
		return ScaleDownError, fmt.Errorf("Failed to get instance config for %s: %v", nodeToRemove.Name, err)
	}

	err = gceManager.DeleteInstances([]*config.InstanceConfig{instanceConfig})
	if err != nil {
		return ScaleDownError, fmt.Errorf("Failed to delete %v: %v", instanceConfig, err)
	}

	return ScaleDownNodeDeleted, nil
}
<|endoftext|>