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 }