Move packages to proejct/pkg to follow go project folder structure convention (#1190)

This commit is contained in:
Ta-Ching Chen
2019-05-31 16:28:55 +08:00
committed by GitHub
parent 1c5fd92ad6
commit a0e9a39511
196 changed files with 1672 additions and 1716 deletions
+280
View File
@@ -0,0 +1,280 @@
/*
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 controller
import (
"fmt"
"net/http"
"os"
"runtime/debug"
"strings"
"github.com/gorilla/mux"
"go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"github.com/fission/fission/pkg/crd"
ferror "github.com/fission/fission/pkg/error"
"github.com/fission/fission/pkg/fission-cli/logdb"
"github.com/fission/fission/pkg/info"
"github.com/fission/fission/pkg/utils"
)
var podNamespace string
func init() {
podNamespace = os.Getenv("POD_NAMESPACE")
if podNamespace == "" {
podNamespace = "fission"
}
}
type (
API struct {
logger *zap.Logger
fissionClient *crd.FissionClient
kubernetesClient *kubernetes.Clientset
storageServiceUrl string
builderManagerUrl string
workflowApiUrl string
functionNamespace string
useIstio bool
featureStatus map[string]string
}
logDBConfig struct {
httpURL string
username string
password string
}
)
func MakeAPI(logger *zap.Logger, featureStatus map[string]string) (*API, error) {
api, err := makeCRDBackedAPI(logger)
u := os.Getenv("STORAGE_SERVICE_URL")
if len(u) > 0 {
api.storageServiceUrl = strings.TrimSuffix(u, "/")
} else {
api.storageServiceUrl = "http://storagesvc"
}
u = os.Getenv("BUILDER_MANAGER_URL")
if len(u) > 0 {
api.builderManagerUrl = strings.TrimSuffix(u, "/")
} else {
api.builderManagerUrl = "http://buildermgr"
}
wfEnv := os.Getenv("WORKFLOW_API_URL")
if len(u) > 0 {
api.workflowApiUrl = strings.TrimSuffix(wfEnv, "/")
} else {
api.workflowApiUrl = "http://workflows-apiserver"
}
fnNs := os.Getenv("FISSION_FUNCTION_NAMESPACE")
if len(fnNs) > 0 {
api.functionNamespace = fnNs
} else {
api.functionNamespace = "fission-function"
}
api.featureStatus = featureStatus
return api, err
}
func (api *API) respondWithSuccess(w http.ResponseWriter, resp []byte) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_, err := w.Write(resp)
if err != nil {
// this will probably fail too, but try anyway
api.respondWithError(w, err)
}
}
func (api *API) respondWithError(w http.ResponseWriter, err error) {
debug.PrintStack()
// this error type comes with an HTTP code, so just use that
se, ok := err.(*kerrors.StatusError)
if ok {
http.Error(w, string(se.ErrStatus.Reason), int(se.ErrStatus.Code))
return
}
code, msg := ferror.GetHTTPError(err)
api.logger.Error(msg, zap.Int("code", code))
http.Error(w, msg, code)
}
func (api *API) extractQueryParamFromRequest(r *http.Request, queryParam string) string {
values := r.URL.Query()
return values.Get(queryParam)
}
// check if namespace exists, if not create it.
func (api *API) createNsIfNotExists(ns string) error {
if ns == metav1.NamespaceDefault {
// we dont have to create default ns
return nil
}
_, err := api.kubernetesClient.CoreV1().Namespaces().Get(ns, metav1.GetOptions{})
if err != nil && kerrors.IsNotFound(err) {
ns := &apiv1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: ns,
},
}
_, err = api.kubernetesClient.CoreV1().Namespaces().Create(ns)
}
return err
}
func (api *API) getLogDBConfig(dbType string) logDBConfig {
dbType = strings.ToUpper(dbType)
// retrieve db auth config from the env
url := os.Getenv(fmt.Sprintf("%s_URL", dbType))
if url == "" {
// set up default database url
url = logdb.INFLUXDB_URL
}
username := os.Getenv(fmt.Sprintf("%s_USERNAME", dbType))
password := os.Getenv(fmt.Sprintf("%s_PASSWORD", dbType))
return logDBConfig{
httpURL: url,
username: username,
password: password,
}
}
func (api *API) HomeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprintf(w, info.ApiInfo().String())
}
func (api *API) ApiVersionMismatchHandler(w http.ResponseWriter, r *http.Request) {
err := ferror.MakeError(ferror.ErrorNotFound, "Fission server supports API v2 only -- v1 is not supported. Please upgrade your Fission client/CLI.")
api.respondWithError(w, err)
}
func (api *API) HealthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func (api *API) GetSvcName(w http.ResponseWriter, r *http.Request) {
appLabelSelector := "application=" + r.URL.Query().Get("application")
services, err := api.kubernetesClient.CoreV1().Services(podNamespace).List(metav1.ListOptions{
LabelSelector: appLabelSelector,
})
if err != nil || len(services.Items) > 1 || len(services.Items) == 0 {
api.respondWithError(w, err)
}
service := services.Items[0]
fmt.Fprintf(w, service.Name+"."+podNamespace)
}
func (api *API) Serve(port int) {
r := mux.NewRouter()
r.HandleFunc("/healthz", api.HealthHandler).Methods("GET")
// Give a useful error message if an older CLI attempts to make a request
r.HandleFunc(`/v1/{rest:[a-zA-Z0-9=\-\/]+}`, api.ApiVersionMismatchHandler)
r.HandleFunc("/", api.HomeHandler)
r.HandleFunc("/v2/packages", api.PackageApiList).Methods("GET")
r.HandleFunc("/v2/packages", api.PackageApiCreate).Methods("POST")
r.HandleFunc("/v2/packages/{package}", api.PackageApiGet).Methods("GET")
r.HandleFunc("/v2/packages/{package}", api.PackageApiUpdate).Methods("PUT")
r.HandleFunc("/v2/packages/{package}", api.PackageApiDelete).Methods("DELETE")
r.HandleFunc("/v2/functions", api.FunctionApiList).Methods("GET")
r.HandleFunc("/v2/functions", api.FunctionApiCreate).Methods("POST")
r.HandleFunc("/v2/functions/{function}", api.FunctionApiGet).Methods("GET")
r.HandleFunc("/v2/functions/{function}", api.FunctionApiUpdate).Methods("PUT")
r.HandleFunc("/v2/functions/{function}", api.FunctionApiDelete).Methods("DELETE")
r.HandleFunc("/v2/triggers/http", api.HTTPTriggerApiList).Methods("GET")
r.HandleFunc("/v2/triggers/http", api.HTTPTriggerApiCreate).Methods("POST")
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiGet).Methods("GET")
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/v2/environments", api.EnvironmentApiList).Methods("GET")
r.HandleFunc("/v2/environments", api.EnvironmentApiCreate).Methods("POST")
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiGet).Methods("GET")
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT")
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE")
r.HandleFunc("/v2/watches", api.WatchApiList).Methods("GET")
r.HandleFunc("/v2/watches", api.WatchApiCreate).Methods("POST")
r.HandleFunc("/v2/watches/{watch}", api.WatchApiGet).Methods("GET")
r.HandleFunc("/v2/watches/{watch}", api.WatchApiUpdate).Methods("PUT")
r.HandleFunc("/v2/watches/{watch}", api.WatchApiDelete).Methods("DELETE")
r.HandleFunc("/v2/triggers/time", api.TimeTriggerApiList).Methods("GET")
r.HandleFunc("/v2/triggers/time", api.TimeTriggerApiCreate).Methods("POST")
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiGet).Methods("GET")
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/v2/triggers/messagequeue", api.MessageQueueTriggerApiList).Methods("GET")
r.HandleFunc("/v2/triggers/messagequeue", api.MessageQueueTriggerApiCreate).Methods("POST")
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiGet).Methods("GET")
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/v2/recorders", api.RecorderApiList).Methods("GET")
r.HandleFunc("/v2/recorders", api.RecorderApiCreate).Methods("POST")
r.HandleFunc("/v2/recorders/{recorder}", api.RecorderApiGet).Methods("GET")
r.HandleFunc("/v2/recorders/{recorder}", api.RecorderApiUpdate).Methods("PUT")
r.HandleFunc("/v2/recorders/{recorder}", api.RecorderApiDelete).Methods("DELETE")
r.HandleFunc("/v2/records", api.RecordsApiListAll).Methods("GET")
r.HandleFunc("/v2/records/function/{function}", api.RecordsApiFilterByFunction).Methods("GET")
r.HandleFunc("/v2/records/trigger/{trigger}", api.RecordsApiFilterByTrigger).Methods("GET")
r.HandleFunc("/v2/records/time", api.RecordsApiFilterByTime).Methods("GET")
r.HandleFunc("/v2/replay/{reqUID}", api.ReplayByReqUID).Methods("GET")
r.HandleFunc("/v2/secrets/{secret}", api.SecretGet).Methods("GET")
r.HandleFunc("/v2/configmaps/{configmap}", api.ConfigMapGet).Methods("GET")
r.HandleFunc("/v2/canaryconfigs", api.CanaryConfigApiCreate).Methods("POST")
r.HandleFunc("/v2/canaryconfigs/{canaryConfig}", api.CanaryConfigApiGet).Methods("GET")
r.HandleFunc("/v2/canaryconfigs/{canaryConfig}", api.CanaryConfigApiUpdate).Methods("PUT")
r.HandleFunc("/v2/canaryconfigs/{canaryConfig}", api.CanaryConfigApiDelete).Methods("DELETE")
r.HandleFunc("/v2/canaryconfigs", api.CanaryConfigApiList).Methods("GET")
r.HandleFunc("/proxy/{dbType}", api.FunctionLogsApiPost).Methods("POST")
r.HandleFunc("/proxy/storage/v1/archive", api.StorageServiceProxy)
r.HandleFunc("/proxy/logs/{function}", api.FunctionPodLogs).Methods("POST")
r.HandleFunc("/proxy/workflows-apiserver/{path:.*}", api.WorkflowApiserverProxy)
r.HandleFunc("/proxy/svcname", api.GetSvcName).Queries("application", "").Methods("GET")
address := fmt.Sprintf(":%v", port)
api.logger.Info("server started", zap.Int("port", port))
r.Use(utils.LoggingMiddleware(api.logger))
err := http.ListenAndServe(address, r)
api.logger.Fatal("done listening", zap.Error(err))
}
+372
View File
@@ -0,0 +1,372 @@
/*
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 controller
import (
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"reflect"
"strings"
"testing"
"time"
"go.uber.org/zap"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
"github.com/fission/fission/pkg/controller/client"
ferror "github.com/fission/fission/pkg/error"
)
var g struct {
client *client.Client
}
func panicIf(err error) {
if err != nil {
log.Panicf("err: %v", err)
}
}
func assert(c bool, msg string) {
if !c {
log.Fatalf("assert failed: %v", msg)
}
}
func assertNameReuseFailure(err error, name string) {
assert(err != nil, "recreating "+name+" with same name must fail")
fe, ok := err.(ferror.Error)
assert(ok, "error must be a fission Error")
assert(fe.Code == ferror.ErrorNameExists, "error must be a name exists error")
}
func assertNotFoundFailure(err error, name string) {
assert(err != nil, "requesting a non-existent "+name+" must fail")
fe, ok := err.(ferror.Error)
assert(ok, "error must be a fission Error")
if fe.Code != ferror.ErrorNotFound {
log.Fatalf("error must be a not found error: %v", fe)
}
}
func assertCronSpecFails(err error) {
assert(err != nil, "using an invalid cron spec must fail")
ok := strings.Contains(err.Error(), "not a valid cron spec")
assert(ok, "invalid cron spec must fail")
}
func TestFunctionApi(t *testing.T) {
testFunc := &fv1.Function{
Metadata: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
},
Spec: fv1.FunctionSpec{
Environment: fv1.EnvironmentReference{
Name: "nodejs",
Namespace: metav1.NamespaceDefault,
},
Package: fv1.FunctionPackageRef{
FunctionName: "xxx",
PackageRef: fv1.PackageRef{
Namespace: metav1.NamespaceDefault,
Name: "xxx",
ResourceVersion: "12345",
},
},
},
}
_, err := g.client.FunctionGet(&metav1.ObjectMeta{
Name: testFunc.Metadata.Name,
Namespace: metav1.NamespaceDefault,
})
assertNotFoundFailure(err, "function")
m, err := g.client.FunctionCreate(testFunc)
panicIf(err)
defer func() {
err := g.client.FunctionDelete(m)
panicIf(err)
}()
_, err = g.client.FunctionCreate(testFunc)
assertNameReuseFailure(err, "function")
testFunc.Metadata.ResourceVersion = m.ResourceVersion
testFunc.Spec.Package.FunctionName = "yyy"
_, err = g.client.FunctionUpdate(testFunc)
panicIf(err)
testFunc.Metadata.ResourceVersion = ""
testFunc.Metadata.Name = "bar"
m2, err := g.client.FunctionCreate(testFunc)
panicIf(err)
defer g.client.FunctionDelete(m2)
funcs, err := g.client.FunctionList(metav1.NamespaceDefault)
panicIf(err)
assert(len(funcs) == 2, fmt.Sprintf("created two functions, but found %v", len(funcs)))
funcs_url := g.client.Url + "/v2/functions"
resp, err := http.Get(funcs_url)
panicIf(err)
defer resp.Body.Close()
assert(resp.StatusCode == 200, "http get status code on /v1/functions")
var found bool = false
for _, b := range resp.Header["Content-Type"] {
if b == "application/json; charset=utf-8" {
found = true
}
}
assert(found, "incorrect response content type")
}
func TestHTTPTriggerApi(t *testing.T) {
testTrigger := &fv1.HTTPTrigger{
Metadata: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
},
Spec: fv1.HTTPTriggerSpec{
Method: http.MethodGet,
RelativeURL: "/hello",
FunctionReference: fv1.FunctionReference{
Type: fv1.FunctionReferenceTypeFunctionName,
Name: "foo",
},
},
}
_, err := g.client.HTTPTriggerGet(&metav1.ObjectMeta{
Name: testTrigger.Metadata.Name,
Namespace: metav1.NamespaceDefault,
})
assertNotFoundFailure(err, "httptrigger")
m, err := g.client.HTTPTriggerCreate(testTrigger)
panicIf(err)
defer g.client.HTTPTriggerDelete(m)
_, err = g.client.HTTPTriggerCreate(testTrigger)
assertNameReuseFailure(err, "httptrigger")
tr, err := g.client.HTTPTriggerGet(m)
panicIf(err)
assert(testTrigger.Spec.Method == tr.Spec.Method &&
testTrigger.Spec.RelativeURL == tr.Spec.RelativeURL &&
testTrigger.Spec.FunctionReference.Type == tr.Spec.FunctionReference.Type &&
testTrigger.Spec.FunctionReference.Name == tr.Spec.FunctionReference.Name, "trigger should match after reading")
testTrigger.Metadata.ResourceVersion = m.ResourceVersion
testTrigger.Spec.RelativeURL = "/hi"
_, err = g.client.HTTPTriggerUpdate(testTrigger)
panicIf(err)
testTrigger.Metadata.ResourceVersion = ""
testTrigger.Metadata.Name = "yyy"
_, err = g.client.HTTPTriggerCreate(testTrigger)
assert(err != nil, "duplicate trigger should not be allowed")
testTrigger.Spec.RelativeURL = "/hi2"
m2, err := g.client.HTTPTriggerCreate(testTrigger)
panicIf(err)
defer g.client.HTTPTriggerDelete(m2)
ts, err := g.client.HTTPTriggerList(metav1.NamespaceDefault)
panicIf(err)
assert(len(ts) == 2, fmt.Sprintf("created two triggers, but found %v", len(ts)))
}
func TestEnvironmentApi(t *testing.T) {
testEnv := &fv1.Environment{
Metadata: metav1.ObjectMeta{
Name: "foo",
Namespace: metav1.NamespaceDefault,
},
Spec: fv1.EnvironmentSpec{
Runtime: fv1.Runtime{
Image: "gcr.io/xyz",
},
Resources: v1.ResourceRequirements{},
},
}
_, err := g.client.EnvironmentGet(&metav1.ObjectMeta{
Name: testEnv.Metadata.Name,
Namespace: metav1.NamespaceDefault,
})
assertNotFoundFailure(err, "environment")
m, err := g.client.EnvironmentCreate(testEnv)
panicIf(err)
defer g.client.EnvironmentDelete(m)
_, err = g.client.EnvironmentCreate(testEnv)
assertNameReuseFailure(err, "environment")
e, err := g.client.EnvironmentGet(m)
panicIf(err)
assert(reflect.DeepEqual(testEnv.Spec, e.Spec), "env should match after reading")
testEnv.Metadata.ResourceVersion = m.ResourceVersion
testEnv.Spec.Runtime.Image = "another-img"
_, err = g.client.EnvironmentUpdate(testEnv)
panicIf(err)
testEnv.Metadata.ResourceVersion = ""
testEnv.Metadata.Name = "bar"
m2, err := g.client.EnvironmentCreate(testEnv)
panicIf(err)
defer g.client.EnvironmentDelete(m2)
ts, err := g.client.EnvironmentList(metav1.NamespaceDefault)
panicIf(err)
assert(len(ts) == 2, fmt.Sprintf("created two envs, but found %v", len(ts)))
}
func TestWatchApi(t *testing.T) {
testWatch := &fv1.KubernetesWatchTrigger{
Metadata: metav1.ObjectMeta{
Name: "xxx",
Namespace: metav1.NamespaceDefault,
},
Spec: fv1.KubernetesWatchTriggerSpec{
Namespace: "default",
Type: "pod",
FunctionReference: fv1.FunctionReference{
Type: fv1.FunctionReferenceTypeFunctionName,
Name: "foo",
},
},
}
_, err := g.client.WatchGet(&metav1.ObjectMeta{
Name: testWatch.Metadata.Name,
Namespace: metav1.NamespaceDefault,
})
assertNotFoundFailure(err, "watch")
m, err := g.client.WatchCreate(testWatch)
panicIf(err)
defer g.client.WatchDelete(m)
_, err = g.client.WatchCreate(testWatch)
assertNameReuseFailure(err, "watch")
w, err := g.client.WatchGet(m)
panicIf(err)
assert(testWatch.Spec.Namespace == w.Spec.Namespace &&
testWatch.Spec.Type == w.Spec.Type &&
testWatch.Spec.FunctionReference.Type == w.Spec.FunctionReference.Type &&
testWatch.Spec.FunctionReference.Name == w.Spec.FunctionReference.Name, "watch should match after reading")
testWatch.Metadata.Name = "yyy"
m2, err := g.client.WatchCreate(testWatch)
panicIf(err)
defer g.client.WatchDelete(m2)
ws, err := g.client.WatchList(metav1.NamespaceDefault)
panicIf(err)
assert(len(ws) == 2, fmt.Sprintf("created two watches, but found %v", len(ws)))
}
func TestTimeTriggerApi(t *testing.T) {
testTrigger := &fv1.TimeTrigger{
Metadata: metav1.ObjectMeta{
Name: "xxx",
Namespace: metav1.NamespaceDefault,
},
Spec: fv1.TimeTriggerSpec{
Cron: "0 30 * * * *",
FunctionReference: fv1.FunctionReference{
Type: fv1.FunctionReferenceTypeFunctionName,
Name: "asdf",
},
},
}
_, err := g.client.TimeTriggerGet(&metav1.ObjectMeta{Name: testTrigger.Metadata.Name})
assertNotFoundFailure(err, "trigger")
m, err := g.client.TimeTriggerCreate(testTrigger)
panicIf(err)
defer g.client.TimeTriggerDelete(m)
_, err = g.client.TimeTriggerCreate(testTrigger)
assertNameReuseFailure(err, "trigger")
tr, err := g.client.TimeTriggerGet(m)
panicIf(err)
assert(testTrigger.Spec.Cron == tr.Spec.Cron &&
testTrigger.Spec.FunctionReference.Type == tr.Spec.FunctionReference.Type &&
testTrigger.Spec.FunctionReference.Name == tr.Spec.FunctionReference.Name, "trigger should match after reading")
testTrigger.Metadata.ResourceVersion = m.ResourceVersion
testTrigger.Spec.Cron = "@hourly"
_, err = g.client.TimeTriggerUpdate(testTrigger)
panicIf(err)
testTrigger.Metadata.ResourceVersion = ""
testTrigger.Metadata.Name = "yyy"
testTrigger.Spec.Cron = "Not valid cron spec"
_, err = g.client.TimeTriggerCreate(testTrigger)
assertCronSpecFails(err)
ts, err := g.client.TimeTriggerList(metav1.NamespaceDefault)
panicIf(err)
assert(len(ts) == 1, fmt.Sprintf("created two time triggers, but found %v", len(ts)))
}
func TestMain(m *testing.M) {
flag.Parse()
// skip test if no cluster available for testing
kubeconfig := os.Getenv("KUBECONFIG")
if len(kubeconfig) == 0 {
log.Println("Skipping test, no kubernetes cluster")
return
}
logger, err := zap.NewDevelopment()
panicIf(err)
go Start(logger, 8888, true)
time.Sleep(5 * time.Second)
g.client = client.MakeClient("http://localhost:8888")
resp, err := http.Get("http://localhost:8888/")
panicIf(err)
assert(resp.StatusCode == 200, "http get status code on root")
var found bool = false
for _, b := range resp.Header["Content-Type"] {
if b == "application/json; charset=utf-8" {
found = true
}
}
assert(found, "incorrect response content type")
_, err = ioutil.ReadAll(resp.Body)
panicIf(err)
os.Exit(m.Run())
}
+184
View File
@@ -0,0 +1,184 @@
/*
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 controller
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
config "github.com/fission/fission/pkg/featureconfig"
)
func (a *API) CanaryConfigApiCreate(w http.ResponseWriter, r *http.Request) {
featureErr := a.featureStatus[config.CanaryFeature]
if len(featureErr) > 0 {
a.respondWithError(w, ferror.MakeError(http.StatusInternalServerError, fmt.Sprintf("Error enabling canary feature: %v", featureErr)))
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var canaryCfg fv1.CanaryConfig
err = json.Unmarshal(body, &canaryCfg)
if err != nil {
a.logger.Error("failed to unmarshal request body", zap.Error(err), zap.Binary("body", body))
a.respondWithError(w, err)
return
}
canaryCfgNew, err := a.fissionClient.CanaryConfigs(canaryCfg.Metadata.Namespace).Create(&canaryCfg)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(canaryCfgNew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) CanaryConfigApiGet(w http.ResponseWriter, r *http.Request) {
featureErr := a.featureStatus[config.CanaryFeature]
if len(featureErr) > 0 {
a.respondWithError(w, ferror.MakeError(http.StatusInternalServerError, fmt.Sprintf("Error enabling canary feature: %v", featureErr)))
return
}
vars := mux.Vars(r)
name := vars["canaryConfig"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
canaryCfg, err := a.fissionClient.CanaryConfigs(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(canaryCfg)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) CanaryConfigApiList(w http.ResponseWriter, r *http.Request) {
featureErr := a.featureStatus[config.CanaryFeature]
if len(featureErr) > 0 {
a.respondWithError(w, ferror.MakeError(http.StatusInternalServerError, fmt.Sprintf("Error enabling canary feature: %v", featureErr)))
return
}
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
canaryCfgs, err := a.fissionClient.CanaryConfigs(ns).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(canaryCfgs.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) CanaryConfigApiUpdate(w http.ResponseWriter, r *http.Request) {
featureErr := a.featureStatus[config.CanaryFeature]
if len(featureErr) > 0 {
a.respondWithError(w, ferror.MakeError(http.StatusInternalServerError, fmt.Sprintf("Error enabling canary feature: %v", featureErr)))
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var c fv1.CanaryConfig
err = json.Unmarshal(body, &c)
if err != nil {
a.respondWithError(w, err)
return
}
canayCfgNew, err := a.fissionClient.CanaryConfigs(c.Metadata.Namespace).Update(&c)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(canayCfgNew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) CanaryConfigApiDelete(w http.ResponseWriter, r *http.Request) {
featureErr := a.featureStatus[config.CanaryFeature]
if len(featureErr) > 0 {
a.respondWithError(w, ferror.MakeError(http.StatusInternalServerError, fmt.Sprintf("Error enabling canary feature: %v", featureErr)))
return
}
vars := mux.Vars(r)
name := vars["canaryConfig"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.CanaryConfigs(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
+133
View File
@@ -0,0 +1,133 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
)
func (c *Client) CanaryConfigCreate(canaryConf *fv1.CanaryConfig) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(canaryConf)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("canaryconfigs"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) CanaryConfigGet(m *metav1.ObjectMeta) (*fv1.CanaryConfig, error) {
relativeUrl := fmt.Sprintf("canaryconfigs/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var canaryCfg fv1.CanaryConfig
err = json.Unmarshal(body, &canaryCfg)
if err != nil {
return nil, err
}
return &canaryCfg, nil
}
func (c *Client) CanaryConfigUpdate(canaryConf *fv1.CanaryConfig) (*metav1.ObjectMeta, error) {
reqbody, err := json.Marshal(canaryConf)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("canaryconfigs/%v", canaryConf.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) CanaryConfigDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("canaryconfigs/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) CanaryConfigList(ns string) ([]fv1.CanaryConfig, error) {
relativeUrl := fmt.Sprintf("canaryconfigs?namespace=%v", ns)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
canaryCfgs := make([]fv1.CanaryConfig, 0)
err = json.Unmarshal(body, &canaryCfgs)
if err != nil {
return nil, err
}
return canaryCfgs, nil
}
+115
View File
@@ -0,0 +1,115 @@
/*
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 client
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strings"
ferror "github.com/fission/fission/pkg/error"
"github.com/fission/fission/pkg/info"
)
type (
Client struct {
Url string
}
)
func MakeClient(serverUrl string) *Client {
return &Client{Url: strings.TrimSuffix(serverUrl, "/")}
}
func (c *Client) delete(relativeUrl string) error {
req, err := http.NewRequest("DELETE", c.url(relativeUrl), nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return errors.New("Delete failed")
} else {
return errors.New("Delete failed: " + string(body))
}
}
return nil
}
func (c *Client) put(relativeUrl string, contentType string, body []byte) (*http.Response, error) {
req, err := http.NewRequest("PUT", c.url(relativeUrl), bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-type", contentType)
return http.DefaultClient.Do(req)
}
func (c *Client) url(relativeUrl string) string {
return c.Url + "/v2/" + relativeUrl
}
func (c *Client) handleResponse(resp *http.Response) ([]byte, error) {
if resp.StatusCode != 200 {
return nil, ferror.MakeErrorFromHTTP(resp)
}
body, err := ioutil.ReadAll(resp.Body)
return body, err
}
func (c *Client) handleCreateResponse(resp *http.Response) ([]byte, error) {
if resp.StatusCode != 201 {
return nil, ferror.MakeErrorFromHTTP(resp)
}
body, err := ioutil.ReadAll(resp.Body)
return body, err
}
func (c *Client) ServerInfo() (*info.ServerInfo, error) {
url := fmt.Sprintf(c.Url)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
info := &info.ServerInfo{}
err = json.Unmarshal(body, info)
if err != nil {
return nil, err
}
return info, nil
}
+99
View File
@@ -0,0 +1,99 @@
/*
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 client
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (c *Client) SecretGet(m *metav1.ObjectMeta) (*apiv1.Secret, error) {
relativeUrl := fmt.Sprintf("secrets/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var secret apiv1.Secret
err = json.Unmarshal(body, &secret)
if err != nil {
return nil, err
}
return &secret, nil
}
func (c *Client) ConfigMapGet(m *metav1.ObjectMeta) (*apiv1.ConfigMap, error) {
relativeUrl := fmt.Sprintf("configmaps/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var configMap apiv1.ConfigMap
err = json.Unmarshal(body, &configMap)
if err != nil {
return nil, err
}
return &configMap, nil
}
func (c *Client) GetSvcURL(label string) (string, error) {
url := fmt.Sprintf("%s/proxy/svcname?"+label, c.Url)
resp, err := http.Get(url)
if err != nil {
return "", err
}
if resp == nil {
return "", fmt.Errorf("Failed to find service for given label: %v", label)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
storageSvc := string(body)
return storageSvc, err
}
+143
View File
@@ -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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
)
func (c *Client) EnvironmentCreate(env *fv1.Environment) (*metav1.ObjectMeta, error) {
err := env.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("Environment", err)
}
reqbody, err := json.Marshal(env)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("environments"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) EnvironmentGet(m *metav1.ObjectMeta) (*fv1.Environment, error) {
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var env fv1.Environment
err = json.Unmarshal(body, &env)
if err != nil {
return nil, err
}
return &env, nil
}
func (c *Client) EnvironmentUpdate(env *fv1.Environment) (*metav1.ObjectMeta, error) {
err := env.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("Environment", err)
}
reqbody, err := json.Marshal(env)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("environments/%v", env.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) EnvironmentDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) EnvironmentList(ns string) ([]fv1.Environment, error) {
relativeUrl := fmt.Sprintf("environments?namespace=%v", ns)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
envs := make([]fv1.Environment, 0)
err = json.Unmarshal(body, &envs)
if err != nil {
return nil, err
}
return envs, nil
}
+156
View File
@@ -0,0 +1,156 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
)
func (c *Client) FunctionCreate(f *fv1.Function) (*metav1.ObjectMeta, error) {
err := f.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("Function", err)
}
reqbody, err := json.Marshal(f)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("functions"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) FunctionGet(m *metav1.ObjectMeta) (*fv1.Function, error) {
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var f fv1.Function
err = json.Unmarshal(body, &f)
if err != nil {
return nil, err
}
return &f, nil
}
func (c *Client) FunctionGetRawDeployment(m *metav1.ObjectMeta) ([]byte, error) {
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
relativeUrl += fmt.Sprintf("&deploymentraw=1")
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
return c.handleResponse(resp)
}
func (c *Client) FunctionUpdate(f *fv1.Function) (*metav1.ObjectMeta, error) {
err := f.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("Function", err)
}
reqbody, err := json.Marshal(f)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("functions/%v", f.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) FunctionDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) FunctionList(functionNamespace string) ([]fv1.Function, error) {
relativeUrl := fmt.Sprintf("functions?namespace=%v", functionNamespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
funcs := make([]fv1.Function, 0)
err = json.Unmarshal(body, &funcs)
if err != nil {
return nil, err
}
return funcs, nil
}
+142
View File
@@ -0,0 +1,142 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
)
func (c *Client) HTTPTriggerCreate(t *fv1.HTTPTrigger) (*metav1.ObjectMeta, error) {
err := t.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("HTTPTrigger", err)
}
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/http"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) HTTPTriggerGet(m *metav1.ObjectMeta) (*fv1.HTTPTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t fv1.HTTPTrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) HTTPTriggerUpdate(t *fv1.HTTPTrigger) (*metav1.ObjectMeta, error) {
err := t.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("HTTPTrigger", err)
}
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/http/%v", t.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) HTTPTriggerDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) HTTPTriggerList(triggerNamespace string) ([]fv1.HTTPTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/http?namespace=%v", triggerNamespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]fv1.HTTPTrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
@@ -0,0 +1,117 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (c *Client) WatchCreate(w *fv1.KubernetesWatchTrigger) (*metav1.ObjectMeta, error) {
err := w.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("KubernetesWatchTrigger", err)
}
reqbody, err := json.Marshal(w)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("watches"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) WatchGet(m *metav1.ObjectMeta) (*fv1.KubernetesWatchTrigger, error) {
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var w fv1.KubernetesWatchTrigger
err = json.Unmarshal(body, &w)
if err != nil {
return nil, err
}
return &w, nil
}
func (c *Client) WatchUpdate(w *fv1.KubernetesWatchTrigger) (*metav1.ObjectMeta, error) {
return nil, ferror.MakeError(ferror.ErrorNotImplmented,
"watch update not implemented")
}
func (c *Client) WatchDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) WatchList(ns string) ([]fv1.KubernetesWatchTrigger, error) {
relativeUrl := fmt.Sprintf("watches?namespace=%v", ns)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
watches := make([]fv1.KubernetesWatchTrigger, 0)
err = json.Unmarshal(body, &watches)
if err != nil {
return nil, err
}
return watches, err
}
+147
View File
@@ -0,0 +1,147 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
)
func (c *Client) MessageQueueTriggerCreate(t *fv1.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
err := t.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("MessageQueueTrigger", err)
}
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/messagequeue"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) MessageQueueTriggerGet(m *metav1.ObjectMeta) (*fv1.MessageQueueTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t fv1.MessageQueueTrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) MessageQueueTriggerUpdate(mqTrigger *fv1.MessageQueueTrigger) (*metav1.ObjectMeta, error) {
err := mqTrigger.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("MessageQueueTrigger", err)
}
reqbody, err := json.Marshal(mqTrigger)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", mqTrigger.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) MessageQueueTriggerDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) MessageQueueTriggerList(mqType string, ns string) ([]fv1.MessageQueueTrigger, error) {
relativeUrl := "triggers/messagequeue"
if len(mqType) > 0 {
// TODO remove this, replace with field selector
relativeUrl += fmt.Sprintf("?mqtype=%v&namespace=%v", mqType, ns)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]fv1.MessageQueueTrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
+142
View File
@@ -0,0 +1,142 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
)
func (c *Client) PackageCreate(f *fv1.Package) (*metav1.ObjectMeta, error) {
err := f.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("Package", err)
}
reqbody, err := json.Marshal(f)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("packages"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) PackageGet(m *metav1.ObjectMeta) (*fv1.Package, error) {
relativeUrl := fmt.Sprintf("packages/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var f fv1.Package
err = json.Unmarshal(body, &f)
if err != nil {
return nil, err
}
return &f, nil
}
func (c *Client) PackageUpdate(f *fv1.Package) (*metav1.ObjectMeta, error) {
err := f.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("Package", err)
}
reqbody, err := json.Marshal(f)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("packages/%v", f.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) PackageDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("packages/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) PackageList(pkgNamespace string) ([]fv1.Package, error) {
relativeUrl := fmt.Sprintf("packages?namespace=%v", pkgNamespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
funcs := make([]fv1.Package, 0)
err = json.Unmarshal(body, &funcs)
if err != nil {
return nil, err
}
return funcs, nil
}
+238
View File
@@ -0,0 +1,238 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
"github.com/fission/fission/pkg/redis/build/gen"
)
func (c *Client) RecorderCreate(r *fv1.Recorder) (*metav1.ObjectMeta, error) {
err := r.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("Recorder", err)
}
reqbody, err := json.Marshal(r)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("recorders"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) RecorderGet(m *metav1.ObjectMeta) (*fv1.Recorder, error) {
relativeUrl := fmt.Sprintf("recorders/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var r fv1.Recorder
err = json.Unmarshal(body, &r)
if err != nil {
return nil, err
}
return &r, nil
}
func (c *Client) RecorderUpdate(recorder *fv1.Recorder) (*metav1.ObjectMeta, error) {
err := recorder.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("Recorder", err)
}
reqbody, err := json.Marshal(recorder)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("recorders/%v", recorder.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) RecorderDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("recorders/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) RecorderList(ns string) ([]fv1.Recorder, error) {
relativeUrl := "recorders"
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
recorders := make([]fv1.Recorder, 0)
err = json.Unmarshal(body, &recorders)
if err != nil {
return nil, err
}
return recorders, nil
}
// TODO: Move to different file?
func (c *Client) RecordsByFunction(function string) ([]*redisCache.RecordedEntry, error) {
relativeUrl := fmt.Sprintf("records/function/%v", function)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
records := make([]*redisCache.RecordedEntry, 0)
err = json.Unmarshal(body, &records)
if err != nil {
return nil, err
}
return records, nil
}
func (c *Client) RecordsAll() ([]*redisCache.RecordedEntry, error) {
relativeUrl := "records"
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
records := make([]*redisCache.RecordedEntry, 0)
err = json.Unmarshal(body, &records)
if err != nil {
return nil, err
}
return records, nil
}
func (c *Client) RecordsByTrigger(trigger string) ([]*redisCache.RecordedEntry, error) {
relativeUrl := fmt.Sprintf("records/trigger/%v", trigger)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
records := make([]*redisCache.RecordedEntry, 0)
err = json.Unmarshal(body, &records)
if err != nil {
return nil, err
}
return records, nil
}
func (c *Client) RecordsByTime(from string, to string) ([]*redisCache.RecordedEntry, error) {
relativeUrl := "records/time"
relativeUrl += fmt.Sprintf("?from=%v&to=%v", from, to)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
records := make([]*redisCache.RecordedEntry, 0)
err = json.Unmarshal(body, &records)
if err != nil {
return nil, err
}
return records, nil
}
+47
View File
@@ -0,0 +1,47 @@
/*
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 client
import (
"encoding/json"
"fmt"
"net/http"
)
func (c *Client) ReplayByReqUID(reqUID string) ([]string, error) {
relativeUrl := fmt.Sprintf("replay/%v", reqUID)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
replayed := make([]string, 0)
err = json.Unmarshal(body, &replayed)
if err != nil {
return nil, err
}
return replayed, nil
}
+142
View File
@@ -0,0 +1,142 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
)
func (c *Client) TimeTriggerCreate(t *fv1.TimeTrigger) (*metav1.ObjectMeta, error) {
err := t.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("TimeTrigger", err)
}
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/time"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) TimeTriggerGet(m *metav1.ObjectMeta) (*fv1.TimeTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t fv1.TimeTrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) TimeTriggerUpdate(t *fv1.TimeTrigger) (*metav1.ObjectMeta, error) {
err := t.Validate()
if err != nil {
return nil, fv1.AggregateValidationErrors("TimeTrigger", err)
}
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/time/%v", t.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m metav1.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) TimeTriggerDelete(m *metav1.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) TimeTriggerList(ns string) ([]fv1.TimeTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/time?namespace=%v", ns)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]fv1.TimeTrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
+67
View File
@@ -0,0 +1,67 @@
/*
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 controller
import (
"context"
"github.com/pkg/errors"
"go.uber.org/zap"
"k8s.io/client-go/kubernetes"
"github.com/fission/fission/pkg/canaryconfigmgr"
"github.com/fission/fission/pkg/crd"
config "github.com/fission/fission/pkg/featureconfig"
)
func ConfigCanaryFeature(context context.Context, logger *zap.Logger, fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset, featureConfig *config.FeatureConfig, featureStatus map[string]string) error {
// start the appropriate controller
if featureConfig.CanaryConfig.IsEnabled {
canaryCfgMgr, err := canaryconfigmgr.MakeCanaryConfigMgr(logger, fissionClient, kubeClient, fissionClient.GetCrdClient(),
featureConfig.CanaryConfig.PrometheusSvc)
if err != nil {
featureStatus[config.CanaryFeature] = err.Error()
return errors.Wrap(err, "failed to start canary config manager")
}
canaryCfgMgr.Run(context)
logger.Info("started canary config manager")
}
return nil
}
// ConfigureFeatures gets the feature config and configures the features that are enabled
func ConfigureFeatures(context context.Context, logger *zap.Logger, unitTestMode bool, fissionClient *crd.FissionClient, kubeClient *kubernetes.Clientset) (map[string]string, error) {
// set feature enabled to false if unitTestMode
if unitTestMode {
return nil, nil
}
// get the featureConfig from config map mounted onto the file system
featureConfig, err := config.GetFeatureConfig()
if err != nil {
logger.Error("error getting feature config", zap.Error(err))
return nil, err
}
featureStatus := make(map[string]string)
// configure respective features
// in the future when new optional features are added, we need to add corresponding feature handlers and invoke them here
err = ConfigCanaryFeature(context, logger, fissionClient, kubeClient, featureConfig, featureStatus)
return featureStatus, err
}
+49
View File
@@ -0,0 +1,49 @@
/*
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 controller
import (
"encoding/json"
"net/http"
"github.com/gorilla/mux"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (a *API) ConfigMapGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["configmap"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
configMap, err := a.kubernetesClient.CoreV1().ConfigMaps(ns).Get(name, metav1.GetOptions{})
if err != nil {
a.logger.Error("error getting config map", zap.Error(err), zap.String("config_map_name", name), zap.String("namespace", ns))
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(configMap)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
+60
View File
@@ -0,0 +1,60 @@
/*
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 controller
import (
"context"
"go.uber.org/zap"
"github.com/fission/fission/pkg/crd"
"github.com/fission/fission/pkg/utils"
)
func Start(logger *zap.Logger, port int, unitTestFlag bool) {
cLogger := logger.Named("controller")
// setup a signal handler for SIGTERM
utils.SetupStackTraceHandler()
fc, kc, apiExtClient, err := crd.MakeFissionClient()
if err != nil {
cLogger.Fatal("failed to connect to k8s API", zap.Error(err))
}
err = crd.EnsureFissionCRDs(cLogger, apiExtClient)
if err != nil {
cLogger.Fatal("failed to create fission CRDs", zap.Error(err))
}
err = fc.WaitForCRDs()
if err != nil {
cLogger.Fatal("error waiting for CRDs", zap.Error(err))
}
ctx, cancel := context.WithCancel(context.Background())
featureStatus, err := ConfigureFeatures(ctx, cLogger, unitTestFlag, fc, kc)
if err != nil {
cLogger.Info("error configuring features - proceeding without optional features", zap.Error(err))
}
defer cancel()
api, err := MakeAPI(cLogger, featureStatus)
if err != nil {
cLogger.Fatal("failed to start controller", zap.Error(err))
}
api.Serve(port)
}
+35
View File
@@ -0,0 +1,35 @@
/*
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 controller
import (
"go.uber.org/zap"
"github.com/fission/fission/pkg/crd"
)
func makeCRDBackedAPI(logger *zap.Logger) (*API, error) {
fissionClient, kubernetesClient, _, err := crd.MakeFissionClient()
if err != nil {
return nil, err
}
return &API{
logger: logger.Named("api"),
fissionClient: fissionClient,
kubernetesClient: kubernetesClient,
}, nil
}
+169
View File
@@ -0,0 +1,169 @@
/*
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 controller
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (a *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) {
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceAll
}
envs, err := a.fissionClient.Environments(ns).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(envs.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var env fv1.Environment
err = json.Unmarshal(body, &env)
if err != nil {
a.logger.Error("failed to unmarshal request body", zap.Error(err), zap.Binary("body", body))
a.respondWithError(w, err)
return
}
// check if namespace exists, if not create it.
err = a.createNsIfNotExists(env.Metadata.Namespace)
if err != nil {
a.respondWithError(w, err)
return
}
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Create(&env)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(enew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["environment"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
env, err := a.fissionClient.Environments(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(env)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) EnvironmentApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["environment"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var env fv1.Environment
err = json.Unmarshal(body, &env)
if err != nil {
a.respondWithError(w, err)
return
}
if name != env.Metadata.Name {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "Environment name doesn't match URL")
a.respondWithError(w, err)
return
}
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Update(&env)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(enew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) EnvironmentApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["environment"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.Environments(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
+271
View File
@@ -0,0 +1,271 @@
/*
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 controller
import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"sort"
"github.com/gorilla/mux"
"go.uber.org/zap"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
restclient "k8s.io/client-go/rest"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (a *API) getIstioServiceLabels(fnName string) map[string]string {
return map[string]string{
"functionName": fnName,
}
}
func (a *API) FunctionApiList(w http.ResponseWriter, r *http.Request) {
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceAll
}
funcs, err := a.fissionClient.Functions(ns).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(funcs.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var f fv1.Function
err = json.Unmarshal(body, &f)
if err != nil {
a.respondWithError(w, err)
return
}
// check if namespace exists, if not create it.
err = a.createNsIfNotExists(f.Metadata.Namespace)
if err != nil {
a.respondWithError(w, err)
return
}
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Create(&f)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(fnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["function"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
f, err := a.fissionClient.Functions(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(f)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) FunctionApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["function"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var f fv1.Function
err = json.Unmarshal(body, &f)
if err != nil {
a.respondWithError(w, err)
return
}
if name != f.Metadata.Name {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "Function name doesn't match URL")
a.respondWithError(w, err)
return
}
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Update(&f)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(fnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["function"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.Functions(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
// FunctionLogsApiPost establishes a proxy server to log database, and redirect
// query command send from client to database then proxy back the db response.
func (a *API) FunctionLogsApiPost(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
// get dbType from url
dbType := vars["dbType"]
// find correspond db http url
dbCnf := a.getLogDBConfig(dbType)
svcUrl, err := url.Parse(dbCnf.httpURL)
if err != nil {
a.logger.Error("failed parse url to establish proxy to database for function logs",
zap.Error(err),
zap.String("database_url", dbCnf.httpURL))
}
// set up proxy server director
director := func(req *http.Request) {
// only replace url Scheme and Host to remote influxDB
// and leave query string intact
req.URL.Scheme = svcUrl.Scheme
req.URL.Host = svcUrl.Host
req.URL.Path = svcUrl.Path
// set up http basic auth for database authentication
req.SetBasicAuth(dbCnf.username, dbCnf.password)
}
proxy := &httputil.ReverseProxy{
Director: director,
}
proxy.ServeHTTP(w, r)
}
// FunctionPodLogs : Get logs for a function directly from pod
func (a *API) FunctionPodLogs(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
fnName := vars["function"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = "fission-function"
}
f, err := a.fissionClient.Functions(ns).Get(fnName)
if err != nil {
a.respondWithError(w, err)
return
}
envName := f.Spec.Environment.Name
if err != nil {
a.respondWithError(w, err)
return
}
// Get function Pods first
selector := "functionName=" + fnName
podList, err := a.kubernetesClient.CoreV1().Pods(ns).List(metav1.ListOptions{LabelSelector: selector})
if err != nil {
a.respondWithError(w, err)
return
}
// Get the logs for last Pod executed
pods := podList.Items
sort.Slice(pods, func(i, j int) bool {
itime := pods[i].ObjectMeta.CreationTimestamp.Time
jtime := pods[j].ObjectMeta.CreationTimestamp.Time
return itime.After(jtime)
})
podLogOpts := apiv1.PodLogOptions{Container: envName} // Only the env container, not fetcher
var podLogsReq *restclient.Request
if len(pods) > 0 {
podLogsReq = a.kubernetesClient.CoreV1().Pods(ns).GetLogs(pods[0].ObjectMeta.Name, &podLogOpts)
} else {
a.respondWithError(w, errors.New("No active pods found"))
return
}
podLogs, err := podLogsReq.Stream()
if err != nil {
a.respondWithError(w, err)
return
}
defer podLogs.Close()
_, err = io.Copy(w, podLogs)
if err != nil {
a.respondWithError(w, err)
return
}
return
}
+198
View File
@@ -0,0 +1,198 @@
/*
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 controller
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (a *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceAll
}
triggers, err := a.fissionClient.HTTPTriggers(ns).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(triggers.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
// checkHTTPTriggerDuplicates checks whether the tuple (Method, Host, URL) is duplicate or not.
func (a *API) checkHTTPTriggerDuplicates(t *fv1.HTTPTrigger) error {
triggers, err := a.fissionClient.HTTPTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
return err
}
for _, ht := range triggers.Items {
if ht.Metadata.UID == t.Metadata.UID {
// Same resource. No need to check.
continue
}
if ht.Spec.RelativeURL == t.Spec.RelativeURL && ht.Spec.Method == t.Spec.Method && ht.Spec.Host == t.Spec.Host {
return ferror.MakeError(ferror.ErrorNameExists,
fmt.Sprintf("HTTPTrigger with same Host, URL & method already exists (%v)",
ht.Metadata.Name))
}
}
return nil
}
func (a *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var t fv1.HTTPTrigger
err = json.Unmarshal(body, &t)
if err != nil {
a.respondWithError(w, err)
return
}
// Ensure we don't have a duplicate HTTP route defined (same URL and method)
err = a.checkHTTPTriggerDuplicates(&t)
if err != nil {
a.respondWithError(w, err)
return
}
// check if namespace exists, if not create it.
err = a.createNsIfNotExists(t.Metadata.Namespace)
if err != nil {
a.respondWithError(w, err)
return
}
tnew, err := a.fissionClient.HTTPTriggers(t.Metadata.Namespace).Create(&t)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["httpTrigger"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
t, err := a.fissionClient.HTTPTriggers(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(t)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["httpTrigger"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var t fv1.HTTPTrigger
err = json.Unmarshal(body, &t)
if err != nil {
a.respondWithError(w, err)
return
}
if name != t.Metadata.Name {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "HTTPTrigger name doesn't match URL")
a.respondWithError(w, err)
return
}
err = a.checkHTTPTriggerDuplicates(&t)
if err != nil {
a.respondWithError(w, err)
return
}
tnew, err := a.fissionClient.HTTPTriggers(t.Metadata.Namespace).Update(&t)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) HTTPTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["httpTrigger"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.HTTPTriggers(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
+159
View File
@@ -0,0 +1,159 @@
/*
Copyright 2017 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 controller
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (a *API) MessageQueueTriggerApiList(w http.ResponseWriter, r *http.Request) {
//mqType := r.FormValue("mqtype") // ignored for now
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceAll
}
triggers, err := a.fissionClient.MessageQueueTriggers(ns).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(triggers.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var mqTrigger fv1.MessageQueueTrigger
err = json.Unmarshal(body, &mqTrigger)
if err != nil {
a.respondWithError(w, err)
return
}
// check if namespace exists, if not create it.
err = a.createNsIfNotExists(mqTrigger.Metadata.Namespace)
if err != nil {
a.respondWithError(w, err)
return
}
tnew, err := a.fissionClient.MessageQueueTriggers(mqTrigger.Metadata.Namespace).Create(&mqTrigger)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) MessageQueueTriggerApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["mqTrigger"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
mqTrigger, err := a.fissionClient.MessageQueueTriggers(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(mqTrigger)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) MessageQueueTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["mqTrigger"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var mqTrigger fv1.MessageQueueTrigger
err = json.Unmarshal(body, &mqTrigger)
if err != nil {
a.respondWithError(w, err)
return
}
if name != mqTrigger.Metadata.Name {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "Message queue trigger name doesn't match URL")
a.respondWithError(w, err)
return
}
tnew, err := a.fissionClient.MessageQueueTriggers(mqTrigger.Metadata.Namespace).Update(&mqTrigger)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) MessageQueueTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["mqTrigger"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.MessageQueueTriggers(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
+185
View File
@@ -0,0 +1,185 @@
/*
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 controller
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/dustin/go-humanize"
"github.com/fission/fission/pkg/types"
"github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (a *API) PackageApiList(w http.ResponseWriter, r *http.Request) {
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceAll
}
funcs, err := a.fissionClient.Packages(ns).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(funcs.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) PackageApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var f fv1.Package
err = json.Unmarshal(body, &f)
if err != nil {
a.respondWithError(w, err)
return
}
// Ensure size limits
if len(f.Spec.Source.Literal) > int(types.ArchiveLiteralSizeLimit) {
err := ferror.MakeError(ferror.ErrorInvalidArgument,
fmt.Sprintf("Package literal larger than %s", humanize.Bytes(uint64(types.ArchiveLiteralSizeLimit))))
a.respondWithError(w, err)
return
}
if len(f.Spec.Deployment.Literal) > int(types.ArchiveLiteralSizeLimit) {
err := ferror.MakeError(ferror.ErrorInvalidArgument,
fmt.Sprintf("Package literal larger than %s", humanize.Bytes(uint64(types.ArchiveLiteralSizeLimit))))
a.respondWithError(w, err)
return
}
// check if namespace exists, if not create it.
err = a.createNsIfNotExists(f.Metadata.Namespace)
if err != nil {
a.respondWithError(w, err)
return
}
fnew, err := a.fissionClient.Packages(f.Metadata.Namespace).Create(&f)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(fnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) PackageApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["package"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
raw := r.FormValue("raw") // just the deployment pkg
f, err := a.fissionClient.Packages(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
var resp []byte
if raw != "" {
resp = []byte(f.Spec.Deployment.Literal)
} else {
resp, err = json.Marshal(f)
if err != nil {
a.respondWithError(w, err)
return
}
}
a.respondWithSuccess(w, resp)
}
func (a *API) PackageApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["package"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var f fv1.Package
err = json.Unmarshal(body, &f)
if err != nil {
a.respondWithError(w, err)
return
}
if name != f.Metadata.Name {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "Package name doesn't match URL")
a.respondWithError(w, err)
return
}
fnew, err := a.fissionClient.Packages(f.Metadata.Namespace).Update(&f)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(fnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) PackageApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["package"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.Packages(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
+148
View File
@@ -0,0 +1,148 @@
/*
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 controller
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (a *API) RecorderApiList(w http.ResponseWriter, r *http.Request) {
recorders, err := a.fissionClient.Recorders(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(recorders.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) RecorderApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var recorder fv1.Recorder
err = json.Unmarshal(body, &recorder)
if err != nil {
a.respondWithError(w, err)
return
}
tnew, err := a.fissionClient.Recorders(recorder.Metadata.Namespace).Create(&recorder)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) RecorderApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["recorder"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
recorder, err := a.fissionClient.Recorders(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(recorder)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) RecorderApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["recorder"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var recorder fv1.Recorder
err = json.Unmarshal(body, &recorder)
if err != nil {
a.respondWithError(w, err)
return
}
if name != recorder.Metadata.Name {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "Recorder name doesn't match URL")
a.respondWithError(w, err)
return
}
rnew, err := a.fissionClient.Recorders(recorder.Metadata.Namespace).Update(&recorder)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(rnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) RecorderApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["recorder"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.Recorders(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
+95
View File
@@ -0,0 +1,95 @@
/*
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 controller
import (
"net/http"
"github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/pkg/redis"
)
func (a *API) RecordsApiListAll(w http.ResponseWriter, r *http.Request) {
resp, err := redis.RecordsListAll(a.logger.Named("redis"))
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) RecordsApiFilterByFunction(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
query := vars["function"]
recorders, err := a.fissionClient.Recorders(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
triggers, err := a.fissionClient.HTTPTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := redis.RecordsFilterByFunction(a.logger.Named("redis"), query, recorders, triggers)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) RecordsApiFilterByTrigger(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
query := vars["trigger"]
recorders, err := a.fissionClient.Recorders(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
triggers, err := a.fissionClient.HTTPTriggers(metav1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := redis.RecordsFilterByTrigger(a.logger.Named("redis"), query, recorders, triggers)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) RecordsApiFilterByTime(w http.ResponseWriter, r *http.Request) {
from := r.FormValue("from")
to := r.FormValue("to")
resp, err := redis.RecordsFilterByTime(a.logger.Named("redis"), from, to)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
+40
View File
@@ -0,0 +1,40 @@
/*
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 controller
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/fission/fission/pkg/redis"
)
func (a *API) ReplayByReqUID(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
queriedID := vars["reqUID"]
routerUrl := fmt.Sprintf("http://router.%v", podNamespace)
resp, err := redis.ReplayByReqUID(a.logger, routerUrl, queriedID)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
+52
View File
@@ -0,0 +1,52 @@
/*
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 controller
import (
"encoding/json"
"net/http"
"github.com/gorilla/mux"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (a *API) SecretGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["secret"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
secret, err := a.kubernetesClient.CoreV1().Secrets(ns).Get(name, metav1.GetOptions{})
if err != nil {
a.logger.Error("error getting secret",
zap.Error(err),
zap.String("secret_name", name),
zap.String("namespace", ns))
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(secret)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
+46
View File
@@ -0,0 +1,46 @@
/*
Copyright 2017 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 controller
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"go.uber.org/zap"
)
func (api *API) StorageServiceProxy(w http.ResponseWriter, r *http.Request) {
u := api.storageServiceUrl
ssUrl, err := url.Parse(u)
if err != nil {
e := "error parsing url"
api.logger.Error(e, zap.Error(err), zap.String("url", u))
http.Error(w, fmt.Sprintf("%s %s: %v", e, u, err), http.StatusInternalServerError)
return
}
director := func(req *http.Request) {
req.URL.Scheme = ssUrl.Scheme
req.URL.Host = ssUrl.Host
req.URL.Path = "/v1/archive"
}
proxy := &httputil.ReverseProxy{
Director: director,
}
proxy.ServeHTTP(w, r)
}
+180
View File
@@ -0,0 +1,180 @@
/*
Copyright 2017 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 controller
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
"github.com/robfig/cron"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (a *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) {
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceAll
}
triggers, err := a.fissionClient.TimeTriggers(ns).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(triggers.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var t fv1.TimeTrigger
err = json.Unmarshal(body, &t)
if err != nil {
a.respondWithError(w, err)
return
}
// validate
_, err = cron.Parse(t.Spec.Cron)
if err != nil {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
a.respondWithError(w, err)
return
}
// check if namespace exists, if not create it.
err = a.createNsIfNotExists(t.Metadata.Namespace)
if err != nil {
a.respondWithError(w, err)
return
}
tnew, err := a.fissionClient.TimeTriggers(t.Metadata.Namespace).Create(&t)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["timeTrigger"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
t, err := a.fissionClient.TimeTriggers(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(t)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) TimeTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["timeTrigger"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var t fv1.TimeTrigger
err = json.Unmarshal(body, &t)
if err != nil {
a.respondWithError(w, err)
return
}
if name != t.Metadata.Name {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "TimeTrigger name doesn't match URL")
a.respondWithError(w, err)
return
}
_, err = cron.Parse(t.Spec.Cron)
if err != nil {
err = ferror.MakeError(ferror.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
a.respondWithError(w, err)
return
}
tnew, err := a.fissionClient.TimeTriggers(t.Metadata.Namespace).Update(&t)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) TimeTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["timeTrigger"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.TimeTriggers(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
+134
View File
@@ -0,0 +1,134 @@
/*
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 controller
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
ferror "github.com/fission/fission/pkg/error"
)
func (a *API) WatchApiList(w http.ResponseWriter, r *http.Request) {
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceAll
}
watches, err := a.fissionClient.KubernetesWatchTriggers(ns).List(metav1.ListOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(watches.Items)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.respondWithError(w, err)
return
}
var watch fv1.KubernetesWatchTrigger
err = json.Unmarshal(body, &watch)
if err != nil {
a.respondWithError(w, err)
return
}
// TODO check for duplicate watches
// TODO check for duplicate watches -> we probably wont need it?
// check if namespace exists, if not create it.
err = a.createNsIfNotExists(watch.Metadata.Namespace)
if err != nil {
a.respondWithError(w, err)
return
}
wnew, err := a.fissionClient.KubernetesWatchTriggers(watch.Metadata.Namespace).Create(&watch)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(wnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
a.respondWithSuccess(w, resp)
}
func (a *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["watch"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
watch, err := a.fissionClient.KubernetesWatchTriggers(ns).Get(name)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(watch)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (a *API) WatchApiUpdate(w http.ResponseWriter, r *http.Request) {
a.respondWithError(w, ferror.MakeError(ferror.ErrorNotImplmented,
"Not implemented"))
}
func (a *API) WatchApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["watch"]
ns := a.extractQueryParamFromRequest(r, "namespace")
if len(ns) == 0 {
ns = metav1.NamespaceDefault
}
err := a.fissionClient.KubernetesWatchTriggers(ns).Delete(name, &metav1.DeleteOptions{})
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, []byte(""))
}
+32
View File
@@ -0,0 +1,32 @@
package controller
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"github.com/gorilla/mux"
)
func (api *API) WorkflowApiserverProxy(w http.ResponseWriter, r *http.Request) {
u := api.workflowApiUrl
ssUrl, err := url.Parse(u)
if err != nil {
msg := fmt.Sprintf("Error parsing url %v: %v", u, err)
http.Error(w, msg, http.StatusInternalServerError)
return
}
vars := mux.Vars(r)
path := fmt.Sprintf("/%s", vars["path"])
director := func(req *http.Request) {
req.URL.Scheme = ssUrl.Scheme
req.URL.Host = ssUrl.Host
req.URL.Path = path
}
proxy := &httputil.ReverseProxy{
Director: director,
}
proxy.ServeHTTP(w, r)
}