2014-11-02 20:52:31 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
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"
2015-10-09 05:18:16 +00:00
"crypto/tls"
2015-08-20 05:08:26 +00:00
"encoding/json"
2015-09-03 17:35:04 +00:00
"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-08-20 05:08:26 +00:00
"reflect"
2015-09-09 21:36:19 +00:00
"strings"
2014-11-02 20:52:31 +00:00
"testing"
2016-06-24 15:25:46 +00:00
"time"
2014-11-02 20:52:31 +00:00
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
2016-05-09 21:47:02 +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"
2016-04-07 22:34:40 +00:00
apiv1 "k8s.io/kubernetes/pkg/api/v1"
2016-03-16 14:17:04 +00:00
"k8s.io/kubernetes/pkg/apimachinery/registered"
2016-04-15 22:30:15 +00:00
"k8s.io/kubernetes/pkg/apis/apps"
2016-09-13 17:55:41 +00:00
appsapiv1alpha1 "k8s.io/kubernetes/pkg/apis/apps/v1alpha1"
2016-02-08 14:03:59 +00:00
"k8s.io/kubernetes/pkg/apis/autoscaling"
2016-04-07 22:34:40 +00:00
autoscalingapiv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
2016-02-17 22:06:35 +00:00
"k8s.io/kubernetes/pkg/apis/batch"
2016-04-07 22:34:40 +00:00
batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
2016-04-28 12:41:45 +00:00
batchapiv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
2016-04-15 01:09:24 +00:00
"k8s.io/kubernetes/pkg/apis/certificates"
2015-10-09 22:04:41 +00:00
"k8s.io/kubernetes/pkg/apis/extensions"
2016-04-07 22:34:40 +00:00
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
2016-05-25 21:20:41 +00:00
"k8s.io/kubernetes/pkg/apis/rbac"
2016-09-26 15:18:19 +00:00
"k8s.io/kubernetes/pkg/apiserver/request"
"k8s.io/kubernetes/pkg/generated/openapi"
2015-11-16 21:46:00 +00:00
"k8s.io/kubernetes/pkg/genericapiserver"
2015-10-27 13:18:45 +00:00
"k8s.io/kubernetes/pkg/kubelet/client"
2016-09-21 13:14:26 +00:00
"k8s.io/kubernetes/pkg/registry/core/endpoint"
"k8s.io/kubernetes/pkg/registry/core/namespace"
ipallocator "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
2016-09-23 19:10:47 +00:00
extensionsrest "k8s.io/kubernetes/pkg/registry/extensions/rest"
2016-09-21 13:06:56 +00:00
"k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata"
2016-08-08 22:12:54 +00:00
"k8s.io/kubernetes/pkg/registry/generic"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/registry/registrytest"
2015-11-20 08:16:41 +00:00
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
2015-11-10 11:23:51 +00:00
"k8s.io/kubernetes/pkg/storage/etcd/etcdtest"
2015-11-17 13:35:00 +00:00
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
2015-11-10 06:28:45 +00:00
"k8s.io/kubernetes/pkg/util/intstr"
2016-04-20 04:11:39 +00:00
utilnet "k8s.io/kubernetes/pkg/util/net"
"k8s.io/kubernetes/pkg/util/sets"
2016-09-06 11:20:36 +00:00
"k8s.io/kubernetes/pkg/version"
2015-09-09 21:36:19 +00:00
2016-08-18 15:12:26 +00:00
"github.com/go-openapi/loads"
"github.com/go-openapi/spec"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/validate"
2015-09-09 21:36:19 +00:00
"github.com/stretchr/testify/assert"
2015-11-20 08:16:41 +00:00
"golang.org/x/net/context"
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.
2016-03-23 23:45:24 +00:00
func setUp ( t * testing . T ) ( * Master , * etcdtesting . EtcdTestServer , Config , * assert . Assertions ) {
2016-08-26 01:38:27 +00:00
server , storageConfig := etcdtesting . NewUnsecuredEtcd3TestClientServer ( t )
2015-11-17 13:35:00 +00:00
2016-03-23 23:45:24 +00:00
master := & Master {
2015-11-16 21:46:00 +00:00
GenericAPIServer : & genericapiserver . GenericAPIServer { } ,
}
config := Config {
2016-09-28 18:52:28 +00:00
GenericConfig : & genericapiserver . Config { } ,
2015-11-16 21:46:00 +00:00
}
2016-03-16 14:17:04 +00:00
resourceEncoding := genericapiserver . NewDefaultResourceEncodingConfig ( )
resourceEncoding . SetVersionEncoding ( api . GroupName , * testapi . Default . GroupVersion ( ) , unversioned . GroupVersion { Group : api . GroupName , Version : runtime . APIVersionInternal } )
resourceEncoding . SetVersionEncoding ( autoscaling . GroupName , * testapi . Autoscaling . GroupVersion ( ) , unversioned . GroupVersion { Group : autoscaling . GroupName , Version : runtime . APIVersionInternal } )
resourceEncoding . SetVersionEncoding ( batch . GroupName , * testapi . Batch . GroupVersion ( ) , unversioned . GroupVersion { Group : batch . GroupName , Version : runtime . APIVersionInternal } )
resourceEncoding . SetVersionEncoding ( apps . GroupName , * testapi . Apps . GroupVersion ( ) , unversioned . GroupVersion { Group : apps . GroupName , Version : runtime . APIVersionInternal } )
resourceEncoding . SetVersionEncoding ( extensions . GroupName , * testapi . Extensions . GroupVersion ( ) , unversioned . GroupVersion { Group : extensions . GroupName , Version : runtime . APIVersionInternal } )
2016-05-25 21:20:41 +00:00
resourceEncoding . SetVersionEncoding ( rbac . GroupName , * testapi . Rbac . GroupVersion ( ) , unversioned . GroupVersion { Group : rbac . GroupName , Version : runtime . APIVersionInternal } )
2016-04-15 01:09:24 +00:00
resourceEncoding . SetVersionEncoding ( certificates . GroupName , * testapi . Certificates . GroupVersion ( ) , unversioned . GroupVersion { Group : certificates . GroupName , Version : runtime . APIVersionInternal } )
2016-08-26 01:38:27 +00:00
storageFactory := genericapiserver . NewDefaultStorageFactory ( * storageConfig , testapi . StorageMediaType ( ) , api . Codecs , resourceEncoding , DefaultAPIResourceConfigSource ( ) )
2016-03-16 14:17:04 +00:00
config . StorageFactory = storageFactory
2016-09-28 18:52:28 +00:00
config . GenericConfig . APIResourceConfigSource = DefaultAPIResourceConfigSource ( )
config . GenericConfig . PublicAddress = net . ParseIP ( "192.168.10.4" )
config . GenericConfig . Serializer = api . Codecs
2015-09-03 17:35:04 +00:00
config . KubeletClient = client . FakeKubeletClient { }
2016-09-28 18:52:28 +00:00
config . GenericConfig . APIPrefix = "/api"
config . GenericConfig . APIGroupPrefix = "/apis"
config . GenericConfig . APIResourceConfigSource = DefaultAPIResourceConfigSource ( )
config . GenericConfig . ProxyDialer = func ( network , addr string ) ( net . Conn , error ) { return nil , nil }
config . GenericConfig . ProxyTLSClientConfig = & tls . Config { }
config . GenericConfig . RequestContextMapper = api . NewRequestContextMapper ( )
config . GenericConfig . EnableVersion = true
2015-10-09 05:18:16 +00:00
2016-03-24 22:44:31 +00:00
// TODO: this is kind of hacky. The trouble is that the sync loop
// runs in a go-routine and there is no way to validate in the test
// that the sync routine has actually run. The right answer here
// is probably to add some sort of callback that we can register
// to validate that it's actually been run, but for now we don't
// run the sync routine and register types manually.
2016-03-10 04:06:31 +00:00
config . disableThirdPartyControllerForTesting = true
2016-04-07 22:34:40 +00:00
master . nodeRegistry = registrytest . NewNodeRegistry ( [ ] string { "node1" , "node2" } , api . NodeResources { } )
return master , server , config , assert . New ( t )
}
func newMaster ( t * testing . T ) ( * Master , * etcdtesting . EtcdTestServer , Config , * assert . Assertions ) {
_ , etcdserver , config , assert := setUp ( t )
2016-09-27 15:52:31 +00:00
master , err := config . Complete ( ) . New ( )
2016-04-07 22:34:40 +00:00
if err != nil {
t . Fatalf ( "Error in bringing up the master: %v" , err )
}
return master , etcdserver , config , assert
}
// limitedAPIResourceConfigSource only enables the core group, the extensions group, the batch group, and the autoscaling group.
func limitedAPIResourceConfigSource ( ) * genericapiserver . ResourceConfig {
ret := genericapiserver . NewResourceConfig ( )
2016-06-15 20:21:53 +00:00
ret . EnableVersions (
apiv1 . SchemeGroupVersion ,
extensionsapiv1beta1 . SchemeGroupVersion ,
batchapiv1 . SchemeGroupVersion ,
batchapiv2alpha1 . SchemeGroupVersion ,
2016-09-13 17:55:41 +00:00
appsapiv1alpha1 . SchemeGroupVersion ,
2016-06-15 20:21:53 +00:00
autoscalingapiv1 . SchemeGroupVersion ,
)
2016-04-07 22:34:40 +00:00
return ret
}
// newLimitedMaster only enables the core group, the extensions group, the batch group, and the autoscaling group.
func newLimitedMaster ( t * testing . T ) ( * Master , * etcdtesting . EtcdTestServer , Config , * assert . Assertions ) {
_ , etcdserver , config , assert := setUp ( t )
2016-09-28 18:52:28 +00:00
config . GenericConfig . APIResourceConfigSource = limitedAPIResourceConfigSource ( )
2016-09-27 15:52:31 +00:00
master , err := config . Complete ( ) . New ( )
2016-02-03 22:26:11 +00:00
if err != nil {
t . Fatalf ( "Error in bringing up the master: %v" , err )
}
2015-11-16 21:46:00 +00:00
return master , etcdserver , config , assert
}
// TestNew verifies that the New function returns a Master
// using the configuration properly.
func TestNew ( t * testing . T ) {
master , etcdserver , config , assert := newMaster ( t )
defer etcdserver . Terminate ( t )
2015-09-03 17:35:04 +00:00
// Verify many of the variables match their config counterparts
assert . Equal ( master . enableCoreControllers , config . EnableCoreControllers )
2015-10-09 05:18:16 +00:00
assert . Equal ( master . tunneler , config . Tunneler )
2016-09-28 18:52:28 +00:00
assert . Equal ( master . RequestContextMapper ( ) , config . GenericConfig . RequestContextMapper )
assert . Equal ( master . ClusterIP , config . GenericConfig . PublicAddress )
2016-08-23 14:09:54 +00:00
// these values get defaulted
_ , serviceClusterIPRange , _ := net . ParseCIDR ( "10.0.0.0/24" )
serviceReadWriteIP , _ := ipallocator . GetIndexedIP ( serviceClusterIPRange , 1 )
assert . Equal ( master . MasterCount , 1 )
assert . Equal ( master . PublicReadWritePort , 6443 )
assert . Equal ( master . ServiceReadWriteIP , serviceReadWriteIP )
2015-10-09 05:18:16 +00:00
// These functions should point to the same memory location
2016-01-06 15:56:41 +00:00
masterDialer , _ := utilnet . Dialer ( master . ProxyTransport )
2015-10-09 05:18:16 +00:00
masterDialerFunc := fmt . Sprintf ( "%p" , masterDialer )
2016-09-28 18:52:28 +00:00
configDialerFunc := fmt . Sprintf ( "%p" , config . GenericConfig . ProxyDialer )
2015-10-09 05:18:16 +00:00
assert . Equal ( masterDialerFunc , configDialerFunc )
2016-09-28 18:52:28 +00:00
assert . Equal ( master . ProxyTransport . ( * http . Transport ) . TLSClientConfig , config . GenericConfig . ProxyTLSClientConfig )
2015-09-03 17:35:04 +00:00
}
2016-03-13 03:46:20 +00:00
// TestNamespaceSubresources ensures the namespace subresource parsing in apiserver/handlers.go doesn't drift
func TestNamespaceSubresources ( t * testing . T ) {
master , etcdserver , _ , _ := newMaster ( t )
defer etcdserver . Terminate ( t )
2016-09-26 15:18:19 +00:00
expectedSubresources := request . NamespaceSubResourcesForTest
2016-03-13 03:46:20 +00:00
foundSubresources := sets . NewString ( )
for k := range master . v1ResourcesStorage {
parts := strings . Split ( k , "/" )
if len ( parts ) == 2 && parts [ 0 ] == "namespaces" {
foundSubresources . Insert ( parts [ 1 ] )
}
}
if ! reflect . DeepEqual ( expectedSubresources . List ( ) , foundSubresources . List ( ) ) {
t . Errorf ( "Expected namespace subresources %#v, got %#v. Update apiserver/handlers.go#namespaceSubresources" , expectedSubresources . List ( ) , foundSubresources . List ( ) )
}
}
2016-09-06 11:20:36 +00:00
// TestVersion tests /version
func TestVersion ( t * testing . T ) {
s , etcdserver , _ , _ := newMaster ( t )
defer etcdserver . Terminate ( t )
req , _ := http . NewRequest ( "GET" , "/version" , nil )
resp := httptest . NewRecorder ( )
s . InsecureHandler . ServeHTTP ( resp , req )
if resp . Code != 200 {
t . Fatalf ( "expected http 200, got: %d" , resp . Code )
}
var info version . Info
err := json . NewDecoder ( resp . Body ) . Decode ( & info )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
if ! reflect . DeepEqual ( version . Get ( ) , info ) {
t . Errorf ( "Expected %#v, Got %#v" , version . Get ( ) , info )
}
}
2015-09-03 17:35:04 +00:00
// TestGetServersToValidate verifies the unexported getServersToValidate function
func TestGetServersToValidate ( t * testing . T ) {
2015-11-17 13:35:00 +00:00
master , etcdserver , config , assert := setUp ( t )
defer etcdserver . Terminate ( t )
2015-05-14 00:29:25 +00:00
servers := master . getServersToValidate ( & config )
2015-05-04 21:17:35 +00:00
2015-11-17 13:35:00 +00:00
// Expected servers to validate: scheduler, controller-manager and etcd.
assert . Equal ( 3 , len ( servers ) , "unexpected server list: %#v" , servers )
2015-09-03 17:35:04 +00:00
2015-11-17 13:35:00 +00:00
for _ , server := range [ ] string { "scheduler" , "controller-manager" , "etcd-0" } {
2015-05-04 21:17:35 +00:00
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 ) }
2016-07-25 21:34:05 +00:00
nodes [ 0 ] . Status . Addresses = [ ] api . NodeAddress { { Type : "ExternalIP" , Address : expectedIP } }
nodes [ 1 ] . Status . Addresses = [ ] api . NodeAddress { { Type : "LegacyHostIP" , Address : expectedIP } }
nodes [ 2 ] . Status . Addresses = [ ] api . NodeAddress { { Type : "ExternalIP" , Address : expectedIP } , { Type : "LegacyHostIP" , Address : "172.0.0.2" } }
2015-07-28 08:10:48 +00:00
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" )
}
2016-06-24 15:25:46 +00:00
type fakeEndpointReconciler struct { }
func ( * fakeEndpointReconciler ) ReconcileEndpoints ( serviceName string , ip net . IP , endpointPorts [ ] api . EndpointPort , reconcilePorts bool ) error {
return nil
}
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
2015-11-17 13:35:00 +00:00
master , etcdserver , _ , assert := setUp ( t )
defer etcdserver . Terminate ( t )
2016-01-06 15:56:41 +00:00
portRange := utilnet . PortRange { Base : 10 , Size : 10 }
2015-09-03 17:35:04 +00:00
master . namespaceRegistry = namespace . NewRegistry ( nil )
master . serviceRegistry = registrytest . NewServiceRegistry ( )
master . endpointRegistry = endpoint . NewRegistry ( nil )
2015-11-16 21:46:00 +00:00
master . ServiceNodePortRange = portRange
master . MasterCount = 1
master . ServiceReadWritePort = 1000
master . PublicReadWritePort = 1010
2015-09-03 17:35:04 +00:00
2016-06-24 15:25:46 +00:00
// test with an empty EndpointReconcilerConfig to ensure the defaults are applied
controller := master . NewBootstrapController ( EndpointReconcilerConfig { } )
2015-09-03 17:35:04 +00:00
assert . Equal ( controller . NamespaceRegistry , master . namespaceRegistry )
2016-06-06 21:48:22 +00:00
assert . Equal ( controller . EndpointReconciler , NewMasterCountEndpointReconciler ( master . MasterCount , master . endpointRegistry ) )
2016-06-24 15:25:46 +00:00
assert . Equal ( controller . EndpointInterval , DefaultEndpointReconcilerInterval )
2015-09-03 17:35:04 +00:00
assert . Equal ( controller . ServiceRegistry , master . serviceRegistry )
assert . Equal ( controller . ServiceNodePortRange , portRange )
2015-11-16 21:46:00 +00:00
assert . Equal ( controller . ServicePort , master . ServiceReadWritePort )
assert . Equal ( controller . PublicServicePort , master . PublicReadWritePort )
2016-06-24 15:25:46 +00:00
// test with a filled-in EndpointReconcilerConfig to make sure its values are used
controller = master . NewBootstrapController ( EndpointReconcilerConfig {
Reconciler : & fakeEndpointReconciler { } ,
Interval : 5 * time . Second ,
} )
assert . Equal ( controller . EndpointReconciler , & fakeEndpointReconciler { } )
assert . Equal ( controller . EndpointInterval , 5 * time . Second )
2015-09-03 17:35:04 +00:00
}
2015-10-07 15:06:05 +00:00
// TestControllerServicePorts verifies master extraServicePorts are
// correctly copied into controller
func TestControllerServicePorts ( t * testing . T ) {
2015-11-17 13:35:00 +00:00
master , etcdserver , _ , assert := setUp ( t )
defer etcdserver . Terminate ( t )
2015-10-07 15:06:05 +00:00
master . namespaceRegistry = namespace . NewRegistry ( nil )
master . serviceRegistry = registrytest . NewServiceRegistry ( )
master . endpointRegistry = endpoint . NewRegistry ( nil )
2015-11-16 21:46:00 +00:00
master . ExtraServicePorts = [ ] api . ServicePort {
2015-10-07 15:06:05 +00:00
{
Name : "additional-port-1" ,
Port : 1000 ,
Protocol : api . ProtocolTCP ,
2015-11-10 06:28:45 +00:00
TargetPort : intstr . FromInt ( 1000 ) ,
2015-10-07 15:06:05 +00:00
} ,
{
Name : "additional-port-2" ,
Port : 1010 ,
Protocol : api . ProtocolTCP ,
2015-11-10 06:28:45 +00:00
TargetPort : intstr . FromInt ( 1010 ) ,
2015-10-07 15:06:05 +00:00
} ,
}
2016-06-24 15:25:46 +00:00
controller := master . NewBootstrapController ( EndpointReconcilerConfig { } )
2015-10-07 15:06:05 +00:00
2016-04-27 04:35:14 +00:00
assert . Equal ( int32 ( 1000 ) , controller . ExtraServicePorts [ 0 ] . Port )
assert . Equal ( int32 ( 1010 ) , controller . ExtraServicePorts [ 1 ] . Port )
2015-10-07 15:06:05 +00:00
}
2015-09-03 17:35:04 +00:00
// TestGetNodeAddresses verifies that proper results are returned
// when requesting node addresses.
func TestGetNodeAddresses ( t * testing . T ) {
2015-11-17 13:35:00 +00:00
master , etcdserver , _ , assert := setUp ( t )
defer etcdserver . Terminate ( t )
2015-09-03 17:35:04 +00:00
// Fail case (no addresses associated with nodes)
2015-10-27 13:47:58 +00:00
nodes , _ := master . nodeRegistry . ListNodes ( api . NewDefaultContext ( ) , nil )
2015-09-03 17:35:04 +00:00
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
2015-10-27 13:47:58 +00:00
nodes , _ = master . nodeRegistry . ListNodes ( api . NewDefaultContext ( ) , nil )
2015-09-03 17:35:04 +00:00
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
2015-10-27 13:47:58 +00:00
nodes , _ = master . nodeRegistry . ListNodes ( api . NewDefaultContext ( ) , nil )
2015-09-03 17:35:04 +00:00
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 )
}
2016-02-12 01:03:43 +00:00
// Because we need to be backwards compatible with release 1.1, at endpoints
// that exist in release 1.1, the responses should have empty APIVersion.
func TestAPIVersionOfDiscoveryEndpoints ( t * testing . T ) {
master , etcdserver , _ , assert := newMaster ( t )
defer etcdserver . Terminate ( t )
server := httptest . NewServer ( master . HandlerContainer . ServeMux )
// /api exists in release-1.1
resp , err := http . Get ( server . URL + "/api" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
apiVersions := unversioned . APIVersions { }
assert . NoError ( decodeResponse ( resp , & apiVersions ) )
assert . Equal ( apiVersions . APIVersion , "" )
// /api/v1 exists in release-1.1
resp , err = http . Get ( server . URL + "/api/v1" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
resourceList := unversioned . APIResourceList { }
assert . NoError ( decodeResponse ( resp , & resourceList ) )
assert . Equal ( resourceList . APIVersion , "" )
// /apis exists in release-1.1
resp , err = http . Get ( server . URL + "/apis" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
groupList := unversioned . APIGroupList { }
assert . NoError ( decodeResponse ( resp , & groupList ) )
assert . Equal ( groupList . APIVersion , "" )
// /apis/extensions exists in release-1.1
resp , err = http . Get ( server . URL + "/apis/extensions" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
group := unversioned . APIGroup { }
assert . NoError ( decodeResponse ( resp , & group ) )
assert . Equal ( group . APIVersion , "" )
// /apis/extensions/v1beta1 exists in release-1.1
resp , err = http . Get ( server . URL + "/apis/extensions/v1beta1" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
resourceList = unversioned . APIResourceList { }
assert . NoError ( decodeResponse ( resp , & resourceList ) )
assert . Equal ( resourceList . APIVersion , "" )
// /apis/autoscaling doesn't exist in release-1.1, so the APIVersion field
// should be non-empty in the results returned by the server.
resp , err = http . Get ( server . URL + "/apis/autoscaling" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
group = unversioned . APIGroup { }
assert . NoError ( decodeResponse ( resp , & group ) )
assert . Equal ( group . APIVersion , "v1" )
// apis/autoscaling/v1 doesn't exist in release-1.1, so the APIVersion field
// should be non-empty in the results returned by the server.
resp , err = http . Get ( server . URL + "/apis/autoscaling/v1" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
resourceList = unversioned . APIResourceList { }
assert . NoError ( decodeResponse ( resp , & resourceList ) )
assert . Equal ( resourceList . APIVersion , "v1" )
}
2015-09-28 18:08:47 +00:00
func TestDiscoveryAtAPIS ( t * testing . T ) {
2016-03-16 14:17:04 +00:00
master , etcdserver , _ , assert := newLimitedMaster ( t )
2016-04-07 22:34:40 +00:00
defer etcdserver . Terminate ( t )
2015-09-28 18:08:47 +00:00
2016-04-07 22:34:40 +00:00
server := httptest . NewServer ( master . HandlerContainer . ServeMux )
resp , err := http . Get ( server . URL + "/apis" )
if ! assert . NoError ( err ) {
t . Errorf ( "unexpected error: %v" , err )
}
2015-09-28 18:08:47 +00:00
2016-04-07 22:34:40 +00:00
assert . Equal ( http . StatusOK , resp . StatusCode )
2015-09-28 18:08:47 +00:00
2016-04-07 22:34:40 +00:00
groupList := unversioned . APIGroupList { }
assert . NoError ( decodeResponse ( resp , & groupList ) )
if err != nil {
t . Fatalf ( "unexpected error: %v" , err )
}
2016-04-15 22:30:15 +00:00
expectGroupNames := sets . NewString ( autoscaling . GroupName , batch . GroupName , apps . GroupName , extensions . GroupName )
2016-04-07 22:34:40 +00:00
expectVersions := map [ string ] [ ] unversioned . GroupVersionForDiscovery {
autoscaling . GroupName : {
2016-02-19 18:31:12 +00:00
{
2016-04-07 22:34:40 +00:00
GroupVersion : testapi . Autoscaling . GroupVersion ( ) . String ( ) ,
Version : testapi . Autoscaling . GroupVersion ( ) . Version ,
2016-02-19 18:31:12 +00:00
} ,
2016-04-07 22:34:40 +00:00
} ,
2016-05-17 08:52:07 +00:00
// batch is using its pkg/apis/batch/ types here since during installation
// both versions get installed and testapi.go currently does not support
// multi-versioned clients
batch . GroupName : {
{
GroupVersion : batchapiv1 . SchemeGroupVersion . String ( ) ,
Version : batchapiv1 . SchemeGroupVersion . Version ,
} ,
{
GroupVersion : batchapiv2alpha1 . SchemeGroupVersion . String ( ) ,
Version : batchapiv2alpha1 . SchemeGroupVersion . Version ,
} ,
} ,
2016-04-15 22:30:15 +00:00
apps . GroupName : {
{
GroupVersion : testapi . Apps . GroupVersion ( ) . String ( ) ,
Version : testapi . Apps . GroupVersion ( ) . Version ,
} ,
} ,
2016-04-07 22:34:40 +00:00
extensions . GroupName : {
2016-02-19 18:31:12 +00:00
{
2016-04-07 22:34:40 +00:00
GroupVersion : testapi . Extensions . GroupVersion ( ) . String ( ) ,
Version : testapi . Extensions . GroupVersion ( ) . Version ,
2016-02-19 18:31:12 +00:00
} ,
2016-04-07 22:34:40 +00:00
} ,
}
expectPreferredVersion := map [ string ] unversioned . GroupVersionForDiscovery {
autoscaling . GroupName : {
2016-03-16 14:17:04 +00:00
GroupVersion : registered . GroupOrDie ( autoscaling . GroupName ) . GroupVersion . String ( ) ,
Version : registered . GroupOrDie ( autoscaling . GroupName ) . GroupVersion . Version ,
2016-04-07 22:34:40 +00:00
} ,
batch . GroupName : {
2016-03-16 14:17:04 +00:00
GroupVersion : registered . GroupOrDie ( batch . GroupName ) . GroupVersion . String ( ) ,
Version : registered . GroupOrDie ( batch . GroupName ) . GroupVersion . Version ,
2016-04-07 22:34:40 +00:00
} ,
2016-04-15 22:30:15 +00:00
apps . GroupName : {
2016-03-16 14:17:04 +00:00
GroupVersion : registered . GroupOrDie ( apps . GroupName ) . GroupVersion . String ( ) ,
Version : registered . GroupOrDie ( apps . GroupName ) . GroupVersion . Version ,
2016-04-15 22:30:15 +00:00
} ,
2016-04-07 22:34:40 +00:00
extensions . GroupName : {
2016-03-16 14:17:04 +00:00
GroupVersion : registered . GroupOrDie ( extensions . GroupName ) . GroupVersion . String ( ) ,
Version : registered . GroupOrDie ( extensions . GroupName ) . GroupVersion . Version ,
2016-04-07 22:34:40 +00:00
} ,
}
2015-12-10 00:35:35 +00:00
2016-06-15 20:21:53 +00:00
assert . Equal ( 4 , len ( groupList . Groups ) )
2016-04-07 22:34:40 +00:00
for _ , group := range groupList . Groups {
if ! expectGroupNames . Has ( group . Name ) {
t . Errorf ( "got unexpected group %s" , group . Name )
}
assert . Equal ( expectVersions [ group . Name ] , group . Versions )
assert . Equal ( expectPreferredVersion [ group . Name ] , group . PreferredVersion )
}
2015-12-10 00:35:35 +00:00
2016-04-07 22:34:40 +00:00
thirdPartyGV := unversioned . GroupVersionForDiscovery { GroupVersion : "company.com/v1" , Version : "v1" }
2016-07-28 06:18:04 +00:00
master . addThirdPartyResourceStorage ( "/apis/company.com/v1" , "foos" , nil ,
2016-04-07 22:34:40 +00:00
unversioned . APIGroup {
Name : "company.com" ,
Versions : [ ] unversioned . GroupVersionForDiscovery { thirdPartyGV } ,
PreferredVersion : thirdPartyGV ,
} )
2015-12-10 00:35:35 +00:00
2016-04-07 22:34:40 +00:00
resp , err = http . Get ( server . URL + "/apis" )
if ! assert . NoError ( err ) {
t . Errorf ( "unexpected error: %v" , err )
}
assert . Equal ( http . StatusOK , resp . StatusCode )
assert . NoError ( decodeResponse ( resp , & groupList ) )
if err != nil {
t . Fatalf ( "unexpected error: %v" , err )
}
2015-12-10 00:35:35 +00:00
2016-06-15 20:21:53 +00:00
assert . Equal ( 5 , len ( groupList . Groups ) )
2015-12-10 00:35:35 +00:00
2016-04-07 22:34:40 +00:00
expectGroupNames . Insert ( "company.com" )
expectVersions [ "company.com" ] = [ ] unversioned . GroupVersionForDiscovery { thirdPartyGV }
expectPreferredVersion [ "company.com" ] = thirdPartyGV
for _ , group := range groupList . Groups {
if ! expectGroupNames . Has ( group . Name ) {
t . Errorf ( "got unexpected group %s" , group . Name )
2016-02-19 18:31:12 +00:00
}
2016-04-07 22:34:40 +00:00
assert . Equal ( expectVersions [ group . Name ] , group . Versions )
assert . Equal ( expectPreferredVersion [ group . Name ] , group . PreferredVersion )
}
2015-09-28 18:08:47 +00:00
}
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
2015-09-09 21:36:19 +00:00
Items [ ] Foo ` json:"items" `
2015-08-20 05:08:26 +00:00
}
2016-05-09 21:47:02 +00:00
func initThirdParty ( t * testing . T , version , name string ) ( * Master , * etcdtesting . EtcdTestServer , * httptest . Server , * assert . Assertions ) {
2016-07-02 05:42:51 +00:00
return initThirdPartyMultiple ( t , [ ] string { version } , [ ] string { name } )
}
func initThirdPartyMultiple ( t * testing . T , versions , names [ ] string ) ( * Master , * etcdtesting . EtcdTestServer , * httptest . Server , * assert . Assertions ) {
2016-02-03 01:29:17 +00:00
master , etcdserver , _ , assert := newMaster ( t )
2016-02-04 05:55:04 +00:00
_ , master . ServiceClusterIPRange , _ = net . ParseCIDR ( "10.0.0.0/24" )
2015-08-20 05:08:26 +00:00
2016-07-02 05:42:51 +00:00
for ix := range names {
api := & extensions . ThirdPartyResource {
ObjectMeta : api . ObjectMeta {
Name : names [ ix ] ,
} ,
Versions : [ ] extensions . APIVersion {
{
Name : versions [ ix ] ,
} ,
} ,
}
2016-07-28 06:18:04 +00:00
hasRsrc , err := master . HasThirdPartyResource ( api )
if err != nil {
t . Errorf ( "Unexpected error: %v" , err )
}
if ! hasRsrc {
err := master . InstallThirdPartyResource ( api )
if ! assert . NoError ( err ) {
t . Errorf ( "Failed to install API: %v" , err )
t . FailNow ( )
}
} else {
t . Errorf ( "Expected %s: %v not to be present!" , names [ ix ] , api )
2016-07-02 05:42:51 +00:00
}
2015-08-19 18:02:01 +00:00
}
2015-08-20 05:08:26 +00:00
2015-11-16 21:46:00 +00:00
server := httptest . NewServer ( master . HandlerContainer . ServeMux )
2016-02-03 01:29:17 +00:00
return master , etcdserver , server , assert
2015-08-20 05:08:26 +00:00
}
2016-07-02 05:42:51 +00:00
func TestInstallMultipleAPIs ( t * testing . T ) {
names := [ ] string { "foo.company.com" , "bar.company.com" }
versions := [ ] string { "v1" , "v1" }
_ , etcdserver , server , assert := initThirdPartyMultiple ( t , versions , names )
defer server . Close ( )
defer etcdserver . Terminate ( t )
for ix := range names {
kind , group , err := thirdpartyresourcedata . ExtractApiGroupAndKind (
& extensions . ThirdPartyResource { ObjectMeta : api . ObjectMeta { Name : names [ ix ] } } )
assert . NoError ( err , "Failed to extract group & kind" )
plural , _ := meta . KindToResource ( unversioned . GroupVersionKind {
Group : group ,
Version : versions [ ix ] ,
Kind : kind ,
} )
resp , err := http . Get (
fmt . Sprintf ( "%s/apis/%s/%s/namespaces/default/%s" , server . URL , group , versions [ ix ] , plural . Resource ) )
if ! assert . NoError ( err , "Failed to do HTTP GET" ) {
return
}
defer resp . Body . Close ( )
assert . Equal ( http . StatusOK , resp . StatusCode )
data , err := ioutil . ReadAll ( resp . Body )
assert . NoError ( err )
obj := map [ string ] interface { } { }
if err = json . Unmarshal ( data , & obj ) ; err != nil {
assert . NoError ( err , fmt . Sprintf ( "unexpected error: %v" , err ) )
}
kindOut , found := obj [ "kind" ]
if ! found {
t . Errorf ( "Missing 'kind' in %v" , obj )
}
assert . Equal ( kindOut , kind + "List" )
}
}
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-09 21:36:19 +00:00
tests := [ ] struct {
items [ ] Foo
2016-05-09 21:47:02 +00:00
name string
test string
2015-09-09 21:36:19 +00:00
} {
2016-05-09 21:47:02 +00:00
{
name : "foo.company.com" ,
test : "null" ,
} ,
2015-09-09 21:36:19 +00:00
{
items : [ ] Foo { } ,
2016-05-09 21:47:02 +00:00
name : "foo.company.com" ,
test : "empty" ,
} ,
{
items : [ ] Foo { } ,
name : "policy.company.com" ,
test : "plurals" ,
2015-09-09 21:36:19 +00:00
} ,
{
items : [ ] Foo {
{
ObjectMeta : api . ObjectMeta {
Name : "test" ,
} ,
TypeMeta : unversioned . TypeMeta {
Kind : "Foo" ,
APIVersion : version ,
} ,
SomeField : "test field" ,
OtherField : 10 ,
} ,
{
ObjectMeta : api . ObjectMeta {
Name : "bar" ,
} ,
TypeMeta : unversioned . TypeMeta {
Kind : "Foo" ,
APIVersion : version ,
} ,
SomeField : "test field another" ,
OtherField : 20 ,
} ,
} ,
2016-05-09 21:47:02 +00:00
name : "foo.company.com" ,
test : "real list" ,
2015-09-09 21:36:19 +00:00
} ,
}
for _ , test := range tests {
2015-11-17 14:21:42 +00:00
func ( ) {
2016-05-09 21:47:02 +00:00
master , etcdserver , server , assert := initThirdParty ( t , version , test . name )
2016-04-21 11:50:55 +00:00
defer server . Close ( )
2015-11-17 14:21:42 +00:00
defer etcdserver . Terminate ( t )
2016-05-09 21:47:02 +00:00
kind , group , err := thirdpartyresourcedata . ExtractApiGroupAndKind (
& extensions . ThirdPartyResource { ObjectMeta : api . ObjectMeta { Name : test . name } } )
assert . NoError ( err , test . test )
plural , _ := meta . KindToResource ( unversioned . GroupVersionKind {
Group : group ,
Version : version ,
Kind : kind ,
} )
2015-11-17 14:21:42 +00:00
if test . items != nil {
2016-08-24 23:35:21 +00:00
s , destroyFunc := generic . NewRawStorage ( master . thirdPartyStorageConfig )
defer destroyFunc ( )
2016-08-08 22:12:54 +00:00
err := createThirdPartyList (
2016-08-24 23:35:21 +00:00
s ,
2016-05-09 21:47:02 +00:00
fmt . Sprintf ( "/ThirdPartyResourceData/%s/%s/default" , group , plural . Resource ) ,
test . items )
if ! assert . NoError ( err , test . test ) {
2016-03-10 04:06:31 +00:00
return
}
2015-11-17 14:21:42 +00:00
}
2015-08-20 05:08:26 +00:00
2016-05-09 21:47:02 +00:00
resp , err := http . Get (
fmt . Sprintf ( "%s/apis/%s/%s/namespaces/default/%s" , server . URL , group , version , plural . Resource ) )
if ! assert . NoError ( err , test . test ) {
2015-11-17 14:21:42 +00:00
return
}
defer resp . Body . Close ( )
2015-09-03 17:35:04 +00:00
2016-05-09 21:47:02 +00:00
assert . Equal ( http . StatusOK , resp . StatusCode , test . test )
2015-08-20 05:08:26 +00:00
2015-11-17 14:21:42 +00:00
data , err := ioutil . ReadAll ( resp . Body )
2016-05-09 21:47:02 +00:00
assert . NoError ( err , test . test )
2015-08-20 05:08:26 +00:00
2015-11-17 14:21:42 +00:00
list := FooList { }
if err = json . Unmarshal ( data , & list ) ; err != nil {
2016-05-09 21:47:02 +00:00
assert . NoError ( err , "unexpected error: %v %s" , err , test . test )
2015-11-17 14:21:42 +00:00
}
2015-08-20 05:08:26 +00:00
2015-11-17 14:21:42 +00:00
if test . items == nil {
if len ( list . Items ) != 0 {
2016-05-09 21:47:02 +00:00
assert . NoError ( err , "expected no items, saw: %v %s" , err , list . Items , test . test )
2015-11-17 14:21:42 +00:00
}
return
2015-09-09 21:36:19 +00:00
}
2015-09-03 17:35:04 +00:00
2015-11-17 14:21:42 +00:00
if len ( list . Items ) != len ( test . items ) {
2016-05-09 21:47:02 +00:00
t . Fatalf ( "(%s) unexpected length: %d vs %d" , test . name , len ( list . Items ) , len ( test . items ) )
2015-09-09 21:36:19 +00:00
}
2015-11-17 14:21:42 +00:00
// The order of elements in LIST is not guaranteed.
mapping := make ( map [ string ] int )
for ix := range test . items {
mapping [ test . items [ ix ] . Name ] = ix
}
for ix := range list . Items {
// Copy things that are set dynamically on the server
expectedObj := test . items [ mapping [ list . Items [ ix ] . Name ] ]
expectedObj . SelfLink = list . Items [ ix ] . SelfLink
expectedObj . ResourceVersion = list . Items [ ix ] . ResourceVersion
expectedObj . Namespace = list . Items [ ix ] . Namespace
expectedObj . UID = list . Items [ ix ] . UID
expectedObj . CreationTimestamp = list . Items [ ix ] . CreationTimestamp
// We endure the order of items by sorting them (using 'mapping')
// so that this function passes.
if ! reflect . DeepEqual ( list . Items [ ix ] , expectedObj ) {
2016-05-09 21:47:02 +00:00
t . Errorf ( "(%s) expected:\n%#v\nsaw:\n%#v\n" , test . name , expectedObj , list . Items [ ix ] )
2015-11-17 14:21:42 +00:00
}
}
} ( )
2015-09-09 21:36:19 +00:00
}
2015-08-20 05:08:26 +00:00
}
2015-11-20 08:16:41 +00:00
func encodeToThirdParty ( name string , obj interface { } ) ( runtime . Object , error ) {
2015-08-20 05:08:26 +00:00
serial , err := json . Marshal ( obj )
if err != nil {
return nil , err
}
2015-10-09 22:49:10 +00:00
thirdPartyData := extensions . ThirdPartyResourceData {
2015-08-20 05:08:26 +00:00
ObjectMeta : api . ObjectMeta { Name : name } ,
Data : serial ,
}
2015-11-20 08:16:41 +00:00
return & thirdPartyData , nil
2015-08-20 05:08:26 +00:00
}
2016-03-25 13:24:58 +00:00
func createThirdPartyObject ( s storage . Interface , path , name string , obj interface { } ) error {
2015-08-20 05:08:26 +00:00
data , err := encodeToThirdParty ( name , obj )
if err != nil {
return err
}
2016-03-25 13:24:58 +00:00
return s . Create ( context . TODO ( ) , etcdtest . AddPrefix ( path ) , data , nil , 0 )
2015-08-20 05:08:26 +00:00
}
2016-03-25 13:24:58 +00:00
func createThirdPartyList ( s storage . Interface , path string , list [ ] Foo ) error {
2015-09-09 21:36:19 +00:00
for _ , obj := range list {
2016-03-25 13:24:58 +00:00
if err := createThirdPartyObject ( s , path + "/" + obj . Name , obj . Name , obj ) ; err != nil {
2015-09-09 21:36:19 +00:00
return err
}
}
return nil
}
2015-08-20 05:08:26 +00:00
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
}
2016-09-01 23:39:10 +00:00
func writeResponseToFile ( resp * http . Response , filename string ) error {
defer resp . Body . Close ( )
data , err := ioutil . ReadAll ( resp . Body )
if err != nil {
return err
}
return ioutil . WriteFile ( filename , data , 0755 )
}
2015-08-20 05:08:26 +00:00
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 ) {
2016-05-09 21:47:02 +00:00
master , etcdserver , server , assert := initThirdParty ( t , version , "foo.company.com" )
2016-04-21 11:50:55 +00:00
defer server . Close ( )
2015-11-17 14:21:42 +00:00
defer etcdserver . Terminate ( t )
2015-08-20 05:08:26 +00:00
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 ,
}
2016-08-24 23:35:21 +00:00
s , destroyFunc := generic . NewRawStorage ( master . thirdPartyStorageConfig )
defer destroyFunc ( )
2016-08-08 22:12:54 +00:00
if ! assert . NoError ( createThirdPartyObject ( s , "/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-11-17 14:21:42 +00:00
expectedObj . ResourceVersion = item . ResourceVersion
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 ) {
2016-05-12 22:59:19 +00:00
registered . AddThirdPartyAPIGroupVersions ( unversioned . GroupVersion { Group : "company.com" , Version : "v1" } , unversioned . GroupVersion { Group : "company.com" , Version : "v3" } )
2015-09-01 05:28:08 +00:00
for _ , version := range versionsToTest {
testInstallThirdPartyAPIPostForVersion ( t , version )
}
}
func testInstallThirdPartyAPIPostForVersion ( t * testing . T , version string ) {
2016-05-09 21:47:02 +00:00
master , etcdserver , server , assert := initThirdParty ( t , version , "foo.company.com" )
2016-04-21 11:50:55 +00:00
defer server . Close ( )
2015-11-17 14:21:42 +00:00
defer etcdserver . Terminate ( t )
2015-08-20 05:08:26 +00:00
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" ,
2015-09-29 21:36:47 +00:00
APIVersion : "company.com/" + 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 ) {
2016-02-25 06:07:54 +00:00
t . Fatalf ( "unexpected error: %v" , err )
2015-08-20 05:08:26 +00:00
}
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
2015-11-17 14:21:42 +00:00
expectedObj . ResourceVersion = item . ResourceVersion
2015-09-03 00:13:38 +00:00
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
}
2015-11-20 08:16:41 +00:00
thirdPartyObj := extensions . ThirdPartyResourceData { }
2016-08-24 23:35:21 +00:00
s , destroyFunc := generic . NewRawStorage ( master . thirdPartyStorageConfig )
defer destroyFunc ( )
2016-08-08 22:12:54 +00:00
err = s . Get ( context . TODO ( ) , etcdtest . AddPrefix ( "/ThirdPartyResourceData/company.com/foos/default/test" ) , & thirdPartyObj , 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-08-20 05:08:26 +00:00
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 ) {
2016-05-09 21:47:02 +00:00
master , etcdserver , server , assert := initThirdParty ( t , version , "foo.company.com" )
2016-04-21 11:50:55 +00:00
defer server . Close ( )
2015-11-17 14:21:42 +00:00
defer etcdserver . Terminate ( t )
2015-08-20 05:08:26 +00:00
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 ,
}
2016-08-24 23:35:21 +00:00
s , destroyFunc := generic . NewRawStorage ( master . thirdPartyStorageConfig )
defer destroyFunc ( )
2016-08-08 22:12:54 +00:00
if ! assert . NoError ( createThirdPartyObject ( s , "/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
2015-11-17 14:21:42 +00:00
expectedObj . ResourceVersion = item . ResourceVersion
2015-09-03 00:13:38 +00:00
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-11-17 14:21:42 +00:00
expectedDeletedKey := etcdtest . AddPrefix ( "ThirdPartyResourceData/company.com/foos/default/test" )
2015-11-20 08:16:41 +00:00
thirdPartyObj := extensions . ThirdPartyResourceData { }
2016-08-08 22:12:54 +00:00
err = s . Get ( context . TODO ( ) , expectedDeletedKey , & thirdPartyObj , false )
2015-12-03 16:09:45 +00:00
if ! storage . IsNotFound ( err ) {
2015-11-17 14:21:42 +00:00
t . Errorf ( "expected deletion didn't happen: %v" , err )
2015-08-20 05:08:26 +00:00
}
}
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
}
2015-09-09 21:36:19 +00:00
2016-02-25 06:07:54 +00:00
func TestInstallThirdPartyAPIListOptions ( t * testing . T ) {
for _ , version := range versionsToTest {
testInstallThirdPartyAPIListOptionsForVersion ( t , version )
}
}
func testInstallThirdPartyAPIListOptionsForVersion ( t * testing . T , version string ) {
2016-05-09 21:47:02 +00:00
_ , etcdserver , server , assert := initThirdParty ( t , version , "foo.company.com" )
2016-04-21 11:50:55 +00:00
defer server . Close ( )
2016-02-25 06:07:54 +00:00
defer etcdserver . Terminate ( t )
// send a GET request with query parameter
resp , err := httpGetWithRV ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos" )
if ! assert . NoError ( err ) {
t . Fatalf ( "unexpected error: %v" , err )
}
assert . Equal ( http . StatusOK , resp . StatusCode )
}
func httpGetWithRV ( url string ) ( * http . Response , error ) {
req , err := http . NewRequest ( "GET" , url , nil )
if err != nil {
return nil , err
}
q := req . URL . Query ( )
// resourceversion is part of a ListOptions
q . Add ( "resourceversion" , "0" )
req . URL . RawQuery = q . Encode ( )
client := & http . Client { }
return client . Do ( req )
}
2015-09-09 21:36:19 +00:00
func TestInstallThirdPartyResourceRemove ( t * testing . T ) {
for _ , version := range versionsToTest {
testInstallThirdPartyResourceRemove ( t , version )
}
}
func testInstallThirdPartyResourceRemove ( t * testing . T , version string ) {
2016-05-09 21:47:02 +00:00
master , etcdserver , server , assert := initThirdParty ( t , version , "foo.company.com" )
2016-04-21 11:50:55 +00:00
defer server . Close ( )
2015-11-17 14:21:42 +00:00
defer etcdserver . Terminate ( t )
2015-09-09 21:36:19 +00:00
expectedObj := Foo {
ObjectMeta : api . ObjectMeta {
Name : "test" ,
} ,
TypeMeta : unversioned . TypeMeta {
Kind : "Foo" ,
} ,
SomeField : "test field" ,
OtherField : 10 ,
}
2016-08-24 23:35:21 +00:00
s , destroyFunc := generic . NewRawStorage ( master . thirdPartyStorageConfig )
defer destroyFunc ( )
2016-08-08 22:12:54 +00:00
if ! assert . NoError ( createThirdPartyObject ( s , "/ThirdPartyResourceData/company.com/foos/default/test" , "test" , expectedObj ) ) {
2015-09-09 21:36:19 +00:00
t . FailNow ( )
return
}
secondObj := expectedObj
secondObj . Name = "bar"
2016-08-08 22:12:54 +00:00
if ! assert . NoError ( createThirdPartyObject ( s , "/ThirdPartyResourceData/company.com/foos/default/bar" , "bar" , secondObj ) ) {
2015-09-09 21:36:19 +00:00
t . FailNow ( )
return
}
resp , err := http . Get ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos/test" )
if ! assert . NoError ( err ) {
t . FailNow ( )
return
}
if resp . StatusCode != http . StatusOK {
t . Errorf ( "unexpected status: %v" , resp )
}
item := Foo { }
if err := decodeResponse ( resp , & item ) ; err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
// TODO: validate etcd set things here
item . ObjectMeta = expectedObj . ObjectMeta
if ! assert . True ( reflect . DeepEqual ( item , expectedObj ) ) {
t . Errorf ( "expected:\n%v\nsaw:\n%v\n" , expectedObj , item )
}
2016-09-23 19:10:47 +00:00
path := extensionsrest . MakeThirdPartyPath ( "company.com" )
2016-07-28 06:18:04 +00:00
master . RemoveThirdPartyResource ( path + "/foos" )
2015-09-09 21:36:19 +00:00
resp , err = http . Get ( server . URL + "/apis/company.com/" + version + "/namespaces/default/foos/test" )
if ! assert . NoError ( err ) {
return
}
if resp . StatusCode != http . StatusNotFound {
t . Errorf ( "unexpected status: %v" , resp )
}
2015-11-17 14:21:42 +00:00
expectedDeletedKeys := [ ] string {
etcdtest . AddPrefix ( "/ThirdPartyResourceData/company.com/foos/default/test" ) ,
etcdtest . AddPrefix ( "/ThirdPartyResourceData/company.com/foos/default/bar" ) ,
2015-09-09 21:36:19 +00:00
}
2015-11-17 14:21:42 +00:00
for _ , key := range expectedDeletedKeys {
2015-11-20 08:16:41 +00:00
thirdPartyObj := extensions . ThirdPartyResourceData { }
2016-08-24 23:35:21 +00:00
s , destroyFunc := generic . NewRawStorage ( master . thirdPartyStorageConfig )
2016-08-08 22:12:54 +00:00
err := s . Get ( context . TODO ( ) , key , & thirdPartyObj , false )
2015-12-03 16:09:45 +00:00
if ! storage . IsNotFound ( err ) {
2015-11-17 14:21:42 +00:00
t . Errorf ( "expected deletion didn't happen: %v" , err )
}
2016-08-24 23:35:21 +00:00
destroyFunc ( )
2015-09-09 21:36:19 +00:00
}
installed := master . ListThirdPartyResources ( )
if len ( installed ) != 0 {
t . Errorf ( "Resource(s) still installed: %v" , installed )
}
2015-11-16 21:46:00 +00:00
services := master . HandlerContainer . RegisteredWebServices ( )
2015-09-09 21:36:19 +00:00
for ix := range services {
if strings . HasPrefix ( services [ ix ] . RootPath ( ) , "/apis/company.com" ) {
t . Errorf ( "Web service still installed at %s: %#v" , services [ ix ] . RootPath ( ) , services [ ix ] )
}
}
}
2016-02-25 22:13:28 +00:00
func TestThirdPartyDiscovery ( t * testing . T ) {
for _ , version := range versionsToTest {
testThirdPartyDiscovery ( t , version )
}
}
2016-04-22 00:48:35 +00:00
type FakeTunneler struct {
SecondsSinceSyncValue int64
SecondsSinceSSHKeySyncValue int64
}
func ( t * FakeTunneler ) Run ( genericapiserver . AddressFunc ) { }
func ( t * FakeTunneler ) Stop ( ) { }
func ( t * FakeTunneler ) Dial ( net , addr string ) ( net . Conn , error ) { return nil , nil }
func ( t * FakeTunneler ) SecondsSinceSync ( ) int64 { return t . SecondsSinceSyncValue }
func ( t * FakeTunneler ) SecondsSinceSSHKeySync ( ) int64 { return t . SecondsSinceSSHKeySyncValue }
// TestIsTunnelSyncHealthy verifies that the 600 second lag test
// is honored.
func TestIsTunnelSyncHealthy ( t * testing . T ) {
assert := assert . New ( t )
tunneler := & FakeTunneler { }
master := & Master {
GenericAPIServer : & genericapiserver . GenericAPIServer { } ,
tunneler : tunneler ,
}
// Pass case: 540 second lag
tunneler . SecondsSinceSyncValue = 540
err := master . IsTunnelSyncHealthy ( nil )
assert . NoError ( err , "IsTunnelSyncHealthy() should not have returned an error." )
// Fail case: 720 second lag
tunneler . SecondsSinceSyncValue = 720
err = master . IsTunnelSyncHealthy ( nil )
assert . Error ( err , "IsTunnelSyncHealthy() should have returned an error." )
}
2016-02-25 22:13:28 +00:00
func testThirdPartyDiscovery ( t * testing . T , version string ) {
2016-05-09 21:47:02 +00:00
_ , etcdserver , server , assert := initThirdParty ( t , version , "foo.company.com" )
2016-04-21 11:50:55 +00:00
defer server . Close ( )
2016-02-25 22:13:28 +00:00
defer etcdserver . Terminate ( t )
resp , err := http . Get ( server . URL + "/apis/company.com/" )
if ! assert . NoError ( err ) {
return
}
assert . Equal ( http . StatusOK , resp . StatusCode )
group := unversioned . APIGroup { }
assert . NoError ( decodeResponse ( resp , & group ) )
assert . Equal ( group . APIVersion , "v1" )
assert . Equal ( group . Kind , "APIGroup" )
assert . Equal ( group . Name , "company.com" )
2016-04-07 22:34:40 +00:00
expectedVersion := unversioned . GroupVersionForDiscovery {
GroupVersion : "company.com/" + version ,
Version : version ,
}
assert . Equal ( group . Versions , [ ] unversioned . GroupVersionForDiscovery { expectedVersion } )
assert . Equal ( group . PreferredVersion , expectedVersion )
2016-02-25 22:13:28 +00:00
resp , err = http . Get ( server . URL + "/apis/company.com/" + version )
if ! assert . NoError ( err ) {
return
}
assert . Equal ( http . StatusOK , resp . StatusCode )
resourceList := unversioned . APIResourceList { }
assert . NoError ( decodeResponse ( resp , & resourceList ) )
assert . Equal ( resourceList . APIVersion , "v1" )
assert . Equal ( resourceList . Kind , "APIResourceList" )
assert . Equal ( resourceList . GroupVersion , "company.com/" + version )
assert . Equal ( resourceList . APIResources , [ ] unversioned . APIResource {
{
Name : "foos" ,
Namespaced : true ,
Kind : "Foo" ,
} ,
} )
}
2016-08-18 15:12:26 +00:00
// TestValidOpenAPISpec verifies that the open api is added
// at the proper endpoint and the spec is valid.
func TestValidOpenAPISpec ( t * testing . T ) {
_ , etcdserver , config , assert := setUp ( t )
defer etcdserver . Terminate ( t )
2016-09-28 18:52:28 +00:00
config . GenericConfig . OpenAPIDefinitions = openapi . OpenAPIDefinitions
config . GenericConfig . EnableOpenAPISupport = true
config . GenericConfig . EnableIndex = true
config . GenericConfig . OpenAPIInfo = spec . Info {
2016-08-18 15:12:26 +00:00
InfoProps : spec . InfoProps {
Title : "Kubernetes" ,
Version : "unversioned" ,
} ,
}
2016-09-27 15:52:31 +00:00
master , err := config . Complete ( ) . New ( )
2016-08-18 15:12:26 +00:00
if err != nil {
t . Fatalf ( "Error in bringing up the master: %v" , err )
}
// make sure swagger.json is not registered before calling install api.
server := httptest . NewServer ( master . HandlerContainer . ServeMux )
resp , err := http . Get ( server . URL + "/swagger.json" )
if ! assert . NoError ( err ) {
t . Errorf ( "unexpected error: %v" , err )
}
assert . Equal ( http . StatusNotFound , resp . StatusCode )
master . InstallOpenAPI ( )
resp , err = http . Get ( server . URL + "/swagger.json" )
if ! assert . NoError ( err ) {
t . Errorf ( "unexpected error: %v" , err )
}
assert . Equal ( http . StatusOK , resp . StatusCode )
// as json schema
var sch spec . Schema
if assert . NoError ( decodeResponse ( resp , & sch ) ) {
validator := validate . NewSchemaValidator ( spec . MustLoadSwagger20Schema ( ) , nil , "" , strfmt . Default )
res := validator . Validate ( & sch )
assert . NoError ( res . AsError ( ) )
}
2016-09-01 23:39:10 +00:00
// TODO(mehdy): The actual validation part of these tests are timing out on jerkin but passing locally. Enable it after debugging timeout issue.
disableValidation := true
// Saving specs to a temporary folder is a good way to debug spec generation without bringing up an actual
// api server.
saveSwaggerSpecs := false
2016-08-18 15:12:26 +00:00
// Validate OpenApi spec
doc , err := loads . Spec ( server . URL + "/swagger.json" )
if assert . NoError ( err ) {
2016-09-01 23:39:10 +00:00
validator := validate . NewSpecValidator ( doc . Schema ( ) , strfmt . Default )
if ! disableValidation {
res , warns := validator . Validate ( doc )
assert . NoError ( res . AsError ( ) )
if ! warns . IsValid ( ) {
t . Logf ( "Open API spec on root has some warnings : %v" , warns )
}
} else {
t . Logf ( "Validation is disabled because it is timing out on jenkins put passing locally." )
}
2016-08-18 15:12:26 +00:00
}
2016-09-01 23:39:10 +00:00
// validate specs on each end-point
resp , err = http . Get ( server . URL )
if ! assert . NoError ( err ) {
t . Errorf ( "unexpected error: %v" , err )
}
assert . Equal ( http . StatusOK , resp . StatusCode )
var list unversioned . RootPaths
if assert . NoError ( decodeResponse ( resp , & list ) ) {
for _ , path := range list . Paths {
if ! strings . HasPrefix ( path , "/api" ) {
continue
}
t . Logf ( "Validating open API spec on %v ..." , path )
if saveSwaggerSpecs {
resp , err = http . Get ( server . URL + path + "/swagger.json" )
if ! assert . NoError ( err ) {
t . Errorf ( "unexpected error: %v" , err )
}
assert . Equal ( http . StatusOK , resp . StatusCode )
assert . NoError ( writeResponseToFile ( resp , "/tmp/swagger_" + strings . Replace ( path , "/" , "_" , - 1 ) + ".json" ) )
}
// Validate OpenApi spec on path
doc , err := loads . Spec ( server . URL + path + "/swagger.json" )
if assert . NoError ( err ) {
validator := validate . NewSpecValidator ( doc . Schema ( ) , strfmt . Default )
if ! disableValidation {
res , warns := validator . Validate ( doc )
assert . NoError ( res . AsError ( ) )
if ! warns . IsValid ( ) {
t . Logf ( "Open API spec on %v has some warnings : %v" , path , warns )
}
} else {
t . Logf ( "Validation is disabled because it is timing out on jenkins put passing locally." )
}
}
}
}
2016-08-18 15:12:26 +00:00
}