-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
254 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package helm | ||
|
||
import "github.com/conplementag/cops-hq/pkg/commands" | ||
|
||
// New creates a new instance of Helm, which is a wrapper around common Helm functionality. | ||
// Parameters: | ||
// executor (can be provided from hq.GetExecutor() or by instantiating your own), | ||
// namespace (the kubernetes namespace to deploy to), | ||
// chartName (the chart name of the helm deployment), | ||
// helmDirectory (directory where your helm resources are stored. To construct the full path, simply use | ||
// filepath.Join() method and the hq.ProjectBasePath) | ||
func New(executor commands.Executor, namespace string, chartName string, helmDirectory string) Helm { | ||
|
||
return &helmWrapper{ | ||
executor: executor, | ||
namespace: namespace, | ||
chartName: chartName, | ||
helmDirectory: helmDirectory, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package helm | ||
|
||
import ( | ||
"fmt" | ||
"github.com/conplementag/cops-hq/internal" | ||
"github.com/conplementag/cops-hq/pkg/commands" | ||
"github.com/sirupsen/logrus" | ||
"gopkg.in/yaml.v3" | ||
"io/ioutil" | ||
"path/filepath" | ||
) | ||
|
||
// Helm is a wrapper around common helm functionality used for automated app deployments to kubernetes. | ||
type Helm interface { | ||
|
||
// SetVariables set the variables for the helm deployment. Variables set will be applied on any subsequent operation. If you do not provide | ||
// any variables with SetVariables, the default variables in file values.yaml are used. | ||
// | ||
// Parameters support are | ||
// helmVariables (this is a map of helm variables, defined as string keys and interface values. Nested structures are supported) | ||
SetVariables(helmVariables map[string]interface{}) error | ||
|
||
// Deploy deploys the helm charts provided in the helmDirectory to the configured kubernetes namespace. | ||
Deploy() error | ||
|
||
// GetVariablesOverrideFileName returns the file name in which the helm variables will be stored. This name is convention based on the helm tool chain. | ||
GetVariablesOverrideFileName() string | ||
} | ||
|
||
type helmWrapper struct { | ||
executor commands.Executor | ||
|
||
namespace string | ||
chartName string | ||
helmDirectory string | ||
|
||
variablesSet bool | ||
} | ||
|
||
func (h *helmWrapper) SetVariables(helmVariables map[string]interface{}) error { | ||
if helmVariables != nil { | ||
logrus.Info("Setting the helm variables...") | ||
|
||
data, err := yaml.Marshal(&helmVariables) | ||
|
||
if err != nil { | ||
return internal.ReturnErrorOrPanic(err) | ||
} | ||
|
||
// file permission: owner: r,w - group: r - other: r -> 0644 | ||
err = ioutil.WriteFile(h.getValuesOverrideFilePath(), data, 0644) | ||
|
||
if err != nil { | ||
return internal.ReturnErrorOrPanic(err) | ||
} | ||
|
||
h.variablesSet = true | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (h *helmWrapper) Deploy() error { | ||
helmCmd := fmt.Sprintf(fmt.Sprintf("helm upgrade --namespace %s --install %s %s -f %s", h.namespace, h.chartName, h.helmDirectory, | ||
h.getValuesFilePath())) | ||
|
||
if h.variablesSet { | ||
helmCmd = fmt.Sprintf("%s -f %s", helmCmd, h.getValuesOverrideFilePath()) | ||
} | ||
|
||
_, err := h.executor.Execute(helmCmd) | ||
|
||
if err != nil { | ||
return internal.ReturnErrorOrPanic(err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (h *helmWrapper) GetVariablesOverrideFileName() string { | ||
return "values.override.yaml" | ||
} | ||
|
||
func (h *helmWrapper) getValuesFilePath() string { | ||
return filepath.Join(h.helmDirectory, getVariablesFileName()) | ||
} | ||
|
||
func (h *helmWrapper) getValuesOverrideFilePath() string { | ||
return filepath.Join(h.helmDirectory, h.GetVariablesOverrideFileName()) | ||
} | ||
|
||
func getVariablesFileName() string { | ||
return "values.yaml" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package helm | ||
|
||
import ( | ||
"github.com/conplementag/cops-hq/pkg/commands" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
type executorMock struct { | ||
mock.Mock | ||
commands.Executor | ||
} | ||
|
||
func Test_SetVariablesCanCreateVariablesFile(t *testing.T) { | ||
// Arrange | ||
h, _ := createSimpleHelmWithDefaultSettings("test") | ||
|
||
var variables = make(map[string]interface{}) | ||
|
||
variables["simple"] = "value" | ||
variables["simpleNumber"] = 3 | ||
variables["truth"] = false | ||
variables["nested"] = map[string]interface{}{ | ||
"key1": "value1_nested", | ||
"key2": 3} | ||
variables["list_of_strings"] = []string{"10.0.0.1", "20.20.20.4"} | ||
variables["empty_array"] = []string{} | ||
|
||
// Act | ||
err := h.SetVariables(variables) | ||
|
||
// Assert | ||
assert.NoError(t, err) | ||
|
||
filePath := filepath.Join(".", h.GetVariablesOverrideFileName()) | ||
fileBytes, err := ioutil.ReadFile(filePath) | ||
|
||
if err != nil { | ||
assert.NoError(t, err) | ||
} | ||
|
||
// the strings below are the exact terraform format | ||
assert.Contains(t, string(fileBytes), "simple: value") | ||
assert.Contains(t, string(fileBytes), "simpleNumber: 3") | ||
assert.Contains(t, string(fileBytes), "truth: false") | ||
assert.Contains(t, string(fileBytes), "nested:") | ||
assert.Contains(t, string(fileBytes), "key1: value1_nested") | ||
assert.Contains(t, string(fileBytes), "key2: 3") | ||
assert.Contains(t, string(fileBytes), "list_of_strings:") | ||
assert.Contains(t, string(fileBytes), "- 10.0.0.1") | ||
assert.Contains(t, string(fileBytes), "empty_array") | ||
assert.Contains(t, string(fileBytes), "[]") | ||
|
||
// Cleanup | ||
if existsFile(filePath) { | ||
os.Remove(filePath) | ||
} | ||
} | ||
|
||
func Test_DeployExecutesExpectedCommandWithOverrideValues(t *testing.T) { | ||
h, executorMock := createSimpleHelmWithDefaultSettings("project") | ||
// helm upgrade with one value file is expected | ||
executorMock.On("Execute", mock.MatchedBy(func(command string) bool { | ||
return strings.Contains(command, "helm upgrade") && strings.Contains(command, "values.yaml") && strings.Contains(command, "values.override.yaml") | ||
})).Once() | ||
|
||
helmVariables := make(map[string]interface{}) | ||
helmVariables["test_key"] = "test_value" | ||
|
||
h.SetVariables(helmVariables) | ||
|
||
// Act | ||
err := h.Deploy() | ||
|
||
// Assert | ||
assert.NoError(t, err) | ||
|
||
executorMock.AssertExpectations(t) | ||
|
||
// Cleanup | ||
filePath := filepath.Join(".", h.GetVariablesOverrideFileName()) | ||
if existsFile(filePath) { | ||
os.Remove(filePath) | ||
} | ||
} | ||
|
||
func Test_DeployExecutesExpectedCommandWithoutOverrideValues(t *testing.T) { | ||
h, executorMock := createSimpleHelmWithDefaultSettings("project") | ||
// helm upgrade with one value file is expected | ||
executorMock.On("Execute", mock.MatchedBy(func(command string) bool { | ||
return strings.Contains(command, "helm upgrade") && strings.Contains(command, "values.yaml") && !strings.Contains(command, "values.override.yaml") | ||
})).Once() | ||
|
||
// Act | ||
err := h.Deploy() | ||
|
||
// Assert | ||
assert.NoError(t, err) | ||
|
||
executorMock.AssertExpectations(t) | ||
} | ||
|
||
func Test_GetVariablesOverrideFileNameReturnsSomething(t *testing.T) { | ||
h, executorMock := createSimpleHelmWithDefaultSettings("project") | ||
// helm upgrade with one value file is expected | ||
executorMock.On("Execute", mock.MatchedBy(func(command string) bool { | ||
return strings.Contains(command, "helm upgrade") && strings.Contains(command, "values.yaml") && !strings.Contains(command, "values.override.yaml") | ||
})).Once() | ||
|
||
// Act | ||
actual := h.GetVariablesOverrideFileName() | ||
|
||
// Assert | ||
assert.NotEmpty(t, actual) | ||
} | ||
|
||
func (e *executorMock) Execute(command string) (string, error) { | ||
e.Called(command) | ||
return "success", nil | ||
} | ||
|
||
func createSimpleHelmWithDefaultSettings(projectName string) (Helm, *executorMock) { | ||
executor := &executorMock{} | ||
|
||
return New(executor, projectName+"test", projectName+"test", filepath.Join(".")), executor | ||
} | ||
|
||
func existsFile(fileName string) bool { | ||
_, error := os.Stat(fileName) | ||
if os.IsNotExist(error) { | ||
return false | ||
} else { | ||
return true | ||
} | ||
} |