Switch Fission's storage over to the new CustomResourceDefinitions, from the deprecated ThirdPartyResources. This allows us to be compatible with Kubernets 1.8 and onwards. This also adds a CLI tool for dumping state from an old fission version and restoring state into new CRDs. The storage service is unaffected by this change.
446 lines
11 KiB
Go
446 lines
11 KiB
Go
/*
|
|
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 crd
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/rest"
|
|
|
|
"github.com/fission/fission"
|
|
)
|
|
|
|
func panicIf(err error) {
|
|
if err != nil {
|
|
log.Panicf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
func functionTests(crdClient *rest.RESTClient) {
|
|
// sample function object
|
|
function := &Function{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Function",
|
|
APIVersion: "fission.io/v1",
|
|
},
|
|
Metadata: metav1.ObjectMeta{
|
|
Name: "hello",
|
|
Namespace: metav1.NamespaceDefault,
|
|
},
|
|
Spec: fission.FunctionSpec{
|
|
Package: fission.FunctionPackageRef{
|
|
PackageRef: fission.PackageRef{
|
|
Name: "foo",
|
|
Namespace: "bar",
|
|
},
|
|
FunctionName: "hello",
|
|
},
|
|
Environment: fission.EnvironmentReference{
|
|
Name: "xxx",
|
|
},
|
|
},
|
|
}
|
|
|
|
// Test function CRUD
|
|
fi := MakeFunctionInterface(crdClient, metav1.NamespaceDefault)
|
|
|
|
// cleanup from old crashed tests, ignore errors
|
|
fi.Delete(function.Metadata.Name, nil)
|
|
|
|
// create
|
|
f, err := fi.Create(function)
|
|
panicIf(err)
|
|
if f.Metadata.Name != function.Metadata.Name {
|
|
log.Panicf("Bad result from create: %v", f)
|
|
}
|
|
|
|
// read
|
|
f, err = fi.Get(function.Metadata.Name)
|
|
panicIf(err)
|
|
if f.Spec.Environment.Name != function.Spec.Environment.Name {
|
|
log.Panicf("Bad result from Get: %v", f)
|
|
}
|
|
|
|
log.Printf("f.Metadata = %#v", f.Metadata)
|
|
|
|
// update
|
|
function.Metadata.ResourceVersion = f.Metadata.ResourceVersion
|
|
function.Spec.Environment.Name = "yyy"
|
|
f, err = fi.Update(function)
|
|
panicIf(err)
|
|
|
|
log.Printf("f.Metadata = %#v", f.Metadata)
|
|
|
|
// list
|
|
fl, err := fi.List(metav1.ListOptions{})
|
|
panicIf(err)
|
|
if len(fl.Items) != 1 {
|
|
log.Panicf("wrong count from function list: %v", len(fl.Items))
|
|
}
|
|
if fl.Items[0].Spec.Environment.Name != f.Spec.Environment.Name {
|
|
log.Panicf("bad object from list: %v", fl.Items[0])
|
|
}
|
|
|
|
// delete
|
|
err = fi.Delete(f.Metadata.Name, nil)
|
|
panicIf(err)
|
|
|
|
// start a watch
|
|
wi, err := fi.Watch(metav1.ListOptions{})
|
|
panicIf(err)
|
|
|
|
start := time.Now()
|
|
function.Metadata.ResourceVersion = ""
|
|
f, err = fi.Create(function)
|
|
panicIf(err)
|
|
defer fi.Delete(f.Metadata.Name, nil)
|
|
|
|
// assert that we get a watch event for the new function
|
|
recvd := false
|
|
select {
|
|
case <-time.NewTimer(1 * time.Second).C:
|
|
if !recvd {
|
|
log.Panicf("Didn't get watch event")
|
|
}
|
|
case ev := <-wi.ResultChan():
|
|
wf, ok := ev.Object.(*Function)
|
|
if !ok {
|
|
log.Panicf("Can't cast to Function")
|
|
}
|
|
if wf.Spec.Environment.Name != function.Spec.Environment.Name {
|
|
log.Panicf("Bad object from watch: %#v", wf)
|
|
}
|
|
log.Printf("watch event took %v", time.Now().Sub(start))
|
|
recvd = true
|
|
}
|
|
|
|
}
|
|
|
|
func environmentTests(crdClient *rest.RESTClient) {
|
|
// sample environment object
|
|
environment := &Environment{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Environment",
|
|
APIVersion: "fission.io/v1",
|
|
},
|
|
Metadata: metav1.ObjectMeta{
|
|
Name: "hello",
|
|
Namespace: metav1.NamespaceDefault,
|
|
},
|
|
Spec: fission.EnvironmentSpec{
|
|
Runtime: fission.Runtime{
|
|
Image: "xxx",
|
|
},
|
|
Builder: fission.Builder{
|
|
Image: "yyy",
|
|
Command: "zzz",
|
|
},
|
|
},
|
|
}
|
|
|
|
// Test environment CRUD
|
|
ei := MakeEnvironmentInterface(crdClient, metav1.NamespaceDefault)
|
|
|
|
// cleanup from old crashed tests, ignore errors
|
|
ei.Delete(environment.Metadata.Name, nil)
|
|
|
|
// create
|
|
e, err := ei.Create(environment)
|
|
panicIf(err)
|
|
if e.Metadata.Name != environment.Metadata.Name {
|
|
log.Panicf("Bad result from create: %v", e)
|
|
}
|
|
|
|
// read
|
|
e, err = ei.Get(environment.Metadata.Name)
|
|
panicIf(err)
|
|
if len(e.Spec.Runtime.Image) != len(environment.Spec.Runtime.Image) {
|
|
log.Panicf("Bad result from Get: %#v", e)
|
|
}
|
|
|
|
// update
|
|
environment.Metadata.ResourceVersion = e.Metadata.ResourceVersion
|
|
environment.Spec.Runtime.Image = "www"
|
|
e, err = ei.Update(environment)
|
|
panicIf(err)
|
|
|
|
// list
|
|
el, err := ei.List(metav1.ListOptions{})
|
|
panicIf(err)
|
|
if len(el.Items) != 1 {
|
|
log.Panicf("wrong count from environment list: %v", len(el.Items))
|
|
}
|
|
if el.Items[0].Spec.Runtime.Image != e.Spec.Runtime.Image {
|
|
log.Panicf("bad object from list: %v", el.Items[0])
|
|
}
|
|
|
|
// delete
|
|
err = ei.Delete(e.Metadata.Name, nil)
|
|
panicIf(err)
|
|
|
|
// start a watch
|
|
wi, err := ei.Watch(metav1.ListOptions{})
|
|
panicIf(err)
|
|
|
|
start := time.Now()
|
|
environment.Metadata.ResourceVersion = ""
|
|
e, err = ei.Create(environment)
|
|
panicIf(err)
|
|
defer ei.Delete(e.Metadata.Name, nil)
|
|
|
|
// assert that we get a watch event for the new environment
|
|
recvd := false
|
|
select {
|
|
case <-time.NewTimer(1 * time.Second).C:
|
|
if !recvd {
|
|
log.Panicf("Didn't get watch event")
|
|
}
|
|
case ev := <-wi.ResultChan():
|
|
obj, ok := ev.Object.(*Environment)
|
|
if !ok {
|
|
log.Panicf("Can't cast to Environment")
|
|
}
|
|
if obj.Spec.Runtime.Image != environment.Spec.Runtime.Image {
|
|
log.Panicf("Bad object from watch: %#v", obj)
|
|
}
|
|
log.Printf("watch event took %v", time.Now().Sub(start))
|
|
recvd = true
|
|
}
|
|
|
|
}
|
|
|
|
func httpTriggerTests(crdClient *rest.RESTClient) {
|
|
// sample httpTrigger object
|
|
httpTrigger := &HTTPTrigger{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "HTTPTrigger",
|
|
APIVersion: "fission.io/v1",
|
|
},
|
|
Metadata: metav1.ObjectMeta{
|
|
Name: "hello",
|
|
Namespace: metav1.NamespaceDefault,
|
|
},
|
|
Spec: fission.HTTPTriggerSpec{
|
|
RelativeURL: "/hi",
|
|
Method: "GET",
|
|
FunctionReference: fission.FunctionReference{
|
|
Type: fission.FunctionReferenceTypeFunctionName,
|
|
Name: "hello",
|
|
},
|
|
},
|
|
}
|
|
|
|
// Test httpTrigger CRUD
|
|
ei := MakeHTTPTriggerInterface(crdClient, metav1.NamespaceDefault)
|
|
|
|
// cleanup from old crashed tests, ignore errors
|
|
ei.Delete(httpTrigger.Metadata.Name, nil)
|
|
|
|
// create
|
|
e, err := ei.Create(httpTrigger)
|
|
panicIf(err)
|
|
if e.Metadata.Name != httpTrigger.Metadata.Name {
|
|
log.Panicf("Bad result from create: %v", e)
|
|
}
|
|
|
|
// read
|
|
e, err = ei.Get(httpTrigger.Metadata.Name)
|
|
panicIf(err)
|
|
if len(e.Spec.Method) != len(httpTrigger.Spec.Method) {
|
|
log.Panicf("Bad result from Get: %#v", e)
|
|
}
|
|
|
|
// update
|
|
httpTrigger.Metadata.ResourceVersion = e.Metadata.ResourceVersion
|
|
httpTrigger.Spec.Method = "POST"
|
|
e, err = ei.Update(httpTrigger)
|
|
panicIf(err)
|
|
|
|
// list
|
|
el, err := ei.List(metav1.ListOptions{})
|
|
panicIf(err)
|
|
if len(el.Items) != 1 {
|
|
log.Panicf("wrong count from http trigger list: %v", len(el.Items))
|
|
}
|
|
if el.Items[0].Spec.Method != e.Spec.Method {
|
|
log.Panicf("bad object from list: %v", el.Items[0])
|
|
}
|
|
|
|
// delete
|
|
err = ei.Delete(e.Metadata.Name, nil)
|
|
panicIf(err)
|
|
|
|
// start a watch
|
|
wi, err := ei.Watch(metav1.ListOptions{})
|
|
panicIf(err)
|
|
|
|
start := time.Now()
|
|
httpTrigger.Metadata.ResourceVersion = ""
|
|
e, err = ei.Create(httpTrigger)
|
|
panicIf(err)
|
|
defer ei.Delete(e.Metadata.Name, nil)
|
|
|
|
// assert that we get a watch event for the new httpTrigger
|
|
recvd := false
|
|
select {
|
|
case <-time.NewTimer(1 * time.Second).C:
|
|
if !recvd {
|
|
log.Panicf("Didn't get watch event")
|
|
}
|
|
case ev := <-wi.ResultChan():
|
|
obj, ok := ev.Object.(*HTTPTrigger)
|
|
if !ok {
|
|
log.Panicf("Can't cast to HTTPTrigger")
|
|
}
|
|
if obj.Spec.Method != httpTrigger.Spec.Method {
|
|
log.Panicf("Bad object from watch: %#v", obj)
|
|
}
|
|
log.Printf("watch event took %v", time.Now().Sub(start))
|
|
recvd = true
|
|
}
|
|
|
|
}
|
|
|
|
func kubernetesWatchTriggerTests(crdClient *rest.RESTClient) {
|
|
// sample kubernetesWatchTrigger object
|
|
kubernetesWatchTrigger := &KubernetesWatchTrigger{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "KubernetesWatchTrigger",
|
|
APIVersion: "fission.io/v1",
|
|
},
|
|
Metadata: metav1.ObjectMeta{
|
|
Name: "hello",
|
|
Namespace: metav1.NamespaceDefault,
|
|
},
|
|
Spec: fission.KubernetesWatchTriggerSpec{
|
|
Namespace: "foo",
|
|
Type: "pod",
|
|
LabelSelector: map[string]string{
|
|
"x": "y",
|
|
},
|
|
FunctionReference: fission.FunctionReference{
|
|
Type: fission.FunctionReferenceTypeFunctionName,
|
|
Name: "foo",
|
|
},
|
|
},
|
|
}
|
|
|
|
// Test kubernetesWatchTrigger CRUD
|
|
ei := MakeKubernetesWatchTriggerInterface(crdClient, metav1.NamespaceDefault)
|
|
|
|
// cleanup from old crashed tests, ignore errors
|
|
ei.Delete(kubernetesWatchTrigger.Metadata.Name, nil)
|
|
|
|
// create
|
|
e, err := ei.Create(kubernetesWatchTrigger)
|
|
panicIf(err)
|
|
if e.Metadata.Name != kubernetesWatchTrigger.Metadata.Name {
|
|
log.Panicf("Bad result from create: %v", e)
|
|
}
|
|
|
|
// read
|
|
e, err = ei.Get(kubernetesWatchTrigger.Metadata.Name)
|
|
panicIf(err)
|
|
if e.Spec.Type != kubernetesWatchTrigger.Spec.Type {
|
|
log.Panicf("Bad result from Get: %#v", e)
|
|
}
|
|
|
|
// update
|
|
kubernetesWatchTrigger.Metadata.ResourceVersion = e.Metadata.ResourceVersion
|
|
kubernetesWatchTrigger.Spec.Type = "service"
|
|
e, err = ei.Update(kubernetesWatchTrigger)
|
|
panicIf(err)
|
|
|
|
// list
|
|
el, err := ei.List(metav1.ListOptions{})
|
|
panicIf(err)
|
|
if len(el.Items) != 1 {
|
|
log.Panicf("wrong count from kubeWatcher list: %v", len(el.Items))
|
|
}
|
|
if el.Items[0].Spec.Type != e.Spec.Type {
|
|
log.Panicf("bad object from list: %v", el.Items[0])
|
|
}
|
|
|
|
// delete
|
|
err = ei.Delete(e.Metadata.Name, nil)
|
|
panicIf(err)
|
|
|
|
// start a watch
|
|
wi, err := ei.Watch(metav1.ListOptions{})
|
|
panicIf(err)
|
|
|
|
start := time.Now()
|
|
kubernetesWatchTrigger.Metadata.ResourceVersion = ""
|
|
e, err = ei.Create(kubernetesWatchTrigger)
|
|
panicIf(err)
|
|
defer ei.Delete(e.Metadata.Name, nil)
|
|
|
|
// assert that we get a watch event for the new kubernetesWatchTrigger
|
|
recvd := false
|
|
select {
|
|
case <-time.NewTimer(1 * time.Second).C:
|
|
if !recvd {
|
|
log.Panicf("Didn't get watch event")
|
|
}
|
|
case ev := <-wi.ResultChan():
|
|
obj, ok := ev.Object.(*KubernetesWatchTrigger)
|
|
if !ok {
|
|
log.Panicf("Can't cast to KubernetesWatchTrigger")
|
|
}
|
|
if obj.Spec.Type != kubernetesWatchTrigger.Spec.Type {
|
|
log.Panicf("Bad object from watch: %#v", obj)
|
|
}
|
|
log.Printf("watch event took %v", time.Now().Sub(start))
|
|
recvd = true
|
|
}
|
|
|
|
}
|
|
|
|
func TestCrd(t *testing.T) {
|
|
// skip test if no cluster available for testing
|
|
kubeconfig := os.Getenv("KUBECONFIG")
|
|
if len(kubeconfig) == 0 {
|
|
log.Println("Skipping test, no kubernetes cluster")
|
|
return
|
|
}
|
|
|
|
// Create the client config. Needs the KUBECONFIG env var to
|
|
// point at a valid kubeconfig.
|
|
config, _, apiExtClient, err := GetKubernetesClient()
|
|
panicIf(err)
|
|
|
|
// init our types
|
|
err = EnsureFissionCRDs(apiExtClient)
|
|
panicIf(err)
|
|
|
|
// rest client with knowledge about our crd types
|
|
crdClient, err := GetCrdClient(config)
|
|
panicIf(err)
|
|
|
|
err = waitForCRDs(crdClient)
|
|
panicIf(err)
|
|
|
|
functionTests(crdClient)
|
|
environmentTests(crdClient)
|
|
httpTriggerTests(crdClient)
|
|
kubernetesWatchTriggerTests(crdClient)
|
|
}
|