Skip to content

Commit

Permalink
Implement "kubedb init" (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mir Shahriar authored and tamalsaha committed May 23, 2017
1 parent f996970 commit 6307aeb
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs/kubedb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ Find more information at https://github.com/k8sdb/kubedb.

Basic Commands (Beginner):
create Create a resource by filename or stdin
init Create or upgrade unified operator

Basic Commands (Intermediate):
get Display one or many resources
edit Edit a resource on the server
delete Delete resources by file names, stdin, resources and names, or by resources and label selector
delete Delete resources by filenames, stdin, resources and names, or by resources and label selector

Troubleshooting and Debugging Commands:
describe Show details of a specific resource or group of resources
Expand Down
42 changes: 42 additions & 0 deletions docs/kubedb/init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# kubedb init

## Example

##### Help for init command

```bash
$ kubedb init --help

Create or upgrade unified operator for k8sdb databases.

Examples:
# Create operator with version canary.
kubedb init --version=canary

# Upgrade operator to use another version.
kubedb init --version=canary --upgrade

Options:
-n, --namespace='default': Namespace name. Operator will be deployed in this namespace.
--upgrade=false: If present, Upgrade operator to use provided version
--version='': Operator version

Usage:
kubedb init [flags] [options]

Use "kubedb init options" for a list of global command-line options (applies to all commands).
```

##### Create
```bash
$ kubedb init --version=canary

Successfully created operator deployment.
```

##### Upgrade
```bash
$ kubedb init --version=canary --upgrade

Successfully upgraded operator deployment.
```
1 change: 1 addition & 0 deletions pkg/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func NewKubedbCommand(in io.Reader, out, err io.Writer) *cobra.Command {
Message: "Basic Commands (Beginner):",
Commands: []*cobra.Command{
NewCmdCreate(out, err),
NewCmdInit(out, err),
},
},
{
Expand Down
196 changes: 196 additions & 0 deletions pkg/cmd/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package cmd

import (
"fmt"
"io"
"strings"

"github.com/k8sdb/kubedb/pkg/cmd/util"
"github.com/k8sdb/kubedb/pkg/kube"
"github.com/spf13/cobra"
kapi "k8s.io/kubernetes/pkg/api"
k8serr "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
kext "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

var (
init_long = templates.LongDesc(`
Create or upgrade unified operator for k8sdb databases.`)

init_example = templates.Examples(`
# Create operator with version canary.
kubedb init --version=canary
# Upgrade operator to use another version.
kubedb init --version=canary --upgrade`)
)

func NewCmdInit(out io.Writer, errOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "init",
Short: "Create or upgrade unified operator",
Long: init_long,
Example: init_example,
Run: func(cmd *cobra.Command, args []string) {
f := kube.NewKubeFactory(cmd)
cmdutil.CheckErr(RunInit(f, cmd, out, errOut))
},
}

util.AddInitFlags(cmd)
return cmd
}

const (
operatorName = "k8sdb-operator"
operatorImage = "k8sdb/operator"
operatorContainer = "operator"
)

func RunInit(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer) error {
upgrade := cmdutil.GetFlagBool(cmd, "upgrade")
namespace := cmdutil.GetFlagString(cmd, "namespace")
version := cmdutil.GetFlagString(cmd, "version")

f.RESTClient()
client, err := f.ClientSet()
if err != nil {
return err
}

if upgrade {
deployment, err := getOperatorDeployment(client, namespace)
if err != nil {
if k8serr.IsNotFound(err) {
message := fmt.Sprintf("Operator deployment \"%v\" not found.\n\n"+
"Create operator using following commnad:\n"+
"kubedb init --version=%v --namespace=%v", operatorName, version, namespace)

fmt.Fprintln(errOut, message)
return nil
}

return err
}

containers := deployment.Spec.Template.Spec.Containers
if len(containers) == 0 {
fmt.Fprintln(errOut, fmt.Sprintf(`Invalid operator deployment "%v"`, operatorName))
return nil
}

items := strings.Split(containers[0].Image, ":")

image := items[0]
tag := items[1]

if image != operatorImage {
fmt.Fprintln(errOut, fmt.Sprintf(`Operator image mismatch. Can't upgrade to version "%v"`, version))
return nil
}

if tag == version {
fmt.Fprintln(out, "Operator deployment is already using this version.")
return nil
}

if err := util.CheckDockerImageVersion(operatorImage, version); err != nil {
fmt.Fprintln(errOut, fmt.Sprintf(`Operator image %v:%v not found.`, operatorImage, version))
return nil
}

deployment.Spec.Template.Spec.Containers[0].Image = fmt.Sprintf("%v:%v", operatorImage, version)

if err := updateOperatorDeployment(client, deployment); err != nil {
return err
}

fmt.Fprintln(out, "Successfully upgraded operator deployment.")
} else {
var err error
if _, err = getOperatorDeployment(client, namespace); err == nil {
fmt.Fprintln(errOut, "Operator already exists.")
return nil
} else {
if !k8serr.IsNotFound(err) {
return err
}
}

if err := util.CheckDockerImageVersion(operatorImage, version); err != nil {
fmt.Fprintln(errOut, fmt.Sprintf(`Operator image %v:%v not found.`, operatorImage, version))
return nil
}

if err := createOperatorDeployment(client, namespace, version); err != nil {
return err
}

fmt.Fprintln(out, "Successfully created operator deployment.")
}

return nil
}

func getOperatorDeployment(client *internalclientset.Clientset, namespace string) (*kext.Deployment, error) {
return client.ExtensionsClient.Deployments(namespace).Get(operatorName)
}

func createOperatorDeployment(client *internalclientset.Clientset, namespace, version string) error {
label := map[string]string{
"run": operatorName,
}

deployment := &kext.Deployment{
ObjectMeta: kapi.ObjectMeta{
Name: operatorName,
Namespace: namespace,
},
Spec: kext.DeploymentSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: label,
},
Replicas: 1,
Template: kapi.PodTemplateSpec{
ObjectMeta: kapi.ObjectMeta{
Labels: label,
},
Spec: kapi.PodSpec{
Containers: []kapi.Container{
{
Name: operatorContainer,
Image: fmt.Sprintf("%v:%v", operatorImage, version),
Args: []string{
"run",
"--v=4",
},
Env: []kapi.EnvVar{
{
Name: "KUBEDB_OPERATOR_NAMESPACE",
ValueFrom: &kapi.EnvVarSource{
FieldRef: &kapi.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.namespace",
},
},
},
},
},
},
},
},
},
}

_, err := client.ExtensionsClient.Deployments(namespace).Create(deployment)
return err
}

func updateOperatorDeployment(client *internalclientset.Clientset, deployment *kext.Deployment) error {
_, err := client.ExtensionsClient.Deployments(deployment.Namespace).Update(deployment)
return err
}
24 changes: 24 additions & 0 deletions pkg/cmd/util/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package util

import (
"net/http"

docker "github.com/heroku/docker-registry-client/registry"
)

const (
registryUrl = "https://registry-1.docker.io/"
)

func CheckDockerImageVersion(repository, reference string) error {
registry := &docker.Registry{
URL: registryUrl,
Client: &http.Client{
Transport: docker.WrapTransport(http.DefaultTransport, registryUrl, "", ""),
},
Logf: docker.Quiet,
}

_, err := registry.Manifest(repository, reference)
return err
}
7 changes: 7 additions & 0 deletions pkg/cmd/util/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package util

import (
"github.com/spf13/cobra"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl/resource"
)

Expand Down Expand Up @@ -34,6 +35,12 @@ func AddEditFlags(cmd *cobra.Command) {
cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.")
}

func AddInitFlags(cmd *cobra.Command) {
cmd.Flags().StringP("namespace", "n", kapi.NamespaceDefault, "Namespace name. Operator will be deployed in this namespace.")
cmd.Flags().String("version", "canary", "Operator version")
cmd.Flags().Bool("upgrade", false, "If present, Upgrade operator to use provided version")
}

func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions) {
cmd.Flags().StringSliceVarP(&options.Filenames, "filename", "f", options.Filenames, "Filename to use to create the resource")
cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively.")
Expand Down

0 comments on commit 6307aeb

Please sign in to comment.