2014-11-02 20:52:31 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2014-11-02 20:52:31 +00:00
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 master
import (
2015-08-20 05:08:26 +00:00
"bytes"
"encoding/json"
2015-09-03 17:35:04 +00:00
"errors"
"fmt"
2015-08-19 18:02:01 +00:00
"io/ioutil"
2015-09-03 17:35:04 +00:00
"net"
2015-08-19 18:02:01 +00:00
"net/http"
"net/http/httptest"
2015-09-03 17:35:04 +00:00
"os"
"path/filepath"
2015-08-20 05:08:26 +00:00
"reflect"
2014-11-02 20:52:31 +00:00
"testing"
2015-09-03 17:35:04 +00:00
"time"
2014-11-02 20:52:31 +00:00
2015-09-03 17:35:04 +00:00
"github.com/emicklei/go-restful"
"github.com/stretchr/testify/assert"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/latest"
2015-09-03 17:35:04 +00:00
"k8s.io/kubernetes/pkg/api/meta"
2015-09-11 00:20:53 +00:00
"k8s.io/kubernetes/pkg/api/testapi"
2015-09-09 21:59:11 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-09-03 17:35:04 +00:00
"k8s.io/kubernetes/pkg/api/v1"
2015-09-09 22:46:06 +00:00
"k8s.io/kubernetes/pkg/apis/experimental"
2015-09-09 22:49:26 +00:00
"k8s.io/kubernetes/pkg/apiserver"
client "k8s.io/kubernetes/pkg/client/unversioned"
2015-09-03 17:35:04 +00:00
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/endpoint"
"k8s.io/kubernetes/pkg/registry/namespace"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/registry/registrytest"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
2015-09-03 17:35:04 +00:00
"k8s.io/kubernetes/pkg/util"
2014-11-02 20:52:31 +00:00
)
2015-09-03 17:35:04 +00:00
// setUp is a convience function for setting up for (most) tests.
func setUp ( t * testing . T ) ( Master , Config , * assert . Assertions ) {
2014-11-02 20:52:31 +00:00
master := Master { }
config := Config { }
fakeClient := tools . NewFakeEtcdClient ( t )
fakeClient . Machines = [ ] string { "http://machine1:4001" , "http://machine2" , "http://machine3:4003" }
2015-09-11 00:20:53 +00:00
config . DatabaseStorage = etcdstorage . NewEtcdStorage ( fakeClient , testapi . Default . Codec ( ) , etcdtest . PathPrefix ( ) )
2015-09-11 06:37:26 +00:00
config . ExpDatabaseStorage = etcdstorage . NewEtcdStorage ( fakeClient , testapi . Experimental . Codec ( ) , etcdtest . PathPrefix ( ) )
2014-11-02 20:52:31 +00:00
2015-09-09 14:18:17 +00:00
master . nodeRegistry = registrytest . NewNodeRegistry ( [ ] string { "node1" , "node2" } , api . NodeResources { } )
2014-11-02 20:52:31 +00:00
2015-09-03 17:35:04 +00:00
return master , config , assert . New ( t )
}
// TestNew verifies that the New function returns a Master
// using the configuration properly.
func TestNew ( t * testing . T ) {
_ , config , assert := setUp ( t )
config . KubeletClient = client . FakeKubeletClient { }
master := New ( & config )
// Verify many of the variables match their config counterparts
assert . Equal ( master . enableCoreControllers , config . EnableCoreControllers )
assert . Equal ( master . enableLogsSupport , config . EnableLogsSupport )
assert . Equal ( master . enableUISupport , config . EnableUISupport )
assert . Equal ( master . enableSwaggerSupport , config . EnableSwaggerSupport )
assert . Equal ( master . enableSwaggerSupport , config . EnableSwaggerSupport )
assert . Equal ( master . enableProfiling , config . EnableProfiling )
assert . Equal ( master . apiPrefix , config . APIPrefix )
2015-09-14 22:30:32 +00:00
assert . Equal ( master . apiGroupPrefix , config . APIGroupPrefix )
2015-09-03 17:35:04 +00:00
assert . Equal ( master . corsAllowedOriginList , config . CorsAllowedOriginList )
assert . Equal ( master . authenticator , config . Authenticator )
assert . Equal ( master . authorizer , config . Authorizer )
assert . Equal ( master . admissionControl , config . AdmissionControl )
assert . Equal ( master . v1 , ! config . DisableV1 )
assert . Equal ( master . exp , config . EnableExp )
assert . Equal ( master . requestContextMapper , config . RequestContextMapper )
assert . Equal ( master . cacheTimeout , config . CacheTimeout )
assert . Equal ( master . masterCount , config . MasterCount )
assert . Equal ( master . externalHost , config . ExternalHost )
assert . Equal ( master . clusterIP , config . PublicAddress )
assert . Equal ( master . publicReadWritePort , config . ReadWritePort )
assert . Equal ( master . serviceReadWriteIP , config . ServiceReadWriteIP )
assert . Equal ( master . installSSHKey , config . InstallSSHKey )
}
// TestNewEtcdStorage verifies that the usage of NewEtcdStorage reacts properly when
// the correct data is input
func TestNewEtcdStorage ( t * testing . T ) {
assert := assert . New ( t )
fakeClient := tools . NewFakeEtcdClient ( t )
// Pass case
2015-09-12 22:27:05 +00:00
_ , err := NewEtcdStorage ( fakeClient , latest . GroupOrDie ( "" ) . InterfacesFor , testapi . Default . Version ( ) , etcdtest . PathPrefix ( ) )
2015-09-03 17:35:04 +00:00
assert . NoError ( err , "Unable to create etcdstorage: %s" , err )
// Fail case
errorFunc := func ( apiVersion string ) ( * meta . VersionInterfaces , error ) { return nil , errors . New ( "ERROR" ) }
2015-09-12 22:27:05 +00:00
_ , err = NewEtcdStorage ( fakeClient , errorFunc , testapi . Default . Version ( ) , etcdtest . PathPrefix ( ) )
2015-09-03 17:35:04 +00:00
assert . Error ( err , "NewEtcdStorage should have failed" )
}
// TestGetServersToValidate verifies the unexported getServersToValidate function
func TestGetServersToValidate ( t * testing . T ) {
master , config , assert := setUp ( t )
2015-05-14 00:29:25 +00:00
servers := master . getServersToValidate ( & config )
2015-05-04 21:17:35 +00:00
2015-09-03 17:35:04 +00:00
assert . Equal ( 5 , len ( servers ) , "unexpected server list: %#v" , servers )
2015-05-04 21:17:35 +00:00
for _ , server := range [ ] string { "scheduler" , "controller-manager" , "etcd-0" , "etcd-1" , "etcd-2" } {
if _ , ok := servers [ server ] ; ! ok {
t . Errorf ( "server list missing: %s" , server )
}
}
2014-11-02 20:52:31 +00:00
}
2015-07-28 08:10:48 +00:00
2015-09-03 17:35:04 +00:00
// TestFindExternalAddress verifies both pass and fail cases for the unexported
// findExternalAddress function
2015-07-28 08:10:48 +00:00
func TestFindExternalAddress ( t * testing . T ) {
2015-09-03 17:35:04 +00:00
assert := assert . New ( t )
2015-07-28 08:10:48 +00:00
expectedIP := "172.0.0.1"
nodes := [ ] * api . Node { new ( api . Node ) , new ( api . Node ) , new ( api . Node ) }
nodes [ 0 ] . Status . Addresses = [ ] api . NodeAddress { { "ExternalIP" , expectedIP } }
nodes [ 1 ] . Status . Addresses = [ ] api . NodeAddress { { "LegacyHostIP" , expectedIP } }
nodes [ 2 ] . Status . Addresses = [ ] api . NodeAddress { { "ExternalIP" , expectedIP } , { "LegacyHostIP" , "172.0.0.2" } }
2015-09-03 17:35:04 +00:00
// Pass Case
2015-07-28 08:10:48 +00:00
for _ , node := range nodes {
ip , err := findExternalAddress ( node )
2015-09-03 17:35:04 +00:00
assert . NoError ( err , "error getting node external address" )
assert . Equal ( expectedIP , ip , "expected ip to be %s, but was %s" , expectedIP , ip )
2015-07-28 08:10:48 +00:00
}
2015-09-03 17:35:04 +00:00
// Fail case
2015-07-28 08:10:48 +00:00
_ , err := findExternalAddress ( new ( api . Node ) )
2015-09-03 17:35:04 +00:00
assert . Error ( err , "expected findExternalAddress to fail on a node with missing ip information" )
}
// TestApi_v1 verifies that the unexported api_v1 function does indeed
// utilize the correct Version and Codec.
func TestApi_v1 ( t * testing . T ) {
master , _ , assert := setUp ( t )
version := master . api_v1 ( )
assert . Equal ( "v1" , version . Version , "Version was not v1: %s" , version . Version )
assert . Equal ( v1 . Codec , version . Codec , "version.Codec was not for v1: %s" , version . Codec )
for k , v := range master . storage {
assert . Contains ( version . Storage , v , "Value %s not found (key: %s)" , k , v )
2015-07-28 08:10:48 +00:00
}
}
2015-08-19 18:02:01 +00:00
2015-09-03 17:35:04 +00:00
// TestNewBootstrapController verifies master fields are properly copied into controller
func TestNewBootstrapController ( t * testing . T ) {
// Tests a subset of inputs to ensure they are set properly in the controller
master , _ , assert := setUp ( t )
portRange := util . PortRange { Base : 10 , Size : 10 }
master . namespaceRegistry = namespace . NewRegistry ( nil )
master . serviceRegistry = registrytest . NewServiceRegistry ( )
master . endpointRegistry = endpoint . NewRegistry ( nil )
master . serviceNodePortRange = portRange
master . masterCount = 1
master . serviceReadWritePort = 1000
master . publicReadWritePort = 1010
controller := master . NewBootstrapController ( )
assert . Equal ( controller . NamespaceRegistry , master . namespaceRegistry )
assert . Equal ( controller . EndpointRegistry , master . endpointRegistry )
assert . Equal ( controller . ServiceRegistry , master . serviceRegistry )
assert . Equal ( controller . ServiceNodePortRange , portRange )
assert . Equal ( controller . MasterCount , master . masterCount )
assert . Equal ( controller . ServicePort , master . serviceReadWritePort )
assert . Equal ( controller . PublicServicePort , master . publicReadWritePort )
}
// TestNewHandlerContainer verifies that NewHandlerContainer uses the
// mux provided
func TestNewHandlerContainer ( t * testing . T ) {
assert := assert . New ( t )
mux := http . NewServeMux ( )
container := NewHandlerContainer ( mux )
assert . Equal ( mux , container . ServeMux , "ServerMux's do not match" )
}
// TestHandleWithAuth verifies HandleWithAuth adds the path
// to the muxHelper.RegisteredPaths.
func TestHandleWithAuth ( t * testing . T ) {
master , _ , assert := setUp ( t )
mh := apiserver . MuxHelper { Mux : http . NewServeMux ( ) }
master . muxHelper = & mh
handler := func ( r http . ResponseWriter , w * http . Request ) { w . Write ( nil ) }
master . HandleWithAuth ( "/test" , http . HandlerFunc ( handler ) )
assert . Contains ( master . muxHelper . RegisteredPaths , "/test" , "Path not found in muxHelper" )
}
// TestHandleFuncWithAuth verifies HandleFuncWithAuth adds the path
// to the muxHelper.RegisteredPaths.
func TestHandleFuncWithAuth ( t * testing . T ) {
master , _ , assert := setUp ( t )
mh := apiserver . MuxHelper { Mux : http . NewServeMux ( ) }
master . muxHelper = & mh
handler := func ( r http . ResponseWriter , w * http . Request ) { w . Write ( nil ) }
master . HandleFuncWithAuth ( "/test" , handler )
assert . Contains ( master . muxHelper . RegisteredPaths , "/test" , "Path not found in muxHelper" )
}
// TestInstallSwaggerAPI verifies that the swagger api is added
// at the proper endpoint.
func TestInstallSwaggerAPI ( t * testing . T ) {
master , _ , assert := setUp ( t )
mux := http . NewServeMux ( )
master . handlerContainer = NewHandlerContainer ( mux )
// Ensure swagger isn't installed without the call
ws := master . handlerContainer . RegisteredWebServices ( )
if ! assert . Equal ( len ( ws ) , 0 ) {
for x := range ws {
assert . NotEqual ( "/swaggerapi" , ws [ x ] . RootPath ( ) , "SwaggerAPI was installed without a call to InstallSwaggerAPI()" )
}
}
// Install swagger and test
master . InstallSwaggerAPI ( )
ws = master . handlerContainer . RegisteredWebServices ( )
if assert . NotEqual ( 0 , len ( ws ) , "SwaggerAPI not installed." ) {
assert . Equal ( "/swaggerapi/" , ws [ 0 ] . RootPath ( ) , "SwaggerAPI did not install to the proper path. %s != /swaggerapi" , ws [ 0 ] . RootPath ( ) )
}
// Empty externalHost verification
mux = http . NewServeMux ( )
master . handlerContainer = NewHandlerContainer ( mux )
master . externalHost = ""
master . clusterIP = net . IPv4 ( 10 , 10 , 10 , 10 )
master . publicReadWritePort = 1010
master . InstallSwaggerAPI ( )
if assert . NotEqual ( 0 , len ( ws ) , "SwaggerAPI not installed." ) {
assert . Equal ( "/swaggerapi/" , ws [ 0 ] . RootPath ( ) , "SwaggerAPI did not install to the proper path. %s != /swaggerapi" , ws [ 0 ] . RootPath ( ) )
}
}
// TestDefaultAPIGroupVersion verifies that the unexported defaultAPIGroupVersion
// creates the expected APIGroupVersion based off of master.
func TestDefaultAPIGroupVersion ( t * testing . T ) {
master , _ , assert := setUp ( t )
master . dialer = func ( network , addr string ) ( net . Conn , error ) { return nil , nil }
apiGroup := master . defaultAPIGroupVersion ( )
assert . Equal ( apiGroup . Root , master . apiPrefix )
assert . Equal ( apiGroup . Admit , master . admissionControl )
assert . Equal ( apiGroup . Context , master . requestContextMapper )
assert . Equal ( apiGroup . MinRequestTimeout , master . minRequestTimeout )
// These functions should be different instances of the same function
groupDialerFunc := fmt . Sprintf ( "%+v" , apiGroup . ProxyDialerFn )
masterDialerFunc := fmt . Sprintf ( "%+v" , master . dialer )
assert . Equal ( groupDialerFunc , masterDialerFunc )
}
// TestExpapi verifies that the unexported exapi creates
// the an experimental api APIGroupVersion.
func TestExpapi ( t * testing . T ) {
master , config , assert := setUp ( t )
2015-09-09 22:46:06 +00:00
expAPIGroup := master . experimental ( & config )
2015-09-14 22:30:32 +00:00
assert . Equal ( expAPIGroup . Root , master . apiGroupPrefix + "/" + latest . GroupOrDie ( "experimental" ) . Group )
2015-09-12 22:27:05 +00:00
assert . Equal ( expAPIGroup . Mapper , latest . GroupOrDie ( "experimental" ) . RESTMapper )
assert . Equal ( expAPIGroup . Codec , latest . GroupOrDie ( "experimental" ) . Codec )
assert . Equal ( expAPIGroup . Linker , latest . GroupOrDie ( "experimental" ) . SelfLinker )
assert . Equal ( expAPIGroup . Version , latest . GroupOrDie ( "experimental" ) . Version )
2015-09-03 17:35:04 +00:00
}
// TestSecondsSinceSync verifies that proper results are returned
// when checking the time between syncs
func TestSecondsSinceSync ( t * testing . T ) {
master , _ , assert := setUp ( t )
master . lastSync = time . Date ( 2015 , time . January , 1 , 1 , 1 , 1 , 1 , time . UTC ) . Unix ( )
// Nano Second. No difference.
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . January , 1 , 1 , 1 , 1 , 2 , time . UTC ) }
assert . Equal ( int64 ( 0 ) , master . secondsSinceSync ( ) )
// Second
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . January , 1 , 1 , 1 , 2 , 1 , time . UTC ) }
assert . Equal ( int64 ( 1 ) , master . secondsSinceSync ( ) )
// Minute
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . January , 1 , 1 , 2 , 1 , 1 , time . UTC ) }
assert . Equal ( int64 ( 60 ) , master . secondsSinceSync ( ) )
// Hour
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . January , 1 , 2 , 1 , 1 , 1 , time . UTC ) }
assert . Equal ( int64 ( 3600 ) , master . secondsSinceSync ( ) )
// Day
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . January , 2 , 1 , 1 , 1 , 1 , time . UTC ) }
assert . Equal ( int64 ( 86400 ) , master . secondsSinceSync ( ) )
// Month
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . February , 1 , 1 , 1 , 1 , 1 , time . UTC ) }
assert . Equal ( int64 ( 2678400 ) , master . secondsSinceSync ( ) )
// Future Month. Should be -Month.
master . lastSync = time . Date ( 2015 , time . February , 1 , 1 , 1 , 1 , 1 , time . UTC ) . Unix ( )
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . January , 1 , 1 , 1 , 1 , 1 , time . UTC ) }
assert . Equal ( int64 ( - 2678400 ) , master . secondsSinceSync ( ) )
}
// TestGetNodeAddresses verifies that proper results are returned
// when requesting node addresses.
func TestGetNodeAddresses ( t * testing . T ) {
master , _ , assert := setUp ( t )
// Fail case (no addresses associated with nodes)
nodes , _ := master . nodeRegistry . ListNodes ( api . NewDefaultContext ( ) , labels . Everything ( ) , fields . Everything ( ) )
addrs , err := master . getNodeAddresses ( )
assert . Error ( err , "getNodeAddresses should have caused an error as there are no addresses." )
assert . Equal ( [ ] string ( nil ) , addrs )
// Pass case with External type IP
nodes , _ = master . nodeRegistry . ListNodes ( api . NewDefaultContext ( ) , labels . Everything ( ) , fields . Everything ( ) )
for index := range nodes . Items {
nodes . Items [ index ] . Status . Addresses = [ ] api . NodeAddress { { Type : api . NodeExternalIP , Address : "127.0.0.1" } }
}
addrs , err = master . getNodeAddresses ( )
assert . NoError ( err , "getNodeAddresses should not have returned an error." )
assert . Equal ( [ ] string { "127.0.0.1" , "127.0.0.1" } , addrs )
// Pass case with LegacyHost type IP
nodes , _ = master . nodeRegistry . ListNodes ( api . NewDefaultContext ( ) , labels . Everything ( ) , fields . Everything ( ) )
for index := range nodes . Items {
nodes . Items [ index ] . Status . Addresses = [ ] api . NodeAddress { { Type : api . NodeLegacyHostIP , Address : "127.0.0.2" } }
}
addrs , err = master . getNodeAddresses ( )
assert . NoError ( err , "getNodeAddresses failback should not have returned an error." )
assert . Equal ( [ ] string { "127.0.0.2" , "127.0.0.2" } , addrs )
}
// TestRefreshTunnels verifies that the function errors when no addresses
// are associated with nodes
func TestRefreshTunnels ( t * testing . T ) {
master , _ , assert := setUp ( t )
// Fail case (no addresses associated with nodes)
assert . Error ( master . refreshTunnels ( "test" , "/tmp/undefined" ) )
// TODO: pass case without needing actual connections?
}
// TestIsTunnelSyncHealthy verifies that the 600 second lag test
// is honored.
func TestIsTunnelSyncHealthy ( t * testing . T ) {
master , _ , assert := setUp ( t )
// Pass case: 540 second lag
master . lastSync = time . Date ( 2015 , time . January , 1 , 1 , 1 , 1 , 1 , time . UTC ) . Unix ( )
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . January , 1 , 1 , 9 , 1 , 1 , time . UTC ) }
err := master . IsTunnelSyncHealthy ( nil )
assert . NoError ( err , "IsTunnelSyncHealthy() should not have returned an error." )
// Fail case: 720 second lag
master . clock = & util . FakeClock { Time : time . Date ( 2015 , time . January , 1 , 1 , 12 , 1 , 1 , time . UTC ) }
err = master . IsTunnelSyncHealthy ( nil )
assert . Error ( err , "IsTunnelSyncHealthy() should have returned an error." )
}
// generateTempFile creates a temporary file path
func generateTempFilePath ( prefix string ) string {
tmpPath , _ := filepath . Abs ( fmt . Sprintf ( "%s/%s-%d" , os . TempDir ( ) , prefix , time . Now ( ) . Unix ( ) ) )
return tmpPath
}
// TestGenerateSSHKey verifies that SSH key generation does indeed
// generate keys even with keys already exist.
func TestGenerateSSHKey ( t * testing . T ) {
master , _ , assert := setUp ( t )
privateKey := generateTempFilePath ( "private" )
publicKey := generateTempFilePath ( "public" )
// Make sure we have no test keys laying around
os . Remove ( privateKey )
os . Remove ( publicKey )
// Pass case: Sunny day case
err := master . generateSSHKey ( "unused" , privateKey , publicKey )
assert . NoError ( err , "generateSSHKey should not have retuend an error: %s" , err )
// Pass case: PrivateKey exists test case
os . Remove ( publicKey )
err = master . generateSSHKey ( "unused" , privateKey , publicKey )
assert . NoError ( err , "generateSSHKey should not have retuend an error: %s" , err )
// Pass case: PublicKey exists test case
os . Remove ( privateKey )
err = master . generateSSHKey ( "unused" , privateKey , publicKey )
assert . NoError ( err , "generateSSHKey should not have retuend an error: %s" , err )
// Make sure we have no test keys laying around
os . Remove ( privateKey )
os . Remove ( publicKey )
// TODO: testing error cases where the file can not be removed?
}
2015-09-01 05:28:08 +00:00
var versionsToTest = [ ] string { "v1" , "v3" }
2015-08-20 05:08:26 +00:00
type Foo struct {
2015-09-09 21:59:11 +00:00
unversioned . TypeMeta ` json:",inline" `
api . ObjectMeta ` json:"metadata,omitempty" description:"standard object metadata" `
2015-08-20 05:08:26 +00:00
SomeField string ` json:"someField" `
OtherField int ` json:"otherField" `
}
type FooList struct {
2015-09-09 21:59:11 +00:00
unversioned . TypeMeta ` json:",inline" `
unversioned . ListMeta ` json:"metadata,omitempty" description:"standard list metadata; see http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata" `
2015-08-20 05:08:26 +00:00
items [ ] Foo ` json:"items" `
}
2015-09-03 17:35:04 +00:00
func initThirdParty ( t * testing . T , version string ) ( * tools . FakeEtcdClient , * httptest . Server , * assert . Assertions ) {
master , _ , assert := setUp ( t )
2015-09-09 22:46:06 +00:00
api := & experimental . ThirdPartyResource {
2015-08-19 18:02:01 +00:00
ObjectMeta : api . ObjectMeta {
2015-08-20 05:08:26 +00:00
Name : "foo.company.com" ,
2015-08-19 18:02:01 +00:00
} ,
2015-09-09 22:46:06 +00:00
Versions : [ ] experimental . APIVersion {
2015-08-20 05:08:26 +00:00
{
2015-08-19 18:02:01 +00:00
APIGroup : "group" ,
2015-09-01 05:28:08 +00:00
Name : version ,
2015-08-19 18:02:01 +00:00
} ,
} ,
}
master . handlerContainer = restful . NewContainer ( )
2015-08-20 05:08:26 +00:00
fakeClient := tools . NewFakeEtcdClient ( t )
fakeClient . Machines = [ ] string { "http://machine1:4001" , "http://machine2" , "http://machine3:4003" }
2015-09-11 06:37:26 +00:00
master . thirdPartyStorage = etcdstorage . NewEtcdStorage ( fakeClient , testapi . Experimental . Codec ( ) , etcdtest . PathPrefix ( ) )
2015-08-20 05:08:26 +00:00
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( master . InstallThirdPartyAPI ( api ) ) {
2015-08-20 05:08:26 +00:00
t . FailNow ( )
2015-08-19 18:02:01 +00:00
}
2015-08-20 05:08:26 +00:00
2015-08-19 18:02:01 +00:00
server := httptest . NewServer ( master . handlerContainer . ServeMux )
2015-09-03 17:35:04 +00:00
return fakeClient , server , assert
2015-08-20 05:08:26 +00:00
}
func TestInstallThirdPartyAPIList ( t * testing . T ) {
2015-09-01 05:28:08 +00:00
for _ , version := range versionsToTest {
testInstallThirdPartyAPIListVersion ( t , version )
}
}
func testInstallThirdPartyAPIListVersion ( t * testing . T , version string ) {
2015-09-03 17:35:04 +00:00
fakeClient , server , assert := initThirdParty ( t , version )
2015-08-19 18:02:01 +00:00
defer server . Close ( )
2015-08-20 05:08:26 +00:00
fakeClient . ExpectNotFoundGet ( etcdtest . PathPrefix ( ) + "/ThirdPartyResourceData/company.com/foos/default" )
2015-09-14 20:37:40 +00:00
resp , err := http . Get ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos" )
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( err ) {
2015-08-20 05:08:26 +00:00
return
2015-08-19 18:02:01 +00:00
}
2015-09-03 17:35:04 +00:00
2015-08-19 18:02:01 +00:00
defer resp . Body . Close ( )
2015-08-20 05:08:26 +00:00
2015-09-03 17:35:04 +00:00
assert . Equal ( http . StatusOK , resp . StatusCode )
2015-08-20 05:08:26 +00:00
2015-08-19 18:02:01 +00:00
data , err := ioutil . ReadAll ( resp . Body )
2015-09-03 17:35:04 +00:00
assert . NoError ( err )
2015-08-20 05:08:26 +00:00
list := FooList { }
2015-09-03 17:35:04 +00:00
err = json . Unmarshal ( data , & list )
assert . NoError ( err )
2015-08-20 05:08:26 +00:00
}
func encodeToThirdParty ( name string , obj interface { } ) ( [ ] byte , error ) {
serial , err := json . Marshal ( obj )
if err != nil {
return nil , err
}
2015-09-09 22:46:06 +00:00
thirdPartyData := experimental . ThirdPartyResourceData {
2015-08-20 05:08:26 +00:00
ObjectMeta : api . ObjectMeta { Name : name } ,
Data : serial ,
}
2015-09-11 00:20:53 +00:00
return testapi . Default . Codec ( ) . Encode ( & thirdPartyData )
2015-08-20 05:08:26 +00:00
}
func storeToEtcd ( fakeClient * tools . FakeEtcdClient , path , name string , obj interface { } ) error {
data , err := encodeToThirdParty ( name , obj )
if err != nil {
return err
}
_ , err = fakeClient . Set ( etcdtest . PathPrefix ( ) + path , string ( data ) , 0 )
return err
}
func decodeResponse ( resp * http . Response , obj interface { } ) error {
defer resp . Body . Close ( )
data , err := ioutil . ReadAll ( resp . Body )
if err != nil {
return err
}
if err := json . Unmarshal ( data , obj ) ; err != nil {
return err
}
return nil
}
func TestInstallThirdPartyAPIGet ( t * testing . T ) {
2015-09-01 05:28:08 +00:00
for _ , version := range versionsToTest {
testInstallThirdPartyAPIGetVersion ( t , version )
}
}
func testInstallThirdPartyAPIGetVersion ( t * testing . T , version string ) {
2015-09-03 17:35:04 +00:00
fakeClient , server , assert := initThirdParty ( t , version )
2015-08-20 05:08:26 +00:00
defer server . Close ( )
expectedObj := Foo {
ObjectMeta : api . ObjectMeta {
Name : "test" ,
} ,
2015-09-09 21:59:11 +00:00
TypeMeta : unversioned . TypeMeta {
2015-09-01 05:28:08 +00:00
Kind : "Foo" ,
APIVersion : version ,
2015-08-20 05:08:26 +00:00
} ,
SomeField : "test field" ,
OtherField : 10 ,
}
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( storeToEtcd ( fakeClient , "/ThirdPartyResourceData/company.com/foos/default/test" , "test" , expectedObj ) ) {
2015-08-20 05:08:26 +00:00
t . FailNow ( )
return
}
2015-09-14 20:37:40 +00:00
resp , err := http . Get ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos/test" )
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( err ) {
2015-08-20 05:08:26 +00:00
return
}
2015-09-03 17:35:04 +00:00
assert . Equal ( http . StatusOK , resp . StatusCode )
2015-08-20 05:08:26 +00:00
item := Foo { }
2015-09-03 17:35:04 +00:00
assert . NoError ( decodeResponse ( resp , & item ) )
if ! assert . False ( reflect . DeepEqual ( item , expectedObj ) ) {
t . Errorf ( "expected objects to not be equal:\n%v\nsaw:\n%v\n" , expectedObj , item )
2015-08-20 05:08:26 +00:00
}
2015-09-03 00:13:38 +00:00
// Fill in data that the apiserver injects
expectedObj . SelfLink = item . SelfLink
2015-09-03 17:35:04 +00:00
if ! assert . True ( reflect . DeepEqual ( item , expectedObj ) ) {
2015-09-03 00:13:38 +00:00
t . Errorf ( "expected:\n%#v\nsaw:\n%#v\n" , expectedObj , item )
2015-08-20 05:08:26 +00:00
}
}
func TestInstallThirdPartyAPIPost ( t * testing . T ) {
2015-09-01 05:28:08 +00:00
for _ , version := range versionsToTest {
testInstallThirdPartyAPIPostForVersion ( t , version )
}
}
func testInstallThirdPartyAPIPostForVersion ( t * testing . T , version string ) {
2015-09-03 17:35:04 +00:00
fakeClient , server , assert := initThirdParty ( t , version )
2015-08-20 05:08:26 +00:00
defer server . Close ( )
inputObj := Foo {
ObjectMeta : api . ObjectMeta {
Name : "test" ,
} ,
2015-09-09 21:59:11 +00:00
TypeMeta : unversioned . TypeMeta {
2015-09-01 05:28:08 +00:00
Kind : "Foo" ,
APIVersion : version ,
2015-08-20 05:08:26 +00:00
} ,
SomeField : "test field" ,
OtherField : 10 ,
}
2015-08-21 21:24:16 +00:00
data , err := json . Marshal ( inputObj )
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( err ) {
2015-08-20 05:08:26 +00:00
return
}
2015-09-14 20:37:40 +00:00
resp , err := http . Post ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos" , "application/json" , bytes . NewBuffer ( data ) )
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( err ) {
2015-09-14 20:37:40 +00:00
t . Errorf ( "unexpected error: %v" , err )
2015-08-20 05:08:26 +00:00
return
}
2015-09-03 17:35:04 +00:00
assert . Equal ( http . StatusCreated , resp . StatusCode )
2015-08-20 05:08:26 +00:00
item := Foo { }
2015-09-03 17:35:04 +00:00
assert . NoError ( decodeResponse ( resp , & item ) )
2015-08-20 05:08:26 +00:00
2015-09-03 00:13:38 +00:00
// fill in fields set by the apiserver
expectedObj := inputObj
expectedObj . SelfLink = item . SelfLink
expectedObj . Namespace = item . Namespace
expectedObj . UID = item . UID
expectedObj . CreationTimestamp = item . CreationTimestamp
2015-09-03 17:35:04 +00:00
if ! assert . True ( reflect . DeepEqual ( item , expectedObj ) ) {
2015-09-03 00:13:38 +00:00
t . Errorf ( "expected:\n%v\nsaw:\n%v\n" , expectedObj , item )
2015-08-20 05:08:26 +00:00
}
etcdResp , err := fakeClient . Get ( etcdtest . PathPrefix ( ) + "/ThirdPartyResourceData/company.com/foos/default/test" , false , false )
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( err ) {
2015-09-01 05:28:08 +00:00
t . FailNow ( )
2015-08-20 05:08:26 +00:00
}
2015-09-03 17:35:04 +00:00
2015-09-11 06:37:26 +00:00
obj , err := testapi . Experimental . Codec ( ) . Decode ( [ ] byte ( etcdResp . Node . Value ) )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
2015-09-09 22:46:06 +00:00
thirdPartyObj , ok := obj . ( * experimental . ThirdPartyResourceData )
2015-09-11 06:37:26 +00:00
if ! ok {
2015-08-20 05:08:26 +00:00
t . Errorf ( "unexpected object: %v" , obj )
}
item = Foo { }
2015-09-03 17:35:04 +00:00
assert . NoError ( json . Unmarshal ( thirdPartyObj . Data , & item ) )
2015-08-20 05:08:26 +00:00
2015-09-03 17:35:04 +00:00
if ! assert . True ( reflect . DeepEqual ( item , inputObj ) ) {
2015-08-20 05:08:26 +00:00
t . Errorf ( "expected:\n%v\nsaw:\n%v\n" , inputObj , item )
}
}
func TestInstallThirdPartyAPIDelete ( t * testing . T ) {
2015-09-01 05:28:08 +00:00
for _ , version := range versionsToTest {
testInstallThirdPartyAPIDeleteVersion ( t , version )
}
}
func testInstallThirdPartyAPIDeleteVersion ( t * testing . T , version string ) {
2015-09-03 17:35:04 +00:00
fakeClient , server , assert := initThirdParty ( t , version )
2015-08-20 05:08:26 +00:00
defer server . Close ( )
expectedObj := Foo {
ObjectMeta : api . ObjectMeta {
2015-09-03 00:13:38 +00:00
Name : "test" ,
Namespace : "default" ,
2015-08-20 05:08:26 +00:00
} ,
2015-09-09 21:59:11 +00:00
TypeMeta : unversioned . TypeMeta {
2015-08-21 21:24:16 +00:00
Kind : "Foo" ,
2015-08-20 05:08:26 +00:00
} ,
SomeField : "test field" ,
OtherField : 10 ,
}
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( storeToEtcd ( fakeClient , "/ThirdPartyResourceData/company.com/foos/default/test" , "test" , expectedObj ) ) {
2015-08-20 05:08:26 +00:00
t . FailNow ( )
return
}
2015-09-14 20:37:40 +00:00
resp , err := http . Get ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos/test" )
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( err ) {
2015-08-20 05:08:26 +00:00
return
}
2015-09-03 17:35:04 +00:00
assert . Equal ( http . StatusOK , resp . StatusCode )
2015-08-20 05:08:26 +00:00
item := Foo { }
2015-09-03 17:35:04 +00:00
assert . NoError ( decodeResponse ( resp , & item ) )
2015-08-20 05:08:26 +00:00
2015-09-03 00:13:38 +00:00
// Fill in fields set by the apiserver
expectedObj . SelfLink = item . SelfLink
expectedObj . Namespace = item . Namespace
2015-09-03 17:35:04 +00:00
if ! assert . True ( reflect . DeepEqual ( item , expectedObj ) ) {
2015-08-20 05:08:26 +00:00
t . Errorf ( "expected:\n%v\nsaw:\n%v\n" , expectedObj , item )
}
2015-09-14 20:37:40 +00:00
resp , err = httpDelete ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos/test" )
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( err ) {
2015-08-20 05:08:26 +00:00
return
}
2015-09-03 17:35:04 +00:00
assert . Equal ( http . StatusOK , resp . StatusCode )
2015-08-20 05:08:26 +00:00
2015-09-14 20:37:40 +00:00
resp , err = http . Get ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos/test" )
2015-09-03 17:35:04 +00:00
if ! assert . NoError ( err ) {
2015-08-20 05:08:26 +00:00
return
}
2015-09-03 17:35:04 +00:00
assert . Equal ( http . StatusNotFound , resp . StatusCode )
2015-08-20 05:08:26 +00:00
expectDeletedKeys := [ ] string { etcdtest . PathPrefix ( ) + "/ThirdPartyResourceData/company.com/foos/default/test" }
2015-09-03 17:35:04 +00:00
if ! assert . True ( reflect . DeepEqual ( fakeClient . DeletedKeys , expectDeletedKeys ) ) {
2015-08-20 05:08:26 +00:00
t . Errorf ( "unexpected deleted keys: %v" , fakeClient . DeletedKeys )
}
}
func httpDelete ( url string ) ( * http . Response , error ) {
req , err := http . NewRequest ( "DELETE" , url , nil )
if err != nil {
return nil , err
2015-08-19 18:02:01 +00:00
}
2015-08-20 05:08:26 +00:00
client := & http . Client { }
return client . Do ( req )
2015-08-19 18:02:01 +00:00
}