Skip to content
This repository has been archived by the owner on Sep 22, 2020. It is now read-only.

Add initial namespace creation logic. #8

Merged
merged 2 commits into from
Feb 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions pkg/pipelines/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,49 +57,52 @@ func Bootstrap(o *BootstrapOptions) error {
if !installed {
return errors.New("failed due to Tekton Pipelines or Triggers are not installed")
}

outputs := make([]interface{}, 0)
names := namespaceNames(o.Prefix)
for _, n := range createNamespaces(values(names)) {
outputs = append(outputs, n)
}

githubAuth, err := createOpaqueSecret("github-auth", o.GithubToken)
githubAuth, err := createOpaqueSecret(namespacedName(names["cicd"], "github-auth"), o.GithubToken)
if err != nil {
return err
return fmt.Errorf("failed to generate path to file: %w", err)
}
outputs = append(outputs, githubAuth)

// Create Docker Secret
dockerSecret, err := createDockerSecret(o.QuayAuthFileName)
dockerSecret, err := createDockerSecret(o.QuayAuthFileName, names["cicd"])
if err != nil {
return err
}
outputs = append(outputs, dockerSecret)

// Create Tasks
tasks := tasks.Generate(githubAuth.GetName())
tasks := tasks.Generate(githubAuth.GetName(), names["cicd"])
for _, task := range tasks {
outputs = append(outputs, task)
}

// Create Event Listener
eventListener := eventlisteners.Generate(o.GitRepo)
eventListener := eventlisteners.Generate(o.GitRepo, names["cicd"])
outputs = append(outputs, eventListener)

// Create route
route := routes.Generate()
outputs = append(outputs, route)

// Create Service Account, Role, Role Bindings, and ClusterRole Bindings
sa := createServiceAccount(saName, dockerSecretName)
sa := createServiceAccount(namespacedName(names["cicd"], saName), dockerSecretName)
outputs = append(outputs, sa)
role := createRole(roleName, rules)
role := createRole(namespacedName(names["cicd"], roleName), rules)
outputs = append(outputs, role)
outputs = append(outputs, createRoleBinding(roleBindingName, &sa, role.Kind, role.Name))
outputs = append(outputs, createRoleBinding("edit-clusterrole-binding", &sa, "ClusterRole", "edit"))
outputs = append(outputs, createRoleBinding(namespacedName(roleBindingName, names["ci-cd"]), sa, role.Kind, role.Name))
outputs = append(outputs, createRoleBinding(namespacedName("edit-clusterrole-binding", ""), sa, "ClusterRole", "edit"))

return marshalOutputs(os.Stdout, outputs)
}

// createDockerSecret creates Docker secret
func createDockerSecret(quayIOAuthFilename string) (*corev1.Secret, error) {
func createDockerSecret(quayIOAuthFilename, ns string) (*corev1.Secret, error) {

authJSONPath, err := homedir.Expand(quayIOAuthFilename)
if err != nil {
Expand All @@ -112,7 +115,7 @@ func createDockerSecret(quayIOAuthFilename string) (*corev1.Secret, error) {
}
defer f.Close()

dockerSecret, err := createDockerConfigSecret(dockerSecretName, f)
dockerSecret, err := createDockerConfigSecret(namespacedName(dockerSecretName, ns), f)
if err != nil {
return nil, err
}
Expand All @@ -130,6 +133,15 @@ func checkTektonInstall() (bool, error) {
return tektonChecker.checkInstall()
}

func values(m map[string]string) []string {
values := []string{}
for _, v := range m {
values = append(values, v)

}
return values
}

// marshalOutputs marshal outputs to given writer
func marshalOutputs(out io.Writer, outputs []interface{}) error {
for _, r := range outputs {
Expand Down
11 changes: 6 additions & 5 deletions pkg/pipelines/eventlisteners/eventlisteners.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ const (
stageCDDeployFilters = "(header.match('X-GitHub-Event', 'push') && body.repository.full_name == '%s') && body.ref.startsWith('refs/heads/master')"
)

// Generate will create the required eventlisteners
func Generate(githubRepo string) triggersv1.EventListener {
// Generate will create the required eventlisteners.
func Generate(githubRepo string, ns string) triggersv1.EventListener {
githubStageRepo := githubRepo + "-stage-config"
return triggersv1.EventListener{
TypeMeta: createListenerTypeMeta(),
ObjectMeta: createListenerObjectMeta("cicd-event-listener"),
ObjectMeta: createListenerObjectMeta("cicd-event-listener", ns),
Spec: triggersv1.EventListenerSpec{
ServiceAccountName: "demo-sa",
Triggers: []triggersv1.EventListenerTrigger{
Expand Down Expand Up @@ -100,8 +100,9 @@ func createListenerTypeMeta() metav1.TypeMeta {
}
}

func createListenerObjectMeta(name string) metav1.ObjectMeta {
func createListenerObjectMeta(name, ns string) metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: name,
Name: name,
Namespace: ns,
}
}
10 changes: 6 additions & 4 deletions pkg/pipelines/eventlisteners/eventlisteners_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ func TestGenerateEventListener(t *testing.T) {
APIVersion: "tekton.dev/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "cicd-event-listener",
Name: "cicd-event-listener",
Namespace: "testing",
},
Spec: triggersv1.EventListenerSpec{
ServiceAccountName: "demo-sa",
Expand Down Expand Up @@ -96,17 +97,18 @@ func TestGenerateEventListener(t *testing.T) {
},
}

eventListener := Generate("sample")
eventListener := Generate("sample", "testing")
if diff := cmp.Diff(validEventListener, eventListener); diff != "" {
t.Fatalf("Generate() failed:\n%s", diff)
}
}

func TestCreateListenerObjectMeta(t *testing.T) {
validObjectMeta := metav1.ObjectMeta{
Name: "sample",
Name: "sample",
Namespace: "testing",
}
objectMeta := createListenerObjectMeta("sample")
objectMeta := createListenerObjectMeta("sample", "testing")
if diff := cmp.Diff(validObjectMeta, objectMeta); diff != "" {
t.Fatalf("createListenerObjectMeta() failed:\n%s", diff)
}
Expand Down
27 changes: 27 additions & 0 deletions pkg/pipelines/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package pipelines

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apitypes "k8s.io/apimachinery/pkg/types"
)

func typeMeta(kind, apiVersion string) metav1.TypeMeta {
return metav1.TypeMeta{
Kind: kind,
APIVersion: apiVersion,
}
}

func objectMeta(n apitypes.NamespacedName) metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: n.Name,
Namespace: n.Namespace,
}
}

func namespacedName(ns, name string) apitypes.NamespacedName {
return apitypes.NamespacedName{
Namespace: ns,
Name: name,
}
}
40 changes: 40 additions & 0 deletions pkg/pipelines/namespaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package pipelines

import (
"fmt"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var namespaceBaseNames = map[string]string{
"dev": "dev-environment",
"stage": "stage-environment",
"cicd": "cicd-environment",
}

func createNamespaces(names []string) []*corev1.Namespace {
ns := []*corev1.Namespace{}
for _, n := range names {
ns = append(ns, createNamespace(n))
}
return ns
}

func namespaceNames(prefix string) map[string]string {
prefixedNames := make(map[string]string)
for k, v := range namespaceBaseNames {
prefixedNames[k] = fmt.Sprintf("%s%s", prefix, v)
}
return prefixedNames
}

func createNamespace(name string) *corev1.Namespace {
ns := &corev1.Namespace{
TypeMeta: typeMeta("Namespace", "v1"),
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
}
return ns
}
54 changes: 54 additions & 0 deletions pkg/pipelines/namespaces_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package pipelines

import (
"testing"

"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestCreateNamespace(t *testing.T) {
ns := createNamespace("test-environment")
want := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
Kind: "Namespace",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-environment",
},
}

if diff := cmp.Diff(want, ns); diff != "" {
t.Fatalf("createNamespace() failed got\n%s", diff)
}
}

func TestNamespaceNames(t *testing.T) {
ns := namespaceNames("test-")
want := map[string]string{
"dev": "test-dev-environment",
"stage": "test-stage-environment",
"cicd": "test-cicd-environment",
}
if diff := cmp.Diff(want, ns); diff != "" {
t.Fatalf("namespaceNames() failed got\n%s", diff)
}
}

func TestCreateNamespaces(t *testing.T) {
ns := createNamespaces([]string{
"test-dev-environment",
"test-stage-environment",
"test-cicd-environment",
})
want := []*corev1.Namespace{
createNamespace("test-dev-environment"),
createNamespace("test-stage-environment"),
createNamespace("test-cicd-environment"),
}
if diff := cmp.Diff(want, ns); diff != "" {
t.Fatalf("createNamespaces() failed got\n%s", diff)
}
}
25 changes: 6 additions & 19 deletions pkg/pipelines/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,35 @@ import (
"strings"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apitypes "k8s.io/apimachinery/pkg/types"
)

// createOpaqueSecret creates a Kubernetes v1/Secret with the provided name and
// body, and type Opaque.
func createOpaqueSecret(name, data string) (*corev1.Secret, error) {
func createOpaqueSecret(name apitypes.NamespacedName, data string) (*corev1.Secret, error) {
r := strings.NewReader(data)

return createSecret(name, "token", corev1.SecretTypeOpaque, r)
}

// createDockerConfigSecret creates a Kubernetes v1/Secret with the provided name and
// body, and type DockerConfigJson.
func createDockerConfigSecret(name string, in io.Reader) (*corev1.Secret, error) {
func createDockerConfigSecret(name apitypes.NamespacedName, in io.Reader) (*corev1.Secret, error) {
return createSecret(name, ".dockerconfigjson", corev1.SecretTypeDockerConfigJson, in)
}

func createSecret(name, key string, st corev1.SecretType, in io.Reader) (*corev1.Secret, error) {
func createSecret(name apitypes.NamespacedName, key string, st corev1.SecretType, in io.Reader) (*corev1.Secret, error) {
data, err := ioutil.ReadAll(in)
if err != nil {
return nil, fmt.Errorf("failed to read secret data: %w", err)
}
secret := &corev1.Secret{
TypeMeta: createTypeMeta("Secret", "v1"),
ObjectMeta: createObjectMeta(name),
TypeMeta: typeMeta("Secret", "v1"),
ObjectMeta: objectMeta(name),
Type: st,
Data: map[string][]byte{
key: data,
},
}
return secret, nil
}

func createTypeMeta(kind, apiVersion string) metav1.TypeMeta {
return metav1.TypeMeta{
Kind: kind,
APIVersion: apiVersion,
}
}

func createObjectMeta(name string) metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: name,
}
}
12 changes: 7 additions & 5 deletions pkg/pipelines/secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

func TestCreateOpaqueSecret(t *testing.T) {
data := "abcdefghijklmnop"
secret, err := createOpaqueSecret("github-auth", data)
secret, err := createOpaqueSecret(namespacedName("cicd", "github-auth"), data)
if err != nil {
t.Fatal(err)
}
Expand All @@ -24,7 +24,8 @@ func TestCreateOpaqueSecret(t *testing.T) {
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "github-auth",
Name: "github-auth",
Namespace: "cicd",
},
Type: corev1.SecretTypeOpaque,
Data: map[string][]byte{
Expand All @@ -39,15 +40,15 @@ func TestCreateOpaqueSecret(t *testing.T) {

func TestCreateDockerConfigSecretWithErrorReading(t *testing.T) {
testErr := errors.New("test failure")
_, err := createDockerConfigSecret("github-auth", errorReader{testErr})
_, err := createDockerConfigSecret(namespacedName("cici", "github-auth"), errorReader{testErr})
if !matchError(t, "failed to read .* test failure", err) {
t.Fatalf("got an unexpected error: %#v", err)
}
}

func TestCreateDockerConfigSecret(t *testing.T) {
data := []byte(`abcdefghijklmnop`)
secret, err := createDockerConfigSecret("regcred", bytes.NewReader(data))
secret, err := createDockerConfigSecret(namespacedName("cicd", "regcred"), bytes.NewReader(data))
if err != nil {
t.Fatal(err)
}
Expand All @@ -58,7 +59,8 @@ func TestCreateDockerConfigSecret(t *testing.T) {
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "regcred",
Name: "regcred",
Namespace: "cicd",
},
Type: corev1.SecretTypeDockerConfigJson,
Data: map[string][]byte{
Expand Down
Loading