diff --git a/cli/cmds/cluster/cluster.go b/cli/cmds/cluster/cluster.go new file mode 100644 index 00000000..d8146a54 --- /dev/null +++ b/cli/cmds/cluster/cluster.go @@ -0,0 +1,27 @@ +package cluster + +import ( + "github.com/galal-hussein/k3k/cli/cmds" + "github.com/urfave/cli" +) + +var clusterSubcommands = []cli.Command{ + { + Name: "create", + Usage: "Create new cluster", + SkipFlagParsing: false, + SkipArgReorder: true, + Action: createCluster, + Flags: append(cmds.CommonFlags, clusterCreateFlags...), + }, +} + +func NewClusterCommand() cli.Command { + cmd := cli.Command{ + Name: "cluster", + Usage: "cluster command", + Subcommands: clusterSubcommands, + } + + return cmd +} diff --git a/cli/cmds/cluster/create.go b/cli/cmds/cluster/create.go new file mode 100644 index 00000000..3d897266 --- /dev/null +++ b/cli/cmds/cluster/create.go @@ -0,0 +1,151 @@ +package cluster + +import ( + "context" + "errors" + "os" + + "github.com/galal-hussein/k3k/cli/cmds" + "github.com/galal-hussein/k3k/pkg/apis/k3k.io/v1alpha1" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + Scheme = runtime.NewScheme() +) + +func init() { + _ = clientgoscheme.AddToScheme(Scheme) + _ = v1alpha1.AddToScheme(Scheme) +} + +var ( + name string + token string + clusterCIDR string + serviceCIDR string + servers int64 + agents int64 + serverArgs cli.StringSlice + agentArgs cli.StringSlice + + clusterCreateFlags = []cli.Flag{ + cli.StringFlag{ + Name: "name", + Usage: "name of the cluster", + Destination: &name, + }, + cli.Int64Flag{ + Name: "servers", + Usage: "number of servers", + Destination: &servers, + Value: 1, + }, + cli.Int64Flag{ + Name: "agents", + Usage: "number of agents", + Destination: &agents, + }, + cli.StringFlag{ + Name: "token", + Usage: "token of the cluster", + Destination: &token, + }, + cli.StringFlag{ + Name: "cluster-cidr", + Usage: "cluster CIDR", + Destination: &clusterCIDR, + }, + cli.StringFlag{ + Name: "service-cidr", + Usage: "service CIDR", + Destination: &serviceCIDR, + }, + cli.StringSliceFlag{ + Name: "server-args", + Usage: "servers extra arguments", + Value: &serverArgs, + }, + cli.StringSliceFlag{ + Name: "agent-args", + Usage: "agents extra arguments", + Value: &agentArgs, + }, + } +) + +func createCluster(clx *cli.Context) error { + ctx := context.Background() + if err := validateCreateFlags(clx); err != nil { + return err + } + + restConfig, err := clientcmd.BuildConfigFromFlags("", cmds.Kubeconfig) + if err != nil { + return err + } + + ctrlClient, err := client.New(restConfig, client.Options{ + Scheme: Scheme, + }) + if err != nil { + return err + } + logrus.Infof("creating a new cluster [%s]", name) + cluster := newCluster( + name, + token, + int32(servers), + int32(agents), + clusterCIDR, + serviceCIDR, + serverArgs, + agentArgs, + ) + + return ctrlClient.Create(ctx, cluster) +} + +func validateCreateFlags(clx *cli.Context) error { + if token == "" { + return errors.New("empty cluster token") + } + if name == "" { + return errors.New("empty cluster name") + } + if servers <= 0 { + return errors.New("invalid number of servers") + } + if cmds.Kubeconfig == "" && os.Getenv("KUBECONFIG") == "" { + return errors.New("empty kubeconfig") + } + return nil +} + +func newCluster(name, token string, servers, agents int32, clusterCIDR, serviceCIDR string, serverArgs, agentArgs []string) *v1alpha1.Cluster { + return &v1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Cluster", + APIVersion: "k3k.io/v1alpha1", + }, + Spec: v1alpha1.ClusterSpec{ + Name: name, + Token: token, + Servers: &servers, + Agents: &agents, + ClusterCIDR: clusterCIDR, + ServiceCIDR: serviceCIDR, + ServerArgs: serverArgs, + AgentArgs: agentArgs, + }, + } +} diff --git a/cli/cmds/cluster/delete.go b/cli/cmds/cluster/delete.go new file mode 100644 index 00000000..916b1b53 --- /dev/null +++ b/cli/cmds/cluster/delete.go @@ -0,0 +1 @@ +package cluster diff --git a/cli/cmds/root.go b/cli/cmds/root.go new file mode 100644 index 00000000..b57a193c --- /dev/null +++ b/cli/cmds/root.go @@ -0,0 +1,42 @@ +package cmds + +import ( + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var ( + debug bool + Kubeconfig string + CommonFlags = []cli.Flag{ + cli.StringFlag{ + Name: "kubeconfig", + EnvVar: "KUBECONFIG", + Usage: "Kubeconfig path", + Destination: &Kubeconfig, + }, + } +) + +func NewApp() *cli.App { + app := cli.NewApp() + app.Name = "k3kcli" + app.Usage = "CLI for K3K" + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug", + Usage: "Turn on debug logs", + Destination: &debug, + EnvVar: "K3K_DEBUG", + }, + } + + app.Before = func(clx *cli.Context) error { + if debug { + logrus.SetLevel(logrus.DebugLevel) + } + return nil + } + + return app +} diff --git a/cli/main.go b/cli/main.go new file mode 100644 index 00000000..3a20669c --- /dev/null +++ b/cli/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "os" + + "github.com/galal-hussein/k3k/cli/cmds" + "github.com/galal-hussein/k3k/cli/cmds/cluster" + "github.com/galal-hussein/k3k/pkg/version" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +func main() { + app := cmds.NewApp() + app.Commands = []cli.Command{ + cluster.NewClusterCommand(), + } + app.Version = version.Version + " (" + version.GitCommit + ")" + + if err := app.Run(os.Args); err != nil { + logrus.Fatal(err) + } +} diff --git a/go.mod b/go.mod index b75ffa2d..29f114c5 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/galal-hussein/k3k go 1.19 require ( + github.com/sirupsen/logrus v1.8.1 + github.com/urfave/cli v1.22.12 k8s.io/api v0.26.1 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 @@ -12,6 +14,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect @@ -34,7 +37,7 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sys v0.3.0 // indirect diff --git a/go.sum b/go.sum index 249ff13c..69509794 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,7 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -51,6 +52,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -244,6 +247,8 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua github.com/rancher/dynamiclistener v0.3.5 h1:5TaIHvkDGmZKvc96Huur16zfTKOiLhDtK4S+WV0JA6A= github.com/rancher/dynamiclistener v0.3.5/go.mod h1:dW/YF6/m2+uEyJ5VtEcd9THxda599HP6N9dSXk81+k0= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -254,12 +259,19 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= +github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/package/Dockerfile b/package/Dockerfile index aec50209..54097820 100644 --- a/package/Dockerfile +++ b/package/Dockerfile @@ -1,3 +1,4 @@ FROM alpine COPY bin/k3k /usr/bin/ +COPY bin/k3kcli /usr/bin/ CMD ["k3k"] diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 00000000..b229a9f9 --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,10 @@ +package version + +import "strings" + +var ( + Program = "k3k" + ProgramUpper = strings.ToUpper(Program) + Version = "dev" + GitCommit = "HEAD" +) diff --git a/scripts/build b/scripts/build index 1dca29ee..c20e8b88 100755 --- a/scripts/build +++ b/scripts/build @@ -1,5 +1,5 @@ #!/bin/bash -set -e +set -ex source $(dirname $0)/version @@ -16,3 +16,11 @@ if [ "$CROSS" = "true" ] && [ "$ARCH" = "amd64" ]; then GOOS=darwin go build -ldflags "$LINKFLAGS" -o bin/k3k-darwin GOOS=windows go build -ldflags "$LINKFLAGS" -o bin/k3k-windows fi + +# build k3kcli +CGO_ENABLED=0 go build -ldflags "$LINKFLAGS $OTHER_LINKFLAGS" -o bin/k3kcli ./cli +if [ "$CROSS" = "true" ] && [ "$ARCH" = "amd64" ]; then + GOOS=darwin go build -ldflags "$LINKFLAGS" -o bin/k3kcli-darwin ./cli + GOOS=windows go build -ldflags "$LINKFLAGS" -o bin/k3kcli-windows ./cli +fi + diff --git a/scripts/ci b/scripts/ci index 52334105..80ea182d 100755 --- a/scripts/ci +++ b/scripts/ci @@ -4,6 +4,7 @@ set -e cd $(dirname $0) ./build +./build-cli ./test ./validate ./validate-ci diff --git a/scripts/package b/scripts/package index a4dcd8cb..dbbe0e66 100755 --- a/scripts/package +++ b/scripts/package @@ -7,6 +7,7 @@ cd $(dirname $0)/.. mkdir -p dist/artifacts cp bin/k3k dist/artifacts/k3k${SUFFIX} +cp bin/k3kcli dist/artifacts/k3kcli${SUFFIX} IMAGE=${REPO}/k3k:${TAG} DOCKERFILE=package/Dockerfile