Service type ClusterIP - Controller port forward through CLI (#431)

setting serviceType to ClusterIP as default for fission controller and corresponding changes in fission cli to be able to port-forward the controller pod.
This commit is contained in:
prithviramesh
2018-02-27 08:22:57 -08:00
committed by smruthi2187
parent 1428c64b4f
commit b9a559af2f
20 changed files with 355 additions and 77 deletions
+18 -1
View File
@@ -157,6 +157,20 @@ func createArchive(client *client.Client, fileName string, specFile string) *fis
archive.Type = fission.ArchiveTypeLiteral
archive.Literal = contents
} else {
// make a kubernetes client
_, kubeClient, _, err := crd.GetKubernetesClient()
if err != nil {
fatal(err.Error())
}
fissionNamespace := os.Getenv("FISSION_NAMESPACE")
// get svc end point for storagesvc
service, err := kubeClient.CoreV1().Services(fissionNamespace).Get("storagesvc", metav1.GetOptions{})
if err != nil {
fatal(fmt.Sprintf("Error getting storage service object from kubernetes :%v", err.Error()))
}
u := strings.TrimSuffix(client.Url, "/") + "/proxy/storage"
ssClient := storageSvcClient.MakeClient(u)
@@ -164,7 +178,10 @@ func createArchive(client *client.Client, fileName string, specFile string) *fis
id, err := ssClient.Upload(fileName, nil)
checkErr(err, fmt.Sprintf("upload file %v", fileName))
archiveUrl := ssClient.GetUrl(id)
// this needs to be storagesvc.fission
storageSvcEndpoint := fmt.Sprintf("http://%s.%s/", service.Name, service.Namespace)
storageServiceClient := storageSvcClient.MakeClient(storageSvcEndpoint)
archiveUrl := storageServiceClient.GetUrl(id)
archive.Type = fission.ArchiveTypeUrl
archive.URL = archiveUrl
+28 -2
View File
@@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
@@ -602,10 +603,35 @@ 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")
}
routerURL := os.Getenv("FISSION_ROUTER")
if len(routerURL) == 0 {
fatal("Need FISSION_ROUTER set to your fission router.")
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
}
}
routerURL = "127.0.0.1:" + localRouterPort
}
url := fmt.Sprintf("http://%s/fission-function/%s", routerURL, fnName)
@@ -621,7 +647,7 @@ func fnTest(c *cli.Context) error {
body, err := ioutil.ReadAll(resp.Body)
checkErr(err, "read log response from pod")
fmt.Printf("Error calling function %v: %v %v", fnName, resp.StatusCode, string(body))
fmt.Printf("Error calling function %s: %d %s", fnName, resp.StatusCode, string(body))
defer resp.Body.Close()
err = printPodLogs(c)
if err != nil {
+17 -1
View File
@@ -28,8 +28,24 @@ func main() {
app.Usage = "Serverless functions for Kubernetes"
app.Version = "0.5.0"
// fetch the FISSION_URL env variable. If not set, port-forward to controller.
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)
value = "http://127.0.0.1:" + localPort
} else {
value = fissionUrl
}
app.Flags = []cli.Flag{
cli.StringFlag{Name: "server", Usage: "Fission server URL", EnvVar: "FISSION_URL"},
cli.StringFlag{Name: "server", Value: value, Usage: "Fission server URL"},
}
// trigger method and url flags (used in function and route CLIs)
+3 -1
View File
@@ -53,9 +53,11 @@ func downloadStoragesvcURL(client *client.Client, fileUrl string) io.ReadCloser
if err != nil {
return nil
}
// replace in-cluster storage service host with controller server url
fileDownloadUrl := strings.TrimSuffix(client.Url, "/") + "/proxy/storage" + u.RequestURI()
fileDownloadUrl := strings.TrimSuffix(client.Url, "/") + "/proxy/storage/" + u.RequestURI()
reader, err := downloadURL(fileDownloadUrl)
checkErr(err, fmt.Sprintf("download from storage service url: %v", fileUrl))
return reader
}
+136
View File
@@ -0,0 +1,136 @@
package main
import (
"fmt"
"net"
"os"
"strconv"
"time"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/portforward"
"k8s.io/client-go/tools/remotecommand"
"github.com/fission/fission/crd"
)
func findFreePort() (string, error) {
listener, err := net.Listen("tcp", ":0")
if err != nil {
return "", err
}
port := strconv.Itoa(listener.Addr().(*net.TCPAddr).Port)
file, err := listener.(*net.TCPListener).File()
if err != nil {
return "", nil
}
err = listener.Close()
if err != nil {
return "", err
}
err = file.Close()
if err != nil {
return "", err
}
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()
if err != nil {
fatal(err.Error())
}
//get the podname for the controller
podList, err := podClient.CoreV1().Pods(fissionNamespace).List(meta_v1.ListOptions{LabelSelector: "application=fission-api"})
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{})
if err != nil {
fatal(fmt.Sprintf("Error getting %v service :%v", serviceName, err.Error()))
}
var targetPort string
for _, servicePort := range service.Spec.Ports {
targetPort = servicePort.TargetPort.String()
}
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")
url := req.URL()
//create ports slice
portCombo := localPort + ":" + targetPort
ports := []string{portCombo}
//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())
fatal(msg)
}
fw, err := portforward.New(dialer, ports, stopChannel, readyChannel, nil, os.Stderr)
if err != nil {
msg := fmt.Sprintf("portforward.new errored out :%v", err.Error())
fatal(msg)
}
return fw.ForwardPorts()
}
func controllerPodPortForward(fissionNamespace string) string {
localControllerPort, 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)
if conn != nil {
conn.Close()
} else {
break
}
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)
if err != nil {
fatal(err.Error())
}
}()
for {
conn, _ := net.DialTimeout("tcp", net.JoinHostPort("", localControllerPort), time.Millisecond)
if conn != nil {
conn.Close()
break
}
time.Sleep(time.Millisecond * 50)
}
return localControllerPort
}