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

Add support for namespaces #219

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
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
41 changes: 40 additions & 1 deletion installer/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion installer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,20 @@ After running the above command, `sc` should get installed in your GOPATH/bin di
```
- To add the Service Broker to the Service Catalog, run
```bash
sc add-gcp-broker
sc add-gcp-broker
```
By default it is created in global context as a ClusterServiceBroker,
secrets and oauth deployment are installed in "google-auth" namespace.
This behaviour can be changed using `--context` and `--namespace` flags.
If you want to install GCP Service Broker and all dependencies in 'demo' namespace run:
```bash
sc add-gcp-broker --namespace demo --context namespace
```
- To remove the Service Broker from the Service Catalog, run
```bash
sc remove-gcp-broker
```
This command accepts the same set of flags as add-gcp-broker

## Build

Expand Down
108 changes: 88 additions & 20 deletions installer/pkg/cmd/gcp_broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ const (
brokerSARole = "roles/servicebroker.operator"
gcpBrokerTemplateDir = "templates/gcp/"
gcpBrokerDeprecatedTemplateDir = "templates/gcp-deprecated/"
gcpBrokerDefaultNamespace = "google-auth"
)

var (
gcpBrokerDeprecatedFileNames = []string{"google-oauth-deployment", "service-account-secret"}
gcpBrokerFileNames = []string{"namespace", "gcp-broker", "google-oauth-deployment", "service-account-secret", "google-oauth-rbac", "google-oauth-service-account"}
gcpBrokerFileNames = []string{"namespace", "google-oauth-deployment", "service-account-secret", "google-oauth-rbac", "google-oauth-service-account", "gcp-broker"}

requiredAPIs = []string{
"servicebroker.googleapis.com",
Expand Down Expand Up @@ -95,23 +96,50 @@ var (
}
)

// AddBrokerConfig contains installation configuration.
type AddBrokerConfig struct {
// namespace for gcp broker
Namespace string

// scope of installation (cluster or namespace)
Scope string
}

type RemoveBrokerConfig struct {
// namespace for gcp broker
Namespace string

// scope of installation (cluster or namespace)
Scope string
SkipDeprecated bool
SkipGCPIntegration bool
}

func NewAddGCPBrokerCmd() *cobra.Command {
return &cobra.Command{
bc := &AddBrokerConfig{}

c := &cobra.Command{
Use: "add-gcp-broker",
Short: "Adds the Service Broker",
Long: `Adds Google Cloud Platfrom Service Broker to Service Catalog`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := addGCPBroker(); err != nil {
if err := addGCPBroker(bc); err != nil {
fmt.Println("Failed to configure the Service Broker")
return err
}
fmt.Println("The Service Broker has been added successfully.")
return nil
},
}

// add gcp-broker command flags
c.Flags().StringVar(&bc.Namespace, "namespace", gcpBrokerDefaultNamespace, "Namespace for the GCP broker")
c.Flags().StringVar(&bc.Scope, "scope", "cluster", "Scope of GCP broker: cluster or namespace")

return c
}

func addGCPBroker() error {
func addGCPBroker(bc *AddBrokerConfig) error {
projectID, err := gcp.GetConfigValue("core", "project")
if err != nil {
return fmt.Errorf("error getting configured project value : %v", err)
Expand All @@ -123,7 +151,7 @@ func addGCPBroker() error {
return err
}

brokerSAName, err := constructSAName()
brokerSAName, err := constructSAName(bc.Namespace)
if err != nil {
return fmt.Errorf("error constructing service account name: %v", err)
}
Expand Down Expand Up @@ -167,6 +195,8 @@ func addGCPBroker() error {
data := map[string]interface{}{
"SvcAccountKey": key,
"GCPBrokerURL": vb.URL,
"Namespace": bc.Namespace,
"Scope": bc.Scope,
}

// generate config files and deploy the GCP broker resources
Expand All @@ -190,7 +220,7 @@ func addGCPBroker() error {
func enableRequiredAPIs(projectID string) error {
if err := gcp.EnableAPIs(requiredAPIs); err != nil {
var b bytes.Buffer
fmt.Fprintln(&b, "error enabling APIs. To make sure all APIs are correctly enabled, use links below:")
fmt.Fprintf(&b, "error enabling APIs: %v\nTo make sure all APIs are correctly enabled, use links below:\n", err)
for _, a := range requiredAPIs {
fmt.Fprintf(&b, " %s: https://console.cloud.google.com/apis/library/%s/?project=%s\n", a, a, projectID)
}
Expand All @@ -205,7 +235,7 @@ func enableRequiredAPIs(projectID string) error {
return nil
}

func constructSAName() (string, error) {
func constructSAName(namespace string) (string, error) {
bout, err := exec.Command("kubectl", "config", "view", "--output", "json").CombinedOutput()
if err != nil {
return "", fmt.Errorf("error retriving kubernetes config: %s : %v", string(bout), err)
Expand All @@ -226,7 +256,7 @@ func constructSAName() (string, error) {
// Hash the cluster name using MD5 algorithm.
// This is because SA name only allows a maximum of 30 characters, so we need to reduce the length
// of the cluster name.
md5res := md5.Sum([]byte(fcn))
md5res := md5.Sum([]byte(fcn + namespace))
var md5bytes []byte = md5res[:]

// Use base32 to encode the MD5 hash result.
Expand All @@ -235,7 +265,7 @@ func constructSAName() (string, error) {
res := base32.StdEncoding.EncodeToString(md5bytes)

// Remove the last six "="s.
// The raw result of MD5 hash is 16 bytes, so base32 encoding result will alway have a padding
// The raw result of MD5 hash is 16 bytes, so base32 encoding result will always have a padding
// "======".
// This step can be replaced by base32.StdEncoding.WithPadding(base32.NoPadding) in Golang 1.9.
res = strings.Trim(res, "=")
Expand Down Expand Up @@ -372,32 +402,52 @@ func cleanupNewKey(email, key string) {
}

func NewRemoveGCPBrokerCmd() *cobra.Command {
return &cobra.Command{
bc := &RemoveBrokerConfig{}

c := &cobra.Command{
Use: "remove-gcp-broker",
Short: "Remove the Service Broker",
Long: `Removes Google Cloud Platform Service Broker from service catalog`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := removeGCPBroker(); err != nil {
if err := removeGCPBroker(bc); err != nil {
fmt.Println("Failed to remove the Service Broker")
return err
}
fmt.Println("The Service Broker removed successfully.")
return nil
},
}

// add gcp-broker command flags
c.Flags().StringVar(&bc.Namespace, "namespace", gcpBrokerDefaultNamespace, "Namespace for the GCP broker or only for secrets (in cluster mode)")
c.Flags().StringVar(&bc.Scope, "scope", "cluster", "Scope of GCP broker: cluster or namespace")
c.Flags().BoolVar(&bc.SkipDeprecated, "skip-deprecated", false, "Skip deletion of deprecated resources")
c.Flags().BoolVar(&bc.SkipGCPIntegration, "skip-gcp-integration", false, "Skip deletion of GCP resources")

return c
}

func removeGCPBroker() error {
func removeGCPBroker(bc *RemoveBrokerConfig) error {
// Create temporary directory for k8s artifacts and other temporary files.
dir, err := ioutil.TempDir("/tmp", "service-catalog-gcp")
if err != nil {
return fmt.Errorf("error creating temporary dir: %v", err)
}

defer os.RemoveAll(dir)
data := map[string]interface{}{
"Namespace": bc.Namespace,
"Scope": bc.Scope,
}

//remove namespace template from the list if it's not the default one
if bc.Namespace != gcpBrokerDefaultNamespace {
i := 0 //position of the "namespace.tpl"
gcpBrokerFileNames = append(gcpBrokerFileNames[:i], gcpBrokerFileNames[i+1:]...)
}

// remove GCP Broker k8s resources
err = generateConfigs(dir, gcpBrokerTemplateDir, gcpBrokerFileNames, nil)
err = generateConfigs(dir, gcpBrokerTemplateDir, gcpBrokerFileNames, data)
if err != nil {
return fmt.Errorf("error generating configs for the Service Broker: %v", err)
}
Expand All @@ -410,17 +460,23 @@ func removeGCPBroker() error {
// due to moving the google-oauth resources to a separate namespace, we
// must also remove deprecated Service Broker k8s resources for backwards
// compatibility
err = removeDeprecatedGCPBrokerResources()
if err != nil {
return fmt.Errorf("error deleting broker resources : %v", err)
if !bc.SkipDeprecated {
err = removeDeprecatedGCPBrokerResources()
if err != nil {
return fmt.Errorf("error deleting broker resources : %v", err)
}
}

if bc.SkipGCPIntegration {
return nil
}

projectID, err := gcp.GetConfigValue("core", "project")
if err != nil {
return fmt.Errorf("error getting configured project value : %v", err)
}

brokerSAName, err := constructSAName()
brokerSAName, err := constructSAName(bc.Namespace)
if err != nil {
return fmt.Errorf("error constructing service account name: %v", err)
}
Expand All @@ -441,11 +497,20 @@ func removeGCPBroker() error {
return nil
}

// Remove the Service Broker Operator role.
err = gcp.RemoveServiceAccountPerms(projectID, brokerSAEmail, brokerSARole)
// Check if RoleBinding is present
bindingExists, err := gcp.CheckIfBindingExists(projectID, brokerSAEmail)
if err != nil {
return err
}
if bindingExists {
// Remove the Service Broker Operator role.
err = gcp.RemoveServiceAccountPerms(projectID, brokerSAEmail, brokerSARole)
if err != nil {
return err
}
} else {
fmt.Println("WARNING: There are no service account permissions to remove")
}

// Clean up all the associated keys.
err = gcp.RemoveAllServiceAccountKeys(brokerSAEmail)
Expand Down Expand Up @@ -491,6 +556,7 @@ func generateConfigs(genDir, templateDir string, filenames []string, data map[st

func deployConfigs(dir string, filenames []string) error {
for _, f := range filenames {
fmt.Printf("Creating k8s resource using '%v' template\n", f)
output, err := exec.Command("kubectl", "apply", "-f", filepath.Join(dir, f+".yaml")).CombinedOutput()
// TODO: cleanup
if err != nil {
Expand All @@ -501,7 +567,9 @@ func deployConfigs(dir string, filenames []string) error {
}

func removeConfigs(dir string, filenames []string) error {
for _, f := range filenames {
for i := len(filenames) - 1; i >= 0; i-- {
f := filenames[i]
fmt.Printf("Removing k8s resource using '%v' template\n", f)
output, err := exec.Command("kubectl", "delete", "-f", filepath.Join(dir, f+".yaml"), "--ignore-not-found").CombinedOutput()
// TODO: cleanup
if err != nil {
Expand Down
6 changes: 4 additions & 2 deletions installer/pkg/cmd/service_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/GoogleCloudPlatform/k8s-service-catalog/installer/pkg/gcp"
"github.com/GoogleCloudPlatform/k8s-service-catalog/installer/pkg/version"
"github.com/Masterminds/semver"
"github.com/Masterminds/sprig"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -352,7 +353,8 @@ func generateFileFromTmpl(dst, src string, data map[string]interface{}) error {
if err != nil {
return err
}
tp, err := template.New("").Parse(string(b))

tp, err := template.New("").Funcs(sprig.FuncMap()).Parse(string(b))
if err != nil {
return err
}
Expand Down Expand Up @@ -506,7 +508,7 @@ func NewCheckDependenciesCmd() *cobra.Command {
return &cobra.Command{
Use: "check",
Short: "performs a dependency check",
Long: `This utility requires cfssl, gcloud, kubectl binaries to be
Long: `This utility requires cfssl, gcloud, kubectl binaries to be
present in PATH. This command performs the dependency check.`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkDependencies(); err != nil {
Expand Down
Loading