From 7ddbea59a2a644fc223a339fa96da5849ba62258 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Wed, 28 Feb 2018 18:14:57 -0800 Subject: [PATCH] Default values for FISSION_* env vars (#518) This makes set up easier for new users. Users can still set FISSION_NAMESPACE but it will default to "fission". Users can also still set KUBECONFIG, but it will default to $HOME/.kube/config. Also, update the post-install chart notes.txt and the install guide to use "fission function test" as the first step after install. This means that if there's anything wrong with the setup, the user will see useful errors instead of "internal server error". Also, they can test their setup without worrying about nodeports or ingresses or whatever. All existing functionality of FISSION_URL and FISSION_ROUTER continues to work. --- .../content/installation/installation.en.md | 37 +--------- charts/fission-all/templates/NOTES.txt | 42 ++--------- charts/fission-all/templates/deployment.yaml | 1 + charts/fission-all/templates/svc.yaml | 2 + charts/fission-core/templates/NOTES.txt | 42 ++--------- charts/fission-core/templates/deployment.yaml | 1 + charts/fission-core/templates/svc.yaml | 2 + fission/function.go | 28 ++------ fission/main.go | 30 ++++++-- fission/portforward.go | 71 +++++++++++-------- 10 files changed, 90 insertions(+), 166 deletions(-) diff --git a/Documentation/docs-site/content/installation/installation.en.md b/Documentation/docs-site/content/installation/installation.en.md index c671977a..7fa55c23 100644 --- a/Documentation/docs-site/content/installation/installation.en.md +++ b/Documentation/docs-site/content/installation/installation.en.md @@ -119,39 +119,6 @@ $ curl -Lo fission https://github.com/fission/fission/releases/download/0.5.0/fi For Windows, you can use the linux binary on WSL. Or you can download this windows executable: [fission.exe](https://github.com/fission/fission/releases/download/0.5.0/fission-cli-windows.exe) -### Set environment vars - -Set the FISSION_URL and FISSION_ROUTER environment variables. -FISSION_URL is used by the fission CLI to find the server. -(FISSION_ROUTER is only needed for the examples below to work.) - -#### Minikube - -If you're using minikube, use these commands: - -``` - $ export FISSION_URL=http://$(minikube ip):31313 - $ export FISSION_ROUTER=$(minikube ip):31314 -``` -#### Cloud setups - -Save the external IP addresses of controller and router services in -FISSION_URL and FISSION_ROUTER, respectively. Wait for services to -get IP addresses (check this with ```kubectl --namespace fission get -svc```). Then: - -##### AWS -``` - $ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..hostname}') - $ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..hostname}') -``` - -##### GCP -``` - $ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..ip}') - $ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}') -``` - ### Run an example Finally, you're ready to use Fission! @@ -163,9 +130,7 @@ $ curl -LO https://raw.githubusercontent.com/fission/fission/master/examples/nod $ fission function create --name hello --env nodejs --code hello.js -$ fission route create --method GET --url /hello --function hello - -$ curl http://$FISSION_ROUTER/hello +$ fission function test --name hello Hello, world! ``` diff --git a/charts/fission-all/templates/NOTES.txt b/charts/fission-all/templates/NOTES.txt index 713dcc59..4803a94a 100644 --- a/charts/fission-all/templates/NOTES.txt +++ b/charts/fission-all/templates/NOTES.txt @@ -9,47 +9,17 @@ Linux: Windows: For Windows, you can use the linux binary on WSL. Or you can download this windows executable: https://github.com/fission/fission/releases/download/0.5.0/fission-cli-windows.exe -2. Set the FISSION environment variables. - -{{- if contains "NodePort" .Values.serviceType }} - $ export FISSION_URL=http://$(minikube ip):{{ .Values.controllerPort }} - -{{- else if contains "LoadBalancer" .Values.serviceType }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl --namespace fission get -w svc' and 'kubectl --namespace fission get svc -w controller' - - $ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..ip}') - -{{- else if contains "ClusterIP" .Values.serviceType }} - $ export FISSION_NAMESPACE={{ .Release.Namespace }} - $ export KUBECONFIG=${HOME}/.kube/config - -{{- end }} - -{{- if contains "NodePort" .Values.routerServiceType }} - $ export FISSION_ROUTER=$(minikube ip):{{ .Values.routerPort }} - -{{- else if contains "LoadBalancer" .Values.routerServiceType }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status by running 'kubectl --namespace fission get -w svc' and 'kubectl --namespace fission get svc -w router' - $ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}') - -{{- else if contains "ClusterIP" .Values.routerServiceType }} - $ export KUBECONFIG=${HOME}/.kube/config - $ curl -Lo port-forward-router.sh https://github.com/fission/fission/port-forward-router.sh && chmod +x port-forward-router.sh && ./port-forward-router.sh {{ .Release.Namespace }} router 9999 - $ export FISSION_ROUTER=127.0.0.1:9999 - -{{- end }} - -3. Finally, you're ready to use Fission! +2. You're ready to use Fission! + # Create an environment $ fission env create --name nodejs --image fission/node-env + # Get a hello world $ curl https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js > hello.js + # Register this function with Fission $ fission function create --name hello --env nodejs --code hello.js - $ fission route create --method GET --url /hello --function hello - - $ curl http://$FISSION_ROUTER/hello + # Run this function + $ fission function test --name hello Hello, world! diff --git a/charts/fission-all/templates/deployment.yaml b/charts/fission-all/templates/deployment.yaml index b42bfeea..5a3e44c5 100644 --- a/charts/fission-all/templates/deployment.yaml +++ b/charts/fission-all/templates/deployment.yaml @@ -168,6 +168,7 @@ spec: template: metadata: labels: + application: fission-router svc: router spec: containers: diff --git a/charts/fission-all/templates/svc.yaml b/charts/fission-all/templates/svc.yaml index 16d54dda..114bdccc 100644 --- a/charts/fission-all/templates/svc.yaml +++ b/charts/fission-all/templates/svc.yaml @@ -4,6 +4,7 @@ metadata: name: router labels: svc: router + application: fission-router chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: type: {{ .Values.routerServiceType }} @@ -23,6 +24,7 @@ metadata: name: controller labels: svc: controller + application: fission-api chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: type: {{ .Values.serviceType }} diff --git a/charts/fission-core/templates/NOTES.txt b/charts/fission-core/templates/NOTES.txt index 0be2b2e8..4803a94a 100644 --- a/charts/fission-core/templates/NOTES.txt +++ b/charts/fission-core/templates/NOTES.txt @@ -9,47 +9,17 @@ Linux: Windows: For Windows, you can use the linux binary on WSL. Or you can download this windows executable: https://github.com/fission/fission/releases/download/0.5.0/fission-cli-windows.exe -2. Set the FISSION_URL and FISSION_ROUTER environment variables. - -{{- if contains "NodePort" .Values.serviceType }} - $ export FISSION_URL=http://$(minikube ip):{{ .Values.controllerPort }} - -{{- else if contains "LoadBalancer" .Values.serviceType }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl --namespace fission get -w svc' and 'kubectl --namespace fission get svc -w controller' - - $ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..ip}') - -{{- else if contains "ClusterIP" .Values.serviceType }} - $ export FISSION_NAMESPACE={{ .Release.Namespace }} - $ export KUBECONFIG=${HOME}/.kube/config - -{{- end }} - -{{- if contains "NodePort" .Values.routerServiceType }} - $ export FISSION_ROUTER=$(minikube ip):{{ .Values.routerPort }} - -{{- else if contains "LoadBalancer" .Values.routerServiceType }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status by running 'kubectl --namespace fission get -w svc' and 'kubectl --namespace fission get svc -w router' - $ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}') - -{{- else if contains "ClusterIP" .Values.routerServiceType }} - $ export KUBECONFIG=${HOME}/.kube/config - $ curl -Lo port-forward-router.sh https://github.com/fission/fission/port-forward-router.sh && chmod +x port-forward-router.sh && ./port-forward-router.sh {{ .Release.Namespace }} router 9999 - $ export FISSION_ROUTER=127.0.0.1:9999 - -{{- end }} - -3. Finally, you're ready to use Fission! +2. You're ready to use Fission! + # Create an environment $ fission env create --name nodejs --image fission/node-env + # Get a hello world $ curl https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js > hello.js + # Register this function with Fission $ fission function create --name hello --env nodejs --code hello.js - $ fission route create --method GET --url /hello --function hello - - $ curl http://$FISSION_ROUTER/hello + # Run this function + $ fission function test --name hello Hello, world! diff --git a/charts/fission-core/templates/deployment.yaml b/charts/fission-core/templates/deployment.yaml index d241f216..1b2a2c1e 100644 --- a/charts/fission-core/templates/deployment.yaml +++ b/charts/fission-core/templates/deployment.yaml @@ -168,6 +168,7 @@ spec: template: metadata: labels: + application: fission-router svc: router spec: containers: diff --git a/charts/fission-core/templates/svc.yaml b/charts/fission-core/templates/svc.yaml index 252b989c..1283bb29 100644 --- a/charts/fission-core/templates/svc.yaml +++ b/charts/fission-core/templates/svc.yaml @@ -4,6 +4,7 @@ metadata: name: router labels: svc: router + application: fission-router chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: type: {{ .Values.routerServiceType }} @@ -23,6 +24,7 @@ metadata: name: controller labels: svc: controller + application: fission-api chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: type: {{ .Values.serviceType }} diff --git a/fission/function.go b/fission/function.go index 41dbf69e..0e1c6090 100644 --- a/fission/function.go +++ b/fission/function.go @@ -21,10 +21,10 @@ import ( "errors" "fmt" "io/ioutil" - "net" "net/http" "net/url" "os" + "strings" "text/tabwriter" "time" @@ -690,7 +690,6 @@ func fnPods(c *cli.Context) error { } func fnTest(c *cli.Context) error { - //we can port-forward the router specifically for this method fnName := c.String("name") if len(fnName) == 0 { fatal("Need function name to be specified with --name") @@ -698,27 +697,12 @@ func fnTest(c *cli.Context) error { routerURL := os.Getenv("FISSION_ROUTER") if len(routerURL) == 0 { - localRouterPort, err := findFreePort() - if err != nil { - fatal(fmt.Sprintf("Error finding unused port for router :%s", err.Error())) - } - - fissionNamespace := os.Getenv("FISSION_NAMESPACE") - go func() { - err := runportForward("router", localRouterPort, fissionNamespace) - if err != nil { - fatal(err.Error()) - } - }() - - for { - conn, _ := net.DialTimeout("tcp", net.JoinHostPort("", localRouterPort), time.Second) - if conn != nil { - conn.Close() - break - } - } + // Portforward to the fission router + localRouterPort := setupPortForward(getKubeConfigPath(), + getFissionNamespace(), "application=fission-router") routerURL = "127.0.0.1:" + localRouterPort + } else { + routerURL = strings.TrimPrefix(routerURL, "http://") } url := fmt.Sprintf("http://%s/fission-function/%s", routerURL, fnName) diff --git a/fission/main.go b/fission/main.go index 941c22cb..4b22b98c 100644 --- a/fission/main.go +++ b/fission/main.go @@ -18,10 +18,29 @@ package main import ( "os" + "path/filepath" "github.com/urfave/cli" ) +func getFissionNamespace() string { + fissionNamespace := os.Getenv("FISSION_NAMESPACE") + if len(fissionNamespace) == 0 { + // TODO make this smarter, perhaps based on helm releases + fissionNamespace = "fission" + } + return fissionNamespace +} + +func getKubeConfigPath() string { + kubeConfig := os.Getenv("KUBECONFIG") + if len(kubeConfig) == 0 { + home := os.Getenv("HOME") + kubeConfig = filepath.Join(home, ".kube", "config") + } + return kubeConfig +} + func main() { app := cli.NewApp() app.Name = "fission" @@ -32,13 +51,10 @@ func main() { var value string fissionUrl := os.Getenv("FISSION_URL") if len(fissionUrl) == 0 { - // check here to specify env var for KUBECONFIG and FISSION_NAMESPACE - fissionNamespace := os.Getenv("FISSION_NAMESPACE") - kubeConfig := os.Getenv("KUBECONFIG") - if len(kubeConfig) == 0 || len(fissionNamespace) == 0 { - fatal("Environment variables KUBECONFIG and FISSION_NAMESPACE are mandatory if the serviceType is ClusterIP") - } - localPort := controllerPodPortForward(fissionNamespace) + fissionNamespace := getFissionNamespace() + kubeConfig := getKubeConfigPath() + localPort := setupPortForward( + kubeConfig, fissionNamespace, "application=fission-api") value = "http://127.0.0.1:" + localPort } else { value = fissionUrl diff --git a/fission/portforward.go b/fission/portforward.go index bd6c4431..94ff1aac 100644 --- a/fission/portforward.go +++ b/fission/portforward.go @@ -8,10 +8,11 @@ import ( "time" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + // "k8s.io/client-go/rest" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/portforward" "k8s.io/client-go/tools/remotecommand" - - "github.com/fission/fission/crd" ) func findFreePort() (string, error) { @@ -39,28 +40,38 @@ func findFreePort() (string, error) { return port, nil } -func runportForward(serviceName string, localPort string, fissionNamespace string) error { - //KUBECONFIG needs to be set to the correct path i.e ~/.kube/config - config, podClient, _, err := crd.GetKubernetesClient() +// runPortForward creates a local port forward to the specified pod +func runPortForward(kubeConfig string, labelSelector string, localPort string, fissionNamespace string) error { + config, err := clientcmd.BuildConfigFromFlags("", kubeConfig) if err != nil { - fatal(err.Error()) + fatal(fmt.Sprintf("Failed to connect to Kubernetes: %s", err)) } - //get the podname for the controller - podList, err := podClient.CoreV1().Pods(fissionNamespace).List(meta_v1.ListOptions{LabelSelector: "application=fission-api"}) + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + fatal(fmt.Sprintf("Failed to connect to Kubernetes: %s", err)) + } + + // get the pod; if there is more than one, always port-forward to the first. + podList, err := clientset.CoreV1().Pods(fissionNamespace). + List(meta_v1.ListOptions{LabelSelector: labelSelector}) if err != nil || len(podList.Items) == 0 { fatal("Error getting controller pod for port-forwarding") } - // if there are more than one pods, always port-forward to the first pod returned podName := podList.Items[0].Name podNameSpace := podList.Items[0].Namespace - //get the ControllerPort - service, err := podClient.CoreV1().Services(podNameSpace).Get(serviceName, meta_v1.GetOptions{}) + // get the service and the target port + svcs, err := clientset.CoreV1().Services(podNameSpace). + List(meta_v1.ListOptions{LabelSelector: labelSelector}) if err != nil { - fatal(fmt.Sprintf("Error getting %v service :%v", serviceName, err.Error())) + fatal(fmt.Sprintf("Error getting %v service :%v", labelSelector, err.Error())) } + if len(svcs.Items) == 0 { + fatal(fmt.Sprintf("Service %v not found", labelSelector)) + } + service := &svcs.Items[0] var targetPort string for _, servicePort := range service.Spec.Ports { @@ -70,15 +81,16 @@ func runportForward(serviceName string, localPort string, fissionNamespace strin stopChannel := make(chan struct{}, 1) readyChannel := make(chan struct{}) - //create request URL - req := podClient.CoreV1Client.RESTClient().Post().Resource("pods").Namespace(podNameSpace).Name(podName).SubResource("portforward") + // create request URL + req := clientset.CoreV1Client.RESTClient().Post().Resource("pods"). + Namespace(podNameSpace).Name(podName).SubResource("portforward") url := req.URL() - //create ports slice + // create ports slice portCombo := localPort + ":" + targetPort ports := []string{portCombo} - //actually start the port-forwarding process here + // actually start the port-forwarding process here dialer, err := remotecommand.NewExecutor(config, "POST", url) if err != nil { msg := fmt.Sprintf("newexecutor errored out :%v", err.Error()) @@ -94,15 +106,20 @@ func runportForward(serviceName string, localPort string, fissionNamespace strin return fw.ForwardPorts() } -func controllerPodPortForward(fissionNamespace string) string { - localControllerPort, err := findFreePort() +// Port forward a free local port to a pod on the cluster. The pod is +// found in the specified namespace by labelSelector. The pod's port +// is found by looking for a service in the same namespace and using +// its targetPort. Once the port forward is started, wait for it to +// start accepting connections before returning. +func setupPortForward(kubeConfig, namespace, labelSelector string) string { + localPort, err := findFreePort() if err != nil { fatal(fmt.Sprintf("Error finding unused port :%v", err.Error())) } - timeBefore := time.Now() for { - conn, _ := net.DialTimeout("tcp", net.JoinHostPort("", localControllerPort), time.Millisecond) + conn, _ := net.DialTimeout("tcp", + net.JoinHostPort("", localPort), time.Millisecond) if conn != nil { conn.Close() } else { @@ -111,20 +128,16 @@ func controllerPodPortForward(fissionNamespace string) string { time.Sleep(time.Millisecond * 50) } - timeAfter := time.Since(timeBefore) - if timeAfter.Seconds()/1000 >= 100 { - fatal(fmt.Sprintln("Lag in connecting to a free port on the localhost")) - } - go func() { - err := runportForward("controller", localControllerPort, fissionNamespace) + err := runPortForward(kubeConfig, labelSelector, localPort, namespace) if err != nil { - fatal(err.Error()) + fatal(fmt.Sprintf("Error forwarding to controller port: %s", err.Error())) } }() for { - conn, _ := net.DialTimeout("tcp", net.JoinHostPort("", localControllerPort), time.Millisecond) + conn, _ := net.DialTimeout("tcp", + net.JoinHostPort("", localPort), time.Millisecond) if conn != nil { conn.Close() break @@ -132,5 +145,5 @@ func controllerPodPortForward(fissionNamespace string) string { time.Sleep(time.Millisecond * 50) } - return localControllerPort + return localPort }