Move packages to proejct/pkg to follow go project folder structure convention (#1190)
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/portforward"
|
||||
"k8s.io/client-go/transport/spdy"
|
||||
|
||||
"github.com/fission/fission/pkg/fission-cli/log"
|
||||
"github.com/fission/fission/pkg/utils"
|
||||
)
|
||||
|
||||
// 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(namespace, labelSelector string) string {
|
||||
log.Verbose(2, "Setting up port forward to %s in namespace %s",
|
||||
labelSelector, namespace)
|
||||
|
||||
localPort, err := findFreePort()
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Error finding unused port :%v", err.Error()))
|
||||
}
|
||||
|
||||
log.Verbose(2, "Waiting for local port %v", localPort)
|
||||
for {
|
||||
conn, _ := net.DialTimeout("tcp",
|
||||
net.JoinHostPort("", localPort), time.Millisecond)
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
}
|
||||
|
||||
log.Verbose(2, "Starting port forward from local port %v", localPort)
|
||||
go func() {
|
||||
err := runPortForward(labelSelector, localPort, namespace)
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Error forwarding to port %v: %s", localPort, err.Error()))
|
||||
}
|
||||
}()
|
||||
|
||||
log.Verbose(2, "Waiting for port forward %v to start...", localPort)
|
||||
for {
|
||||
conn, _ := net.DialTimeout("tcp",
|
||||
net.JoinHostPort("", localPort), time.Millisecond)
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
}
|
||||
|
||||
log.Verbose(2, "Port forward from local port %v started", localPort)
|
||||
|
||||
return localPort
|
||||
}
|
||||
|
||||
func findFreePort() (string, error) {
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
port := strconv.Itoa(listener.Addr().(*net.TCPAddr).Port)
|
||||
|
||||
err = listener.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// runPortForward creates a local port forward to the specified pod
|
||||
func runPortForward(labelSelector string, localPort string, ns string) error {
|
||||
config, clientset := GetKubernetesClient()
|
||||
|
||||
log.Verbose(2, "Connected to Kubernetes API")
|
||||
|
||||
// if namespace is unset, try to find a pod in any namespace
|
||||
if len(ns) == 0 {
|
||||
ns = meta_v1.NamespaceAll
|
||||
}
|
||||
|
||||
// get the pod; if there is more than one, ask the user to disambiguate
|
||||
podList, err := clientset.CoreV1().Pods(ns).
|
||||
List(meta_v1.ListOptions{LabelSelector: labelSelector})
|
||||
if err != nil || len(podList.Items) == 0 {
|
||||
log.Fatal(fmt.Sprintf("Error getting pod for port-forwarding with label selector %v: %v", labelSelector, err))
|
||||
}
|
||||
|
||||
nsList := make([]string, 0)
|
||||
namespaces := make(map[string][]*v1.Pod)
|
||||
|
||||
// make a useful error message if there is more than one install
|
||||
if len(podList.Items) > 0 {
|
||||
for _, p := range podList.Items {
|
||||
if _, ok := namespaces[p.Namespace]; !ok {
|
||||
namespaces[p.Namespace] = []*v1.Pod{}
|
||||
nsList = append(nsList, p.Namespace)
|
||||
}
|
||||
namespaces[p.Namespace] = append(namespaces[p.Namespace], &p)
|
||||
}
|
||||
if len(nsList) > 1 {
|
||||
log.Fatal(fmt.Sprintf("Found %v fission installs, set FISSION_NAMESPACE to one of: %v",
|
||||
len(namespaces), strings.Join(nsList, " ")))
|
||||
}
|
||||
}
|
||||
|
||||
// there is at most one namespace in nsList,
|
||||
// use index 0 to get from it directly.
|
||||
ns = nsList[0]
|
||||
pods, ok := namespaces[ns]
|
||||
if !ok {
|
||||
log.Fatal(fmt.Sprintf("Error finding fission install within the given namespace %v, please check FISSION_NAMESPACE is set properly", ns))
|
||||
}
|
||||
|
||||
var podName, podNameSpace string
|
||||
|
||||
// make sure we establish the connection to a healthy pod
|
||||
for _, p := range pods {
|
||||
if utils.IsReadyPod(p) {
|
||||
podName = p.Name
|
||||
podNameSpace = p.Namespace
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// get the service and the target port
|
||||
svcs, err := clientset.CoreV1().Services(podNameSpace).
|
||||
List(meta_v1.ListOptions{LabelSelector: labelSelector})
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Error getting %v service :%v", labelSelector, err.Error()))
|
||||
}
|
||||
if len(svcs.Items) == 0 {
|
||||
log.Fatal(fmt.Sprintf("Service %v not found", labelSelector))
|
||||
}
|
||||
service := &svcs.Items[0]
|
||||
|
||||
var targetPort string
|
||||
for _, servicePort := range service.Spec.Ports {
|
||||
targetPort = servicePort.TargetPort.String()
|
||||
}
|
||||
log.Verbose(2, "Connecting to port %v on pod %v/%v", targetPort, podNameSpace, podNameSpace)
|
||||
|
||||
stopChannel := make(chan struct{}, 1)
|
||||
readyChannel := make(chan struct{})
|
||||
|
||||
// create request URL
|
||||
req := clientset.CoreV1().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
|
||||
transport, upgrader, err := spdy.RoundTripperFor(config)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Failed to connect to Fission service on Kubernetes: %v", err.Error())
|
||||
log.Fatal(msg)
|
||||
}
|
||||
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", url)
|
||||
|
||||
outStream := os.Stdout
|
||||
if log.Verbosity < 2 {
|
||||
outStream = nil
|
||||
}
|
||||
fw, err := portforward.New(dialer, ports, stopChannel, readyChannel, outStream, os.Stderr)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("portforward.new errored out :%v", err.Error())
|
||||
log.Fatal(msg)
|
||||
}
|
||||
|
||||
log.Verbose(2, "Starting port forwarder")
|
||||
return fw.ForwardPorts()
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright 2018 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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/fission/fission/pkg/controller/client"
|
||||
"github.com/fission/fission/pkg/fission-cli/log"
|
||||
)
|
||||
|
||||
func GetApiClient(serverUrl string) *client.Client {
|
||||
if len(serverUrl) == 0 {
|
||||
// starts local portforwarder etc.
|
||||
serverUrl = GetServerUrl()
|
||||
}
|
||||
|
||||
isHTTPS := strings.Index(serverUrl, "https://") == 0
|
||||
isHTTP := strings.Index(serverUrl, "http://") == 0
|
||||
|
||||
if !(isHTTP || isHTTPS) {
|
||||
serverUrl = "http://" + serverUrl
|
||||
}
|
||||
|
||||
return client.MakeClient(serverUrl)
|
||||
}
|
||||
|
||||
func GetFissionNamespace() string {
|
||||
fissionNamespace := os.Getenv("FISSION_NAMESPACE")
|
||||
return fissionNamespace
|
||||
}
|
||||
|
||||
func GetServerUrl() string {
|
||||
return GetApplicationUrl("application=fission-api")
|
||||
}
|
||||
|
||||
func GetApplicationUrl(selector string) string {
|
||||
var serverUrl string
|
||||
// Use FISSION_URL env variable if set; otherwise, port-forward to controller.
|
||||
fissionUrl := os.Getenv("FISSION_URL")
|
||||
if len(fissionUrl) == 0 {
|
||||
fissionNamespace := GetFissionNamespace()
|
||||
localPort := SetupPortForward(fissionNamespace, "application=fission-api")
|
||||
serverUrl = "http://127.0.0.1:" + localPort
|
||||
} else {
|
||||
serverUrl = fissionUrl
|
||||
}
|
||||
return serverUrl
|
||||
}
|
||||
|
||||
func CheckErr(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Failed to %v: %v", msg, err))
|
||||
}
|
||||
}
|
||||
|
||||
// KubifyName make a kubernetes compliant name out of an arbitrary string
|
||||
func KubifyName(old string) string {
|
||||
// Kubernetes maximum name length (for some names; others can be 253 chars)
|
||||
maxLen := 63
|
||||
|
||||
newName := strings.ToLower(old)
|
||||
|
||||
// replace disallowed chars with '-'
|
||||
inv, err := regexp.Compile("[^-a-z0-9]")
|
||||
CheckErr(err, "compile regexp")
|
||||
newName = string(inv.ReplaceAll([]byte(newName), []byte("-")))
|
||||
|
||||
// trim leading non-alphabetic
|
||||
leadingnonalpha, err := regexp.Compile("^[^a-z]+")
|
||||
CheckErr(err, "compile regexp")
|
||||
newName = string(leadingnonalpha.ReplaceAll([]byte(newName), []byte{}))
|
||||
|
||||
// trim trailing
|
||||
trailing, err := regexp.Compile("[^a-z0-9]+$")
|
||||
CheckErr(err, "compile regexp")
|
||||
newName = string(trailing.ReplaceAll([]byte(newName), []byte{}))
|
||||
|
||||
// truncate to length
|
||||
if len(newName) > maxLen {
|
||||
newName = newName[0:maxLen]
|
||||
}
|
||||
|
||||
// if we removed everything, call this thing "default". maybe
|
||||
// we should generate a unique name...
|
||||
if len(newName) == 0 {
|
||||
newName = "default"
|
||||
}
|
||||
|
||||
return newName
|
||||
}
|
||||
|
||||
// GetKubernetesClient builds a new kubernetes client. If the KUBECONFIG
|
||||
// environment variable is empty or doesn't exist, ~/.kube/config is used for
|
||||
// the kube config path
|
||||
func GetKubernetesClient() (*restclient.Config, *kubernetes.Clientset) {
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
|
||||
kubeConfigPath := os.Getenv("KUBECONFIG")
|
||||
if len(kubeConfigPath) == 0 {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Could not get the current users directory: %s", err))
|
||||
}
|
||||
|
||||
kubeConfigPath = filepath.Join(usr.HomeDir, ".kube", "config")
|
||||
|
||||
if _, err := os.Stat(kubeConfigPath); os.IsNotExist(err) {
|
||||
log.Fatal("Couldn't find kubeconfig file. " +
|
||||
"Set the KUBECONFIG environment variable to your kubeconfig's path.")
|
||||
}
|
||||
loadingRules.ExplicitPath = kubeConfigPath
|
||||
log.Verbose(2, "Using kubeconfig from %q", kubeConfigPath)
|
||||
} else {
|
||||
log.Verbose(2, "Using kubeconfig from environment %q", kubeConfigPath)
|
||||
}
|
||||
|
||||
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
loadingRules, &clientcmd.ConfigOverrides{}).ClientConfig()
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Failed to build Kubernetes config: %s", err))
|
||||
}
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Failed to connect to Kubernetes: %s", err))
|
||||
}
|
||||
|
||||
return config, clientset
|
||||
}
|
||||
|
||||
// given a list of functions, this checks if the functions actually exist on the cluster
|
||||
func CheckFunctionExistence(fissionClient *client.Client, functions []string, fnNamespace string) (err error) {
|
||||
fnMissing := make([]string, 0)
|
||||
for _, fnName := range functions {
|
||||
meta := &metav1.ObjectMeta{
|
||||
Name: fnName,
|
||||
Namespace: fnNamespace,
|
||||
}
|
||||
|
||||
_, err := fissionClient.FunctionGet(meta)
|
||||
if err != nil {
|
||||
fnMissing = append(fnMissing, fnName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(fnMissing) > 0 {
|
||||
return fmt.Errorf("function(s) %s, not present in namespace : %s", fnMissing, fnNamespace)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/fission/fission/pkg/controller/client"
|
||||
"github.com/fission/fission/pkg/fission-cli/log"
|
||||
"github.com/fission/fission/pkg/fission-cli/plugin"
|
||||
"github.com/fission/fission/pkg/info"
|
||||
)
|
||||
|
||||
// Versions is a container of versions of the client (and its plugins) and server (and its plugins).
|
||||
type Versions struct {
|
||||
Client map[string]info.BuildMeta `json:"client"`
|
||||
Server map[string]info.BuildMeta `json:"server"`
|
||||
}
|
||||
|
||||
func GetVersion(client *client.Client) []byte {
|
||||
serverInfo, err := client.ServerInfo()
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("Error getting Fission API version: %v", err))
|
||||
}
|
||||
|
||||
// Fetch client versions
|
||||
versions := Versions{
|
||||
Client: map[string]info.BuildMeta{
|
||||
"fission/core": info.BuildInfo(),
|
||||
},
|
||||
}
|
||||
for _, pmd := range plugin.FindAll() {
|
||||
versions.Client[pmd.Name] = info.BuildMeta{
|
||||
Version: pmd.Version,
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch server versions
|
||||
versions.Server = map[string]info.BuildMeta{
|
||||
"fission/core": serverInfo.Build,
|
||||
}
|
||||
// FUTURE: fetch versions of plugins server-side
|
||||
bs, err := yaml.Marshal(versions)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to format versions: " + err.Error())
|
||||
}
|
||||
|
||||
return bs
|
||||
}
|
||||
Reference in New Issue
Block a user