diff --git a/fission/common.go b/fission/common.go new file mode 100644 index 00000000..e2bbfec2 --- /dev/null +++ b/fission/common.go @@ -0,0 +1,44 @@ +/* +Copyright 2016 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "os" + + "github.com/platform9/fission/controller/client" +) + +func fatal(msg string) { + os.Stderr.WriteString(msg + "\n") + os.Exit(1) +} + +func getClient(serverUrl string) *client.Client { + + if len(serverUrl) == 0 { + fatal("Need --server or FISSION_URL set to your fission server.") + } + + return client.MakeClient(serverUrl) +} + +func checkErr(err error, msg string) { + if err != nil { + fatal(fmt.Sprintf("Failed to %v: %v", msg, err)) + } +} diff --git a/fission/deployment.go b/fission/deployment.go new file mode 100644 index 00000000..a98319ba --- /dev/null +++ b/fission/deployment.go @@ -0,0 +1,181 @@ +/* +Copyright 2016 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import "fmt" + +func getDeploymentYaml() { + fmt.Printf(` +apiVersion: v1 +kind: Namespace +metadata: + name: fission + labels: + name: fission + +--- +apiVersion: v1 +kind: Namespace +metadata: + name: fission-function + labels: + name: fission-function + +--- +apiVersion: v1 +kind: Service +metadata: + name: controller + namespace: fission + labels: + svc: controller +spec: + type: NodePort + ports: + - port: 80 + targetPort: 8888 + nodePort: 31313 + selector: + svc: controller + +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: controller + namespace: fission +spec: + replicas: 1 + template: + metadata: + labels: + svc: controller + spec: + containers: + - name: controller + image: fission/fission-bundle + command: ["/fission-bundle"] + args: ["--controllerPort", "8888", "--filepath", "/tmp"] + +--- +apiVersion: v1 +kind: Service +metadata: + name: router + namespace: fission + labels: + svc: router +spec: + type: NodePort + ports: + - port: 80 + targetPort: 8888 + nodePort: 31314 + selector: + svc: router + +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: router + namespace: fission +spec: + replicas: 1 + template: + metadata: + labels: + svc: router + spec: + containers: + - name: router + image: fission/fission-bundle + command: ["/fission-bundle"] + args: ["--routerPort", "8888"] + +--- +apiVersion: v1 +kind: Service +metadata: + name: poolmgr + namespace: fission + labels: + svc: poolmgr +spec: + ports: + - port: 80 + targetPort: 8888 + selector: + svc: poolmgr + +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: poolmgr + namespace: fission +spec: + replicas: 1 + template: + metadata: + labels: + svc: poolmgr + spec: + containers: + - name: poolmgr + image: fission/fission-bundle + command: ["/fission-bundle"] + args: ["--poolmgrPort", "8888"] + +--- +apiVersion: v1 +kind: Service +metadata: + name: etcd + namespace: fission + labels: + svc: etcd +spec: + ports: + - port: 2379 + targetPort: 2379 + selector: + svc: etcd + +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: etcd + namespace: fission +spec: + replicas: 1 + template: + metadata: + labels: + svc: etcd + spec: + containers: + - name: etcd + image: quay.io/coreos/etcd + env: + - name: ETCD_LISTEN_CLIENT_URLS + value: http://0.0.0.0:2379 + - name: ETCD_ADVERTISE_CLIENT_URLS + value: http://etcd:2379 +`) +} diff --git a/fission/environment.go b/fission/environment.go new file mode 100644 index 00000000..24186908 --- /dev/null +++ b/fission/environment.go @@ -0,0 +1,111 @@ +/* +Copyright 2016 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "text/tabwriter" + + "github.com/urfave/cli" + + "github.com/platform9/fission" + "os" +) + +func envCreate(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + envName := c.String("name") + envImg := c.String("image") + + env := &fission.Environment{ + Metadata: fission.Metadata{ + Name: envName, + }, + RunContainerImageUrl: envImg, + } + + _, err := client.EnvironmentCreate(env) + checkErr(err, "create environment") + + fmt.Printf("environment '%v' created\n", envName) + return err +} + +func envGet(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + envName := c.String("name") + m := &fission.Metadata{Name: envName} + env, err := client.EnvironmentGet(m) + checkErr(err, "get environment") + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) + fmt.Fprintf(w, "%v\t%v\t%v\n", "NAME", "UID", "IMAGE") + fmt.Fprintf(w, "%v\t%v\t%v\n", + env.Metadata.Name, env.Metadata.Uid, env.RunContainerImageUrl) + w.Flush() + return nil +} + +func envUpdate(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + envName := c.String("name") + image := c.String("image") + env := &fission.Environment{ + Metadata: fission.Metadata{ + Name: envName, + }, + RunContainerImageUrl: image, + } + + _, err := client.EnvironmentUpdate(env) + checkErr(err, "update environment") + + fmt.Printf("environment '%v' updated\n", envName) + return nil +} + +func envDelete(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + envName := c.String("name") + m := &fission.Metadata{Name: envName} + err := client.EnvironmentDelete(m) + checkErr(err, "delete environment") + + fmt.Printf("environment '%v' deleted\n", envName) + return nil +} + +func envList(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + envs, err := client.EnvironmentList() + checkErr(err, "list environments") + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) + fmt.Fprintf(w, "%v\t%v\t%v\n", "NAME", "UID", "IMAGE") + for _, env := range envs { + fmt.Fprintf(w, "%v\t%v\t%v\n", + env.Metadata.Name, env.Metadata.Uid, env.RunContainerImageUrl) + } + w.Flush() + + return nil +} diff --git a/fission/function.go b/fission/function.go new file mode 100644 index 00000000..e54e0a00 --- /dev/null +++ b/fission/function.go @@ -0,0 +1,143 @@ +/* +Copyright 2016 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "text/tabwriter" + + "github.com/urfave/cli" + + "github.com/platform9/fission" +) + +func fnCreate(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + fnName := c.String("name") + envName := c.String("env") + + fileName := c.String("code") + code, err := ioutil.ReadFile(fileName) + checkErr(err, fmt.Sprintf("read %v", fileName)) + + function := &fission.Function{ + Metadata: fission.Metadata{Name: fnName}, + Environment: fission.Metadata{Name: envName}, + Code: string(code), + } + + _, err = client.FunctionCreate(function) + checkErr(err, "create function") + + fmt.Printf("function '%v' created\n", fnName) + return err +} + +func fnGet(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + fnName := c.String("name") + fnUid := c.String("uid") + m := &fission.Metadata{Name: fnName, Uid: fnUid} + + code, err := client.FunctionGetRaw(m) + checkErr(err, "get function") + + fmt.Println(string(code)) + return err +} + +func fnGetMeta(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + fnName := c.String("name") + fnUid := c.String("uid") + m := &fission.Metadata{Name: fnName, Uid: fnUid} + + f, err := client.FunctionGet(m) + checkErr(err, "get function") + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) + fmt.Fprintf(w, "%v\t%v\t%v\n", "NAME", "UID", "ENV") + fmt.Fprintf(w, "%v\t%v\t%v\n", + f.Metadata.Name, f.Metadata.Uid, f.Environment.Name) + w.Flush() + return err +} + +func fnUpdate(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + fnName := c.String("name") + + function, err := client.FunctionGet(&fission.Metadata{Name: fnName}) + checkErr(err, fmt.Sprintf("read function '%v'", fnName)) + + envName := c.String("env") + if len(envName) > 0 { + function.Environment.Name = envName + } + + fileName := c.String("code") + if len(fileName) > 0 { + code, err := ioutil.ReadFile(fileName) + checkErr(err, fmt.Sprintf("read %v", fileName)) + + function.Code = string(code) + } + + _, err = client.FunctionCreate(function) + checkErr(err, "update function") + + fmt.Printf("function '%v' created\n", fnName) + return err +} + +func fnDelete(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + fnName := c.String("name") + fnUid := c.String("uid") + m := &fission.Metadata{Name: fnName, Uid: fnUid} + + err := client.FunctionDelete(m) + checkErr(err, fmt.Sprintf("delete function '%v'", fnName)) + + fmt.Printf("function '%v' deleted\n", fnName) + return err +} + +func fnList(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + fns, err := client.FunctionList() + checkErr(err, "list functions") + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) + + fmt.Fprintf(w, "%v\t%v\t%v\n", "NAME", "UID", "ENV") + for _, f := range fns { + fmt.Fprintf(w, "%v\t%v\t%v\n", + f.Metadata.Name, f.Metadata.Uid, f.Environment.Name) + } + w.Flush() + + return err +} diff --git a/fission/httptrigger.go b/fission/httptrigger.go new file mode 100644 index 00000000..2ff30599 --- /dev/null +++ b/fission/httptrigger.go @@ -0,0 +1,135 @@ +/* +Copyright 2016 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "net/http" + "os" + "strings" + "text/tabwriter" + + "github.com/satori/go.uuid" + "github.com/urfave/cli" + + "github.com/platform9/fission" +) + +// returns one of http.Method* +func getMethod(method string) string { + switch strings.ToUpper(method) { + case "GET": + return http.MethodGet + case "HEAD": + return http.MethodHead + case "POST": + return http.MethodPost + case "PUT": + return http.MethodPut + case "PATCH": + return http.MethodPatch + case "DELETE": + return http.MethodDelete + case "CONNECT": + return http.MethodConnect + case "OPTIONS": + return http.MethodOptions + case "TRACE": + return http.MethodTrace + } + fatal(fmt.Sprintf("Invalid HTTP Method %v", method)) + return "" +} + +func htCreate(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + fnName := c.String("function") + if len(fnName) == 0 { + fatal("Need a function name to create a trigger, use --function") + } + fnUid := c.String("uid") + triggerUrl := c.String("url") + if len(triggerUrl) == 0 { + fatal("Need a trigger URL, use --url") + } + method := c.String("method") + if len(method) == 0 { + method = "GET" + } + + // just name triggers by uuid. + triggerName := uuid.NewV4().String() + + ht := &fission.HTTPTrigger{ + Metadata: fission.Metadata{ + Name: triggerName, + }, + UrlPattern: triggerUrl, + Method: getMethod(method), + Function: fission.Metadata{ + Name: fnName, + Uid: fnUid, + }, + } + + _, err := client.HTTPTriggerCreate(ht) + checkErr(err, "create HTTP trigger") + + fmt.Printf("trigger '%v' created\n", triggerName) + return err +} + +func htGet(c *cli.Context) error { + return nil +} + +func htUpdate(c *cli.Context) error { + return nil +} + +func htDelete(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + htName := c.String("name") + if len(htName) == 0 { + fatal("Need name of trigger to delete, use --name") + } + + err := client.HTTPTriggerDelete(&fission.Metadata{Name: htName}) + checkErr(err, "delete trigger") + + fmt.Printf("trigger '%v' deleted\n", htName) + return nil +} + +func htList(c *cli.Context) error { + client := getClient(c.GlobalString("server")) + + hts, err := client.HTTPTriggerList() + checkErr(err, "list HTTP triggers") + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) + + fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\n", "NAME", "METHOD", "URL", "FUNCTION_NAME", "FUNCTION_UID") + for _, ht := range hts { + fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\n", + ht.Metadata.Name, ht.Method, ht.UrlPattern, ht.Function.Name, ht.Function.Uid) + } + w.Flush() + + return nil +} diff --git a/fission/main.go b/fission/main.go new file mode 100644 index 00000000..414ff909 --- /dev/null +++ b/fission/main.go @@ -0,0 +1,87 @@ +/* +Copyright 2016 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "os" + + "github.com/urfave/cli" +) + +func main() { + app := cli.NewApp() + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "server", Usage: "Fission server URL", EnvVar: "FISSION_URL"}, + } + + // functions + fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"} + fnEnvNameFlag := cli.StringFlag{Name: "env", Usage: "environment name for function"} + fnCodeFlag := cli.StringFlag{Name: "code", Usage: "file containing source code, or - for stdin"} + fnUidFlag := cli.StringFlag{Name: "uid", Usage: "function uid, optional (use latest if unspecified)"} + fnSubcommands := []cli.Command{ + {Name: "create", Usage: "Create new function", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag}, Action: fnCreate}, + {Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGet}, + {Name: "getmeta", Usage: "Get function metadata", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGetMeta}, + {Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag}, Action: fnUpdate}, + {Name: "delete", Usage: "Delete function", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnDelete}, + {Name: "list", Usage: "List all functions", Flags: []cli.Flag{}, Action: fnList}, + } + + // httptriggers + htNameFlag := cli.StringFlag{Name: "name", Usage: "HTTP Trigger name"} + htMethodFlag := cli.StringFlag{Name: "method", Usage: "HTTP Method: GET|POST|PUT|DELETE|HEAD; defaults to GET"} + htUrlFlag := cli.StringFlag{Name: "url", Usage: "URL pattern (see TODO for supported patterns)"} + htFnNameFlag := cli.StringFlag{Name: "function", Usage: "Function name"} + htFnUidFlag := cli.StringFlag{Name: "uid", Usage: "Function UID (optional; uses latest if unspecified)"} + htSubcommands := []cli.Command{ + {Name: "create", Usage: "Create HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag, htFnNameFlag, htFnUidFlag}, Action: htCreate}, + {Name: "get", Usage: "Get HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag}, Action: htGet}, + {Name: "update", Usage: "Update HTTP trigger", Flags: []cli.Flag{htMethodFlag, htUrlFlag, htFnNameFlag, htFnUidFlag}, Action: htUpdate}, + {Name: "delete", Usage: "Delete HTTP trigger", Flags: []cli.Flag{htNameFlag}, Action: htDelete}, + {Name: "list", Usage: "List HTTP triggers", Flags: []cli.Flag{}, Action: htList}, + } + + // environments + envNameFlag := cli.StringFlag{Name: "name", Usage: "Environment name"} + envImageFlag := cli.StringFlag{Name: "image", Usage: "Environment image URL"} + envSubcommands := []cli.Command{ + {Name: "create", Aliases: []string{"add"}, Usage: "Add an environment", Flags: []cli.Flag{envNameFlag, envImageFlag}, Action: envCreate}, + {Name: "get", Usage: "Get environment details", Flags: []cli.Flag{envNameFlag}, Action: envGet}, + {Name: "update", Usage: "Update environment", Flags: []cli.Flag{envNameFlag, envImageFlag}, Action: envUpdate}, + {Name: "delete", Usage: "Delete environment", Flags: []cli.Flag{envNameFlag}, Action: envDelete}, + {Name: "list", Usage: "List all environments", Flags: []cli.Flag{}, Action: envList}, + } + + app.Commands = []cli.Command{ + {Name: "function", Aliases: []string{"fn"}, Usage: "Create, update and manage functions", Subcommands: fnSubcommands}, + {Name: "httptrigger", Aliases: []string{"ht", "route"}, Usage: "Manage HTTP triggers (routes) for functions", Subcommands: htSubcommands}, + {Name: "environemnt", Aliases: []string{"env"}, Usage: "Manage environments", Subcommands: envSubcommands}, + + // Misc commands + { + Name: "get-deployment-yaml", + Usage: "Get deployment yaml. Use it as 'fission get-deployment-yaml | kubectl create -f -'", + Action: func(c *cli.Context) error { + getDeploymentYaml() + return nil + }, + }, + } + + app.Run(os.Args) +}