2015-09-10 21:32:57 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2015-09-10 21:32:57 +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 .
* /
2018-10-05 11:06:12 +00:00
package apply
2015-09-10 21:32:57 +00:00
import (
"bytes"
"encoding/json"
2017-11-21 18:21:55 +00:00
"errors"
2016-06-09 04:14:17 +00:00
"fmt"
2017-11-20 17:30:32 +00:00
"io"
2015-09-10 21:32:57 +00:00
"io/ioutil"
"net/http"
"os"
2017-11-21 18:21:55 +00:00
"path/filepath"
2017-02-19 02:32:43 +00:00
"strings"
2015-09-10 21:32:57 +00:00
"testing"
2018-10-05 00:10:08 +00:00
"github.com/googleapis/gnostic/OpenAPIv2"
2015-09-10 21:32:57 +00:00
"github.com/spf13/cobra"
2018-12-19 16:18:53 +00:00
appsv1 "k8s.io/api/apps/v1"
2018-04-16 21:34:05 +00:00
corev1 "k8s.io/api/core/v1"
2017-01-13 17:48:50 +00:00
kubeerr "k8s.io/apimachinery/pkg/api/errors"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/api/meta"
2017-02-08 19:04:18 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
2017-11-21 18:21:55 +00:00
sptest "k8s.io/apimachinery/pkg/util/strategicpatch/testing"
2018-08-21 10:46:39 +00:00
"k8s.io/cli-runtime/pkg/genericclioptions"
2018-10-05 12:38:38 +00:00
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
2018-05-17 15:27:44 +00:00
dynamicfakeclient "k8s.io/client-go/dynamic/fake"
2017-11-21 18:21:55 +00:00
restclient "k8s.io/client-go/rest"
2017-01-24 15:00:24 +00:00
"k8s.io/client-go/rest/fake"
2018-05-17 15:27:44 +00:00
clienttesting "k8s.io/client-go/testing"
2016-10-18 22:53:26 +00:00
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
2015-09-10 21:32:57 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2017-11-21 18:21:55 +00:00
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
2018-02-21 17:10:38 +00:00
"k8s.io/kubernetes/pkg/kubectl/scheme"
2015-09-10 21:32:57 +00:00
)
2017-11-21 18:21:55 +00:00
var (
2018-10-05 00:10:08 +00:00
fakeSchema = sptest . Fake { Path : filepath . Join ( ".." , ".." , ".." , ".." , "api" , "openapi-spec" , "swagger.json" ) }
2017-11-21 18:21:55 +00:00
testingOpenAPISchemaFns = [ ] func ( ) ( openapi . Resources , error ) { nil , AlwaysErrorOpenAPISchemaFn , openAPISchemaFn }
AlwaysErrorOpenAPISchemaFn = func ( ) ( openapi . Resources , error ) {
return nil , errors . New ( "cannot get openapi spec" )
}
openAPISchemaFn = func ( ) ( openapi . Resources , error ) {
s , err := fakeSchema . OpenAPISchema ( )
if err != nil {
return nil , err
}
return openapi . NewOpenAPIData ( s )
}
2018-10-02 18:58:57 +00:00
codec = scheme . Codecs . LegacyCodec ( scheme . Scheme . PrioritizedVersionsAllGroups ( ) ... )
2017-11-21 18:21:55 +00:00
)
2015-09-10 21:32:57 +00:00
func TestApplyExtraArgsFail ( t * testing . T ) {
2018-02-22 14:52:10 +00:00
f := cmdtesting . NewTestFactory ( )
2018-03-08 22:23:55 +00:00
defer f . Cleanup ( )
2018-04-19 21:43:28 +00:00
c := NewCmdApply ( "kubectl" , f , genericclioptions . NewTestIOStreamsDiscard ( ) )
2015-09-10 21:32:57 +00:00
if validateApplyArgs ( c , [ ] string { "rc" } ) == nil {
t . Fatalf ( "unexpected non-error" )
}
}
func validateApplyArgs ( cmd * cobra . Command , args [ ] string ) error {
if len ( args ) != 0 {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , "Unexpected args: %v" , args )
2015-09-10 21:32:57 +00:00
}
return nil
}
const (
2018-10-05 12:38:38 +00:00
filenameCM = "../../../../test/fixtures/pkg/kubectl/cmd/apply/cm.yaml"
filenameRC = "../../../../test/fixtures/pkg/kubectl/cmd/apply/rc.yaml"
filenameRCArgs = "../../../../test/fixtures/pkg/kubectl/cmd/apply/rc-args.yaml"
filenameRCLastAppliedArgs = "../../../../test/fixtures/pkg/kubectl/cmd/apply/rc-lastapplied-args.yaml"
filenameRCNoAnnotation = "../../../../test/fixtures/pkg/kubectl/cmd/apply/rc-no-annotation.yaml"
filenameRCLASTAPPLIED = "../../../../test/fixtures/pkg/kubectl/cmd/apply/rc-lastapplied.yaml"
filenameSVC = "../../../../test/fixtures/pkg/kubectl/cmd/apply/service.yaml"
filenameRCSVC = "../../../../test/fixtures/pkg/kubectl/cmd/apply/rc-service.yaml"
filenameNoExistRC = "../../../../test/fixtures/pkg/kubectl/cmd/apply/rc-noexist.yaml"
filenameRCPatchTest = "../../../../test/fixtures/pkg/kubectl/cmd/apply/patch.json"
dirName = "../../../../test/fixtures/pkg/kubectl/cmd/apply/testdir"
filenameRCJSON = "../../../../test/fixtures/pkg/kubectl/cmd/apply/rc.json"
filenameWidgetClientside = "../../../../test/fixtures/pkg/kubectl/cmd/apply/widget-clientside.yaml"
filenameWidgetServerside = "../../../../test/fixtures/pkg/kubectl/cmd/apply/widget-serverside.yaml"
2015-09-10 21:32:57 +00:00
)
2018-10-03 14:32:33 +00:00
func readConfigMapList ( t * testing . T , filename string ) [ ] [ ] byte {
2018-04-16 21:34:05 +00:00
data := readBytesFromFile ( t , filename )
cmList := corev1 . ConfigMapList { }
2018-10-02 18:58:57 +00:00
if err := runtime . DecodeInto ( codec , data , & cmList ) ; err != nil {
2018-04-16 21:34:05 +00:00
t . Fatal ( err )
}
2018-10-03 14:32:33 +00:00
var listCmBytes [ ] [ ] byte
for _ , cm := range cmList . Items {
cmBytes , err := runtime . Encode ( codec , & cm )
if err != nil {
t . Fatal ( err )
}
listCmBytes = append ( listCmBytes , cmBytes )
2018-04-16 21:34:05 +00:00
}
2018-10-03 14:32:33 +00:00
return listCmBytes
2018-04-16 21:34:05 +00:00
}
2015-09-10 21:32:57 +00:00
func readBytesFromFile ( t * testing . T , filename string ) [ ] byte {
file , err := os . Open ( filename )
if err != nil {
t . Fatal ( err )
}
2016-09-06 07:58:22 +00:00
defer file . Close ( )
2015-09-10 21:32:57 +00:00
data , err := ioutil . ReadAll ( file )
if err != nil {
t . Fatal ( err )
}
return data
}
2016-11-11 22:38:36 +00:00
func readReplicationController ( t * testing . T , filenameRC string ) ( string , [ ] byte ) {
rcObj := readReplicationControllerFromFile ( t , filenameRC )
metaAccessor , err := meta . Accessor ( rcObj )
if err != nil {
t . Fatal ( err )
}
2018-10-02 18:58:57 +00:00
rcBytes , err := runtime . Encode ( codec , rcObj )
2016-11-11 22:38:36 +00:00
if err != nil {
t . Fatal ( err )
}
return metaAccessor . GetName ( ) , rcBytes
}
2018-08-03 11:51:44 +00:00
func readReplicationControllerFromFile ( t * testing . T , filename string ) * corev1 . ReplicationController {
2015-09-10 21:32:57 +00:00
data := readBytesFromFile ( t , filename )
2018-08-03 11:51:44 +00:00
rc := corev1 . ReplicationController { }
2018-10-02 18:58:57 +00:00
if err := runtime . DecodeInto ( codec , data , & rc ) ; err != nil {
2015-09-10 21:32:57 +00:00
t . Fatal ( err )
}
return & rc
}
2017-02-08 19:04:18 +00:00
func readUnstructuredFromFile ( t * testing . T , filename string ) * unstructured . Unstructured {
data := readBytesFromFile ( t , filename )
unst := unstructured . Unstructured { }
2018-10-02 18:58:57 +00:00
if err := runtime . DecodeInto ( codec , data , & unst ) ; err != nil {
2017-02-08 19:04:18 +00:00
t . Fatal ( err )
}
return & unst
}
2018-08-03 11:51:44 +00:00
func readServiceFromFile ( t * testing . T , filename string ) * corev1 . Service {
2015-09-10 21:32:57 +00:00
data := readBytesFromFile ( t , filename )
2018-08-03 11:51:44 +00:00
svc := corev1 . Service { }
2018-10-02 18:58:57 +00:00
if err := runtime . DecodeInto ( codec , data , & svc ) ; err != nil {
2015-09-10 21:32:57 +00:00
t . Fatal ( err )
}
return & svc
}
func annotateRuntimeObject ( t * testing . T , originalObj , currentObj runtime . Object , kind string ) ( string , [ ] byte ) {
2016-03-25 08:57:45 +00:00
originalAccessor , err := meta . Accessor ( originalObj )
2015-09-10 21:32:57 +00:00
if err != nil {
t . Fatal ( err )
}
2017-02-08 19:04:18 +00:00
// The return value of this function is used in the body of the GET
// request in the unit tests. Here we are adding a misc label to the object.
// In tests, the validatePatchApplication() gets called in PATCH request
// handler in fake round tripper. validatePatchApplication call
// checks that this DELETE_ME label was deleted by the apply implementation in
// kubectl.
2016-03-25 08:57:45 +00:00
originalLabels := originalAccessor . GetLabels ( )
originalLabels [ "DELETE_ME" ] = "DELETE_ME"
originalAccessor . SetLabels ( originalLabels )
2018-10-02 18:58:57 +00:00
original , err := runtime . Encode ( unstructured . JSONFallbackEncoder { Encoder : codec } , originalObj )
2015-09-10 21:32:57 +00:00
if err != nil {
t . Fatal ( err )
}
2016-03-25 08:57:45 +00:00
currentAccessor , err := meta . Accessor ( currentObj )
2015-09-10 21:32:57 +00:00
if err != nil {
t . Fatal ( err )
}
2016-03-25 08:57:45 +00:00
currentAnnotations := currentAccessor . GetAnnotations ( )
if currentAnnotations == nil {
currentAnnotations = make ( map [ string ] string )
2015-09-10 21:32:57 +00:00
}
2018-08-03 11:51:44 +00:00
currentAnnotations [ corev1 . LastAppliedConfigAnnotation ] = string ( original )
2016-03-25 08:57:45 +00:00
currentAccessor . SetAnnotations ( currentAnnotations )
2018-10-02 18:58:57 +00:00
current , err := runtime . Encode ( unstructured . JSONFallbackEncoder { Encoder : codec } , currentObj )
2015-09-10 21:32:57 +00:00
if err != nil {
t . Fatal ( err )
}
2016-03-25 08:57:45 +00:00
return currentAccessor . GetName ( ) , current
2015-09-10 21:32:57 +00:00
}
func readAndAnnotateReplicationController ( t * testing . T , filename string ) ( string , [ ] byte ) {
rc1 := readReplicationControllerFromFile ( t , filename )
rc2 := readReplicationControllerFromFile ( t , filename )
return annotateRuntimeObject ( t , rc1 , rc2 , "ReplicationController" )
}
func readAndAnnotateService ( t * testing . T , filename string ) ( string , [ ] byte ) {
svc1 := readServiceFromFile ( t , filename )
svc2 := readServiceFromFile ( t , filename )
return annotateRuntimeObject ( t , svc1 , svc2 , "Service" )
}
2017-02-08 19:04:18 +00:00
func readAndAnnotateUnstructured ( t * testing . T , filename string ) ( string , [ ] byte ) {
obj1 := readUnstructuredFromFile ( t , filename )
obj2 := readUnstructuredFromFile ( t , filename )
return annotateRuntimeObject ( t , obj1 , obj2 , "Widget" )
}
2015-09-10 21:32:57 +00:00
func validatePatchApplication ( t * testing . T , req * http . Request ) {
patch , err := ioutil . ReadAll ( req . Body )
if err != nil {
t . Fatal ( err )
}
patchMap := map [ string ] interface { } { }
if err := json . Unmarshal ( patch , & patchMap ) ; err != nil {
t . Fatal ( err )
}
annotationsMap := walkMapPath ( t , patchMap , [ ] string { "metadata" , "annotations" } )
2018-08-03 11:51:44 +00:00
if _ , ok := annotationsMap [ corev1 . LastAppliedConfigAnnotation ] ; ! ok {
2015-09-10 21:32:57 +00:00
t . Fatalf ( "patch does not contain annotation:\n%s\n" , patch )
}
labelMap := walkMapPath ( t , patchMap , [ ] string { "metadata" , "labels" } )
if deleteMe , ok := labelMap [ "DELETE_ME" ] ; ! ok || deleteMe != nil {
t . Fatalf ( "patch does not remove deleted key: DELETE_ME:\n%s\n" , patch )
}
}
func walkMapPath ( t * testing . T , start map [ string ] interface { } , path [ ] string ) map [ string ] interface { } {
finish := start
for i := 0 ; i < len ( path ) ; i ++ {
var ok bool
finish , ok = finish [ path [ i ] ] . ( map [ string ] interface { } )
if ! ok {
t . Fatalf ( "key:%s of path:%v not found in map:%v" , path [ i ] , path , start )
}
}
return finish
}
2018-04-16 21:34:05 +00:00
func TestRunApplyPrintsValidObjectList ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2018-10-03 14:32:33 +00:00
configMapList := readConfigMapList ( t , filenameCM )
2018-04-16 21:34:05 +00:00
pathCM := "/namespaces/test/configmaps"
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-04-16 21:34:05 +00:00
defer tf . Cleanup ( )
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-04-16 21:34:05 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
2018-10-03 14:32:33 +00:00
case strings . HasPrefix ( p , pathCM ) && m == "GET" :
fallthrough
case strings . HasPrefix ( p , pathCM ) && m == "PATCH" :
var body io . ReadCloser
switch p {
case pathCM + "/test0" :
body = ioutil . NopCloser ( bytes . NewReader ( configMapList [ 0 ] ) )
case pathCM + "/test1" :
body = ioutil . NopCloser ( bytes . NewReader ( configMapList [ 1 ] ) )
default :
t . Errorf ( "unexpected request to %s" , p )
}
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : body } , nil
2018-04-16 21:34:05 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
}
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2018-04-16 21:34:05 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , _ := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-04-16 21:34:05 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameCM )
cmd . Flags ( ) . Set ( "output" , "json" )
cmd . Flags ( ) . Set ( "dry-run" , "true" )
cmd . Run ( cmd , [ ] string { } )
// ensure that returned list can be unmarshaled back into a configmap list
cmList := corev1 . List { }
2018-10-02 18:58:57 +00:00
if err := runtime . DecodeInto ( codec , buf . Bytes ( ) , & cmList ) ; err != nil {
2018-04-16 21:34:05 +00:00
t . Fatal ( err )
}
2018-10-03 14:32:33 +00:00
if len ( cmList . Items ) != 2 {
t . Fatalf ( "Expected 2 items in the result; got %d" , len ( cmList . Items ) )
}
if ! strings . Contains ( string ( cmList . Items [ 0 ] . Raw ) , "key1" ) {
t . Fatalf ( "Did not get first ConfigMap at the first position" )
}
if ! strings . Contains ( string ( cmList . Items [ 1 ] . Raw ) , "key2" ) {
t . Fatalf ( "Did not get second ConfigMap at the second position" )
}
2018-04-16 21:34:05 +00:00
}
2017-02-08 17:42:31 +00:00
func TestRunApplyViewLastApplied ( t * testing . T ) {
_ , rcBytesWithConfig := readReplicationController ( t , filenameRCLASTAPPLIED )
2017-10-30 21:30:44 +00:00
_ , rcBytesWithArgs := readReplicationController ( t , filenameRCLastAppliedArgs )
2017-02-08 17:42:31 +00:00
nameRC , rcBytes := readReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
tests := [ ] struct {
name , nameRC , pathRC , filePath , outputFormat , expectedErr , expectedOut , selector string
args [ ] string
respBytes [ ] byte
} {
{
name : "view with file" ,
filePath : filenameRC ,
outputFormat : "" ,
expectedErr : "" ,
expectedOut : "test: 1234\n" ,
selector : "" ,
args : [ ] string { } ,
respBytes : rcBytesWithConfig ,
} ,
2017-10-30 21:30:44 +00:00
{
name : "test with file include `%s` in arguments" ,
filePath : filenameRCArgs ,
outputFormat : "" ,
expectedErr : "" ,
expectedOut : "args: -random_flag=%s@domain.com\n" ,
selector : "" ,
args : [ ] string { } ,
respBytes : rcBytesWithArgs ,
} ,
2017-02-08 17:42:31 +00:00
{
name : "view with file json format" ,
filePath : filenameRC ,
outputFormat : "json" ,
expectedErr : "" ,
expectedOut : "{\n \"test\": 1234\n}\n" ,
selector : "" ,
args : [ ] string { } ,
respBytes : rcBytesWithConfig ,
} ,
{
name : "view resource/name invalid format" ,
filePath : "" ,
outputFormat : "wide" ,
2018-10-30 10:35:24 +00:00
expectedErr : "error: Unexpected -o output mode: wide, the flag 'output' must be one of yaml|json\nSee 'view-last-applied -h' for help and examples" ,
2017-02-08 17:42:31 +00:00
expectedOut : "" ,
selector : "" ,
2018-05-07 19:25:40 +00:00
args : [ ] string { "replicationcontroller" , "test-rc" } ,
2017-02-08 17:42:31 +00:00
respBytes : rcBytesWithConfig ,
} ,
{
name : "view resource with label" ,
filePath : "" ,
outputFormat : "" ,
expectedErr : "" ,
expectedOut : "test: 1234\n" ,
selector : "name=test-rc" ,
2018-05-07 19:25:40 +00:00
args : [ ] string { "replicationcontroller" } ,
2017-02-08 17:42:31 +00:00
respBytes : rcBytesWithConfig ,
} ,
{
name : "view resource without annotations" ,
filePath : "" ,
outputFormat : "" ,
expectedErr : "error: no last-applied-configuration annotation found on resource: test-rc" ,
expectedOut : "" ,
selector : "" ,
2018-05-07 19:25:40 +00:00
args : [ ] string { "replicationcontroller" , "test-rc" } ,
2017-02-08 17:42:31 +00:00
respBytes : rcBytes ,
} ,
{
name : "view resource no match" ,
filePath : "" ,
outputFormat : "" ,
expectedErr : "Error from server (NotFound): the server could not find the requested resource (get replicationcontrollers no-match)" ,
expectedOut : "" ,
selector : "" ,
2018-05-07 19:25:40 +00:00
args : [ ] string { "replicationcontroller" , "no-match" } ,
2017-02-08 17:42:31 +00:00
respBytes : nil ,
} ,
}
for _ , test := range tests {
2018-03-08 22:23:55 +00:00
t . Run ( test . name , func ( t * testing . T ) {
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
2017-02-08 17:42:31 +00:00
2018-03-08 22:23:55 +00:00
tf . UnstructuredClient = & fake . RESTClient {
GroupVersion : schema . GroupVersion { Version : "v1" } ,
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == pathRC && m == "GET" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( test . respBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case p == "/namespaces/test/replicationcontrollers" && m == "GET" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( test . respBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case p == "/namespaces/test/replicationcontrollers/no-match" && m == "GET" :
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 404 , Header : cmdtesting . DefaultHeader ( ) , Body : cmdtesting . ObjBody ( codec , & corev1 . Pod { } ) } , nil
2018-03-08 22:23:55 +00:00
case p == "/api/v1/namespaces/test" && m == "GET" :
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : cmdtesting . ObjBody ( codec , & corev1 . Namespace { } ) } , nil
2018-03-08 22:23:55 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
2017-02-08 17:42:31 +00:00
}
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2017-02-08 17:42:31 +00:00
2018-03-08 22:23:55 +00:00
cmdutil . BehaviorOnFatal ( func ( str string , code int ) {
if str != test . expectedErr {
t . Errorf ( "%s: unexpected error: %s\nexpected: %s" , test . name , str , test . expectedErr )
}
} )
2017-02-08 17:42:31 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , _ := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApplyViewLastApplied ( tf , ioStreams )
2018-03-08 22:23:55 +00:00
if test . filePath != "" {
cmd . Flags ( ) . Set ( "filename" , test . filePath )
}
if test . outputFormat != "" {
cmd . Flags ( ) . Set ( "output" , test . outputFormat )
}
if test . selector != "" {
cmd . Flags ( ) . Set ( "selector" , test . selector )
}
cmd . Run ( cmd , test . args )
if buf . String ( ) != test . expectedOut {
t . Fatalf ( "%s: unexpected output: %s\nexpected: %s" , test . name , buf . String ( ) , test . expectedOut )
}
} )
2017-02-08 17:42:31 +00:00
}
}
2016-11-11 22:38:36 +00:00
func TestApplyObjectWithoutAnnotation ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2016-11-11 22:38:36 +00:00
nameRC , rcBytes := readReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
2017-01-22 18:59:48 +00:00
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2016-11-11 22:38:36 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == pathRC && m == "GET" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( rcBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2016-11-11 22:38:36 +00:00
case p == pathRC && m == "PATCH" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( rcBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2016-11-11 22:38:36 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
}
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2016-11-11 22:38:36 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2016-11-11 22:38:36 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
// uses the name from the file, not the response
2018-02-21 01:14:21 +00:00
expectRC := "replicationcontroller/" + nameRC + "\n"
2017-07-13 18:38:38 +00:00
expectWarning := fmt . Sprintf ( warningNoLastAppliedConfigAnnotation , "kubectl" )
2016-11-11 22:38:36 +00:00
if errBuf . String ( ) != expectWarning {
t . Fatalf ( "unexpected non-warning: %s\nexpected: %s" , errBuf . String ( ) , expectWarning )
}
if buf . String ( ) != expectRC {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expectRC )
}
}
2015-09-10 21:32:57 +00:00
func TestApplyObject ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2015-09-10 21:32:57 +00:00
nameRC , currentRC := readAndAnnotateReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
2017-11-21 18:21:55 +00:00
for _ , fn := range testingOpenAPISchemaFns {
2018-03-08 22:23:55 +00:00
t . Run ( "test apply when a local object is specified" , func ( t * testing . T ) {
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == pathRC && m == "GET" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case p == pathRC && m == "PATCH" :
validatePatchApplication ( t , req )
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
}
tf . OpenAPISchemaFunc = fn
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2018-03-08 22:23:55 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-03-08 22:23:55 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
// uses the name from the file, not the response
expectRC := "replicationcontroller/" + nameRC + "\n"
if buf . String ( ) != expectRC {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expectRC )
}
if errBuf . String ( ) != "" {
t . Fatalf ( "unexpected error output: %s" , errBuf . String ( ) )
}
} )
2015-09-10 21:32:57 +00:00
}
}
2017-02-19 02:32:43 +00:00
func TestApplyObjectOutput ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2017-02-19 02:32:43 +00:00
nameRC , currentRC := readAndAnnotateReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
// Add some extra data to the post-patch object
postPatchObj := & unstructured . Unstructured { }
if err := json . Unmarshal ( currentRC , & postPatchObj . Object ) ; err != nil {
t . Fatal ( err )
}
postPatchLabels := postPatchObj . GetLabels ( )
if postPatchLabels == nil {
postPatchLabels = map [ string ] string { }
}
postPatchLabels [ "post-patch" ] = "value"
postPatchObj . SetLabels ( postPatchLabels )
postPatchData , err := json . Marshal ( postPatchObj )
if err != nil {
t . Fatal ( err )
}
2017-11-21 18:21:55 +00:00
for _ , fn := range testingOpenAPISchemaFns {
2018-03-08 22:23:55 +00:00
t . Run ( "test apply returns correct output" , func ( t * testing . T ) {
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == pathRC && m == "GET" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case p == pathRC && m == "PATCH" :
validatePatchApplication ( t , req )
bodyRC := ioutil . NopCloser ( bytes . NewReader ( postPatchData ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
}
tf . OpenAPISchemaFunc = fn
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2018-03-08 22:23:55 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-03-08 22:23:55 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "output" , "yaml" )
cmd . Run ( cmd , [ ] string { } )
if ! strings . Contains ( buf . String ( ) , "test-rc" ) {
t . Fatalf ( "unexpected output: %s\nexpected to contain: %s" , buf . String ( ) , "test-rc" )
}
if ! strings . Contains ( buf . String ( ) , "post-patch: value" ) {
t . Fatalf ( "unexpected output: %s\nexpected to contain: %s" , buf . String ( ) , "post-patch: value" )
}
if errBuf . String ( ) != "" {
t . Fatalf ( "unexpected error output: %s" , errBuf . String ( ) )
}
} )
2015-09-10 21:32:57 +00:00
}
}
2016-06-09 04:14:17 +00:00
func TestApplyRetry ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2016-06-09 04:14:17 +00:00
nameRC , currentRC := readAndAnnotateReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
2017-11-21 18:21:55 +00:00
for _ , fn := range testingOpenAPISchemaFns {
2018-03-08 22:23:55 +00:00
t . Run ( "test apply retries on conflict error" , func ( t * testing . T ) {
firstPatch := true
retry := false
getCount := 0
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == pathRC && m == "GET" :
getCount ++
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case p == pathRC && m == "PATCH" :
if firstPatch {
firstPatch = false
2018-10-30 10:35:24 +00:00
statusErr := kubeerr . NewConflict ( schema . GroupResource { Group : "" , Resource : "rc" } , "test-rc" , fmt . Errorf ( "the object has been modified. Please apply at first" ) )
2018-03-08 22:23:55 +00:00
bodyBytes , _ := json . Marshal ( statusErr )
bodyErr := ioutil . NopCloser ( bytes . NewReader ( bodyBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : http . StatusConflict , Header : cmdtesting . DefaultHeader ( ) , Body : bodyErr } , nil
2018-03-08 22:23:55 +00:00
}
retry = true
validatePatchApplication ( t , req )
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
2017-11-21 18:21:55 +00:00
}
2018-03-08 22:23:55 +00:00
} ) ,
}
tf . OpenAPISchemaFunc = fn
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2016-06-09 04:14:17 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-03-08 22:23:55 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
if ! retry || getCount != 2 {
t . Fatalf ( "apply didn't retry when get conflict error" )
}
// uses the name from the file, not the response
expectRC := "replicationcontroller/" + nameRC + "\n"
if buf . String ( ) != expectRC {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expectRC )
}
if errBuf . String ( ) != "" {
t . Fatalf ( "unexpected error output: %s" , errBuf . String ( ) )
}
} )
2016-06-09 04:14:17 +00:00
}
}
2015-11-05 02:29:56 +00:00
func TestApplyNonExistObject ( t * testing . T ) {
nameRC , currentRC := readAndAnnotateReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers"
pathNameRC := pathRC + "/" + nameRC
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
2017-01-22 18:59:48 +00:00
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2015-11-11 19:54:58 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
2015-11-05 02:29:56 +00:00
switch p , m := req . URL . Path , req . Method ; {
2016-08-30 03:57:59 +00:00
case p == "/api/v1/namespaces/test" && m == "GET" :
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 404 , Header : cmdtesting . DefaultHeader ( ) , Body : ioutil . NopCloser ( bytes . NewReader ( nil ) ) } , nil
2015-11-05 02:29:56 +00:00
case p == pathNameRC && m == "GET" :
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 404 , Header : cmdtesting . DefaultHeader ( ) , Body : ioutil . NopCloser ( bytes . NewReader ( nil ) ) } , nil
2015-11-05 02:29:56 +00:00
case p == pathRC && m == "POST" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 201 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2015-11-05 02:29:56 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
}
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2015-11-05 02:29:56 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , _ := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2015-11-05 02:29:56 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
// uses the name from the file, not the response
2018-02-21 01:14:21 +00:00
expectRC := "replicationcontroller/" + nameRC + "\n"
2015-11-05 02:29:56 +00:00
if buf . String ( ) != expectRC {
t . Errorf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expectRC )
}
}
2017-10-17 07:13:56 +00:00
func TestApplyEmptyPatch ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2017-10-17 07:13:56 +00:00
nameRC , _ := readAndAnnotateReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers"
pathNameRC := pathRC + "/" + nameRC
verifyPost := false
var body [ ] byte
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
2017-10-17 07:13:56 +00:00
tf . UnstructuredClient = & fake . RESTClient {
GroupVersion : schema . GroupVersion { Version : "v1" } ,
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2017-10-17 07:13:56 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == "/api/v1/namespaces/test" && m == "GET" :
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 404 , Header : cmdtesting . DefaultHeader ( ) , Body : ioutil . NopCloser ( bytes . NewReader ( nil ) ) } , nil
2017-10-17 07:13:56 +00:00
case p == pathNameRC && m == "GET" :
if body == nil {
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 404 , Header : cmdtesting . DefaultHeader ( ) , Body : ioutil . NopCloser ( bytes . NewReader ( nil ) ) } , nil
2017-10-17 07:13:56 +00:00
}
bodyRC := ioutil . NopCloser ( bytes . NewReader ( body ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2017-10-17 07:13:56 +00:00
case p == pathRC && m == "POST" :
body , _ = ioutil . ReadAll ( req . Body )
verifyPost = true
bodyRC := ioutil . NopCloser ( bytes . NewReader ( body ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 201 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2017-10-17 07:13:56 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
}
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2017-10-17 07:13:56 +00:00
// 1. apply non exist object
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , _ := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2017-10-17 07:13:56 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
2018-02-21 01:14:21 +00:00
expectRC := "replicationcontroller/" + nameRC + "\n"
2017-10-17 07:13:56 +00:00
if buf . String ( ) != expectRC {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expectRC )
}
if ! verifyPost {
t . Fatal ( "No server-side post call detected" )
}
// 2. test apply already exist object, will not send empty patch request
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , _ = genericclioptions . NewTestIOStreams ( )
cmd = NewCmdApply ( "kubectl" , tf , ioStreams )
2017-10-17 07:13:56 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
if buf . String ( ) != expectRC {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expectRC )
}
}
2016-02-10 00:07:02 +00:00
func TestApplyMultipleObjectsAsList ( t * testing . T ) {
testApplyMultipleObjects ( t , true )
}
func TestApplyMultipleObjectsAsFiles ( t * testing . T ) {
testApplyMultipleObjects ( t , false )
}
func testApplyMultipleObjects ( t * testing . T , asList bool ) {
2015-09-10 21:32:57 +00:00
nameRC , currentRC := readAndAnnotateReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
nameSVC , currentSVC := readAndAnnotateService ( t , filenameSVC )
pathSVC := "/namespaces/test/services/" + nameSVC
2017-11-21 18:21:55 +00:00
for _ , fn := range testingOpenAPISchemaFns {
2018-03-08 22:23:55 +00:00
t . Run ( "test apply on multiple objects" , func ( t * testing . T ) {
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == pathRC && m == "GET" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case p == pathRC && m == "PATCH" :
validatePatchApplication ( t , req )
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case p == pathSVC && m == "GET" :
bodySVC := ioutil . NopCloser ( bytes . NewReader ( currentSVC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodySVC } , nil
2018-03-08 22:23:55 +00:00
case p == pathSVC && m == "PATCH" :
validatePatchApplication ( t , req )
bodySVC := ioutil . NopCloser ( bytes . NewReader ( currentSVC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodySVC } , nil
2018-03-08 22:23:55 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
}
tf . OpenAPISchemaFunc = fn
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2018-03-08 22:23:55 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-03-08 22:23:55 +00:00
if asList {
cmd . Flags ( ) . Set ( "filename" , filenameRCSVC )
} else {
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "filename" , filenameSVC )
}
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
// Names should come from the REST response, NOT the files
expectRC := "replicationcontroller/" + nameRC + "\n"
expectSVC := "service/" + nameSVC + "\n"
// Test both possible orders since output is non-deterministic.
expectOne := expectRC + expectSVC
expectTwo := expectSVC + expectRC
if buf . String ( ) != expectOne && buf . String ( ) != expectTwo {
t . Fatalf ( "unexpected output: %s\nexpected: %s OR %s" , buf . String ( ) , expectOne , expectTwo )
}
if errBuf . String ( ) != "" {
t . Fatalf ( "unexpected error output: %s" , errBuf . String ( ) )
}
} )
2015-09-10 21:32:57 +00:00
}
}
2016-10-25 04:34:28 +00:00
const (
2018-10-05 12:38:38 +00:00
filenameDeployObjServerside = "../../../../test/fixtures/pkg/kubectl/cmd/apply/deploy-serverside.yaml"
filenameDeployObjClientside = "../../../../test/fixtures/pkg/kubectl/cmd/apply/deploy-clientside.yaml"
2016-10-25 04:34:28 +00:00
)
2017-01-23 22:17:15 +00:00
func readDeploymentFromFile ( t * testing . T , file string ) [ ] byte {
raw := readBytesFromFile ( t , file )
2018-12-19 16:18:53 +00:00
obj := & appsv1 . Deployment { }
2018-09-27 22:12:04 +00:00
if err := runtime . DecodeInto ( codec , raw , obj ) ; err != nil {
2016-10-25 04:34:28 +00:00
t . Fatal ( err )
}
2018-09-27 22:12:04 +00:00
objJSON , err := runtime . Encode ( codec , obj )
2016-10-25 04:34:28 +00:00
if err != nil {
t . Fatal ( err )
}
2017-01-23 22:17:15 +00:00
return objJSON
2016-10-25 04:34:28 +00:00
}
func TestApplyNULLPreservation ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2016-10-25 04:34:28 +00:00
deploymentName := "nginx-deployment"
deploymentPath := "/namespaces/test/deployments/" + deploymentName
2017-01-23 22:17:15 +00:00
verifiedPatch := false
deploymentBytes := readDeploymentFromFile ( t , filenameDeployObjServerside )
2017-11-21 18:21:55 +00:00
for _ , fn := range testingOpenAPISchemaFns {
2018-03-08 22:23:55 +00:00
t . Run ( "test apply preserves NULL fields" , func ( t * testing . T ) {
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
2017-01-23 22:17:15 +00:00
2018-03-08 22:23:55 +00:00
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == deploymentPath && m == "GET" :
body := ioutil . NopCloser ( bytes . NewReader ( deploymentBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : body } , nil
2018-03-08 22:23:55 +00:00
case p == deploymentPath && m == "PATCH" :
patch , err := ioutil . ReadAll ( req . Body )
if err != nil {
t . Fatal ( err )
}
patchMap := map [ string ] interface { } { }
if err := json . Unmarshal ( patch , & patchMap ) ; err != nil {
t . Fatal ( err )
}
annotationMap := walkMapPath ( t , patchMap , [ ] string { "metadata" , "annotations" } )
2018-08-03 11:51:44 +00:00
if _ , ok := annotationMap [ corev1 . LastAppliedConfigAnnotation ] ; ! ok {
2018-03-08 22:23:55 +00:00
t . Fatalf ( "patch does not contain annotation:\n%s\n" , patch )
}
strategy := walkMapPath ( t , patchMap , [ ] string { "spec" , "strategy" } )
if value , ok := strategy [ "rollingUpdate" ] ; ! ok || value != nil {
t . Fatalf ( "patch did not retain null value in key: rollingUpdate:\n%s\n" , patch )
}
verifiedPatch = true
// The real API server would had returned the patched object but Kubectl
// is ignoring the actual return object.
// TODO: Make this match actual server behavior by returning the patched object.
body := ioutil . NopCloser ( bytes . NewReader ( deploymentBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : body } , nil
2018-03-08 22:23:55 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
2017-11-21 18:21:55 +00:00
}
2018-03-08 22:23:55 +00:00
} ) ,
}
tf . OpenAPISchemaFunc = fn
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2016-10-25 04:34:28 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-03-08 22:23:55 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameDeployObjClientside )
cmd . Flags ( ) . Set ( "output" , "name" )
2016-10-25 04:34:28 +00:00
2018-03-08 22:23:55 +00:00
cmd . Run ( cmd , [ ] string { } )
2016-10-25 04:34:28 +00:00
2018-12-19 16:18:53 +00:00
expected := "deployment.apps/" + deploymentName + "\n"
2018-03-08 22:23:55 +00:00
if buf . String ( ) != expected {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expected )
}
if errBuf . String ( ) != "" {
t . Fatalf ( "unexpected error output: %s" , errBuf . String ( ) )
}
if ! verifiedPatch {
t . Fatal ( "No server-side patch call detected" )
}
} )
2017-02-08 19:04:18 +00:00
}
}
// TestUnstructuredApply checks apply operations on an unstructured object
func TestUnstructuredApply ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2017-02-08 19:04:18 +00:00
name , curr := readAndAnnotateUnstructured ( t , filenameWidgetClientside )
path := "/namespaces/test/widgets/" + name
verifiedPatch := false
2017-01-23 22:17:15 +00:00
2017-11-21 18:21:55 +00:00
for _ , fn := range testingOpenAPISchemaFns {
2018-03-08 22:23:55 +00:00
t . Run ( "test apply works correctly with unstructured objects" , func ( t * testing . T ) {
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == path && m == "GET" :
body := ioutil . NopCloser ( bytes . NewReader ( curr ) )
return & http . Response {
StatusCode : 200 ,
2018-10-05 12:38:38 +00:00
Header : cmdtesting . DefaultHeader ( ) ,
2018-03-08 22:23:55 +00:00
Body : body } , nil
case p == path && m == "PATCH" :
contentType := req . Header . Get ( "Content-Type" )
if contentType != "application/merge-patch+json" {
t . Fatalf ( "Unexpected Content-Type: %s" , contentType )
}
validatePatchApplication ( t , req )
verifiedPatch = true
body := ioutil . NopCloser ( bytes . NewReader ( curr ) )
return & http . Response {
StatusCode : 200 ,
2018-10-05 12:38:38 +00:00
Header : cmdtesting . DefaultHeader ( ) ,
2018-03-08 22:23:55 +00:00
Body : body } , nil
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
2017-11-21 18:21:55 +00:00
}
2018-03-08 22:23:55 +00:00
} ) ,
}
tf . OpenAPISchemaFunc = fn
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2018-03-08 22:23:55 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-03-08 22:23:55 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameWidgetClientside )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
expected := "widget.unit-test.test.com/" + name + "\n"
if buf . String ( ) != expected {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expected )
}
if errBuf . String ( ) != "" {
t . Fatalf ( "unexpected error output: %s" , errBuf . String ( ) )
}
if ! verifiedPatch {
t . Fatal ( "No server-side patch call detected" )
}
} )
2017-02-08 19:04:18 +00:00
}
}
// TestUnstructuredIdempotentApply checks repeated apply operation on an unstructured object
func TestUnstructuredIdempotentApply ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2017-02-08 19:04:18 +00:00
serversideObject := readUnstructuredFromFile ( t , filenameWidgetServerside )
2018-10-02 18:58:57 +00:00
serversideData , err := runtime . Encode ( unstructured . JSONFallbackEncoder { Encoder : codec } , serversideObject )
2017-02-08 19:04:18 +00:00
if err != nil {
t . Fatal ( err )
}
path := "/namespaces/test/widgets/widget"
2017-11-21 18:21:55 +00:00
for _ , fn := range testingOpenAPISchemaFns {
2018-03-08 22:23:55 +00:00
t . Run ( "test repeated apply operations on an unstructured object" , func ( t * testing . T ) {
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
2017-02-08 19:04:18 +00:00
2018-03-08 22:23:55 +00:00
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == path && m == "GET" :
body := ioutil . NopCloser ( bytes . NewReader ( serversideData ) )
return & http . Response {
StatusCode : 200 ,
2018-10-05 12:38:38 +00:00
Header : cmdtesting . DefaultHeader ( ) ,
2018-03-08 22:23:55 +00:00
Body : body } , nil
case p == path && m == "PATCH" :
2018-01-25 04:29:47 +00:00
// In idempotent updates, kubectl will resolve to an empty patch and not send anything to the server
// Thus, if we reach this branch, kubectl is unnecessarily sending a patch.
2018-03-08 22:23:55 +00:00
patch , err := ioutil . ReadAll ( req . Body )
if err != nil {
t . Fatal ( err )
}
2018-01-25 04:29:47 +00:00
t . Fatalf ( "Unexpected Patch: %s" , patch )
return nil , nil
2018-03-08 22:23:55 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
2017-11-21 18:21:55 +00:00
}
2018-03-08 22:23:55 +00:00
} ) ,
}
tf . OpenAPISchemaFunc = fn
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2017-02-08 19:04:18 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-03-08 22:23:55 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameWidgetClientside )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Run ( cmd , [ ] string { } )
2017-02-08 19:04:18 +00:00
2018-03-08 22:23:55 +00:00
expected := "widget.unit-test.test.com/widget\n"
if buf . String ( ) != expected {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expected )
}
if errBuf . String ( ) != "" {
t . Fatalf ( "unexpected error output: %s" , errBuf . String ( ) )
}
} )
2017-01-23 22:17:15 +00:00
}
2016-10-25 04:34:28 +00:00
}
2017-02-16 08:21:19 +00:00
func TestRunApplySetLastApplied ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2017-02-16 08:21:19 +00:00
nameRC , currentRC := readAndAnnotateReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
noExistRC , _ := readAndAnnotateReplicationController ( t , filenameNoExistRC )
noExistPath := "/namespaces/test/replicationcontrollers/" + noExistRC
noAnnotationName , noAnnotationRC := readReplicationController ( t , filenameRCNoAnnotation )
noAnnotationPath := "/namespaces/test/replicationcontrollers/" + noAnnotationName
tests := [ ] struct {
name , nameRC , pathRC , filePath , expectedErr , expectedOut , output string
} {
{
name : "set with exist object" ,
filePath : filenameRC ,
expectedErr : "" ,
2018-05-01 19:48:46 +00:00
expectedOut : "replicationcontroller/test-rc\n" ,
2017-02-16 08:21:19 +00:00
output : "name" ,
} ,
{
name : "set with no-exist object" ,
filePath : filenameNoExistRC ,
expectedErr : "Error from server (NotFound): the server could not find the requested resource (get replicationcontrollers no-exist)" ,
expectedOut : "" ,
output : "name" ,
} ,
{
name : "set for the annotation does not exist on the live object" ,
filePath : filenameRCNoAnnotation ,
2018-04-26 13:30:21 +00:00
expectedErr : "error: no last-applied-configuration annotation found on resource: no-annotation, to create the annotation, run the command with --create-annotation" ,
2017-02-16 08:21:19 +00:00
expectedOut : "" ,
output : "name" ,
} ,
{
name : "set with exist object output json" ,
filePath : filenameRCJSON ,
expectedErr : "" ,
2018-05-01 19:48:46 +00:00
expectedOut : "replicationcontroller/test-rc\n" ,
2017-02-16 08:21:19 +00:00
output : "name" ,
} ,
{
name : "set test for a directory of files" ,
filePath : dirName ,
expectedErr : "" ,
2018-05-01 19:48:46 +00:00
expectedOut : "replicationcontroller/test-rc\nreplicationcontroller/test-rc\n" ,
2017-02-16 08:21:19 +00:00
output : "name" ,
} ,
}
for _ , test := range tests {
2017-11-14 04:01:51 +00:00
t . Run ( test . name , func ( t * testing . T ) {
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
2017-11-14 04:01:51 +00:00
tf . UnstructuredClient = & fake . RESTClient {
GroupVersion : schema . GroupVersion { Version : "v1" } ,
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2017-11-14 04:01:51 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case p == pathRC && m == "GET" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2017-11-14 04:01:51 +00:00
case p == noAnnotationPath && m == "GET" :
bodyRC := ioutil . NopCloser ( bytes . NewReader ( noAnnotationRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2017-11-14 04:01:51 +00:00
case p == noExistPath && m == "GET" :
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 404 , Header : cmdtesting . DefaultHeader ( ) , Body : cmdtesting . ObjBody ( codec , & corev1 . Pod { } ) } , nil
2017-11-14 04:01:51 +00:00
case p == pathRC && m == "PATCH" :
checkPatchString ( t , req )
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2017-11-14 04:01:51 +00:00
case p == "/api/v1/namespaces/test" && m == "GET" :
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : cmdtesting . ObjBody ( codec , & corev1 . Namespace { } ) } , nil
2017-11-14 04:01:51 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
}
} ) ,
}
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2017-11-14 04:01:51 +00:00
cmdutil . BehaviorOnFatal ( func ( str string , code int ) {
if str != test . expectedErr {
t . Errorf ( "%s: unexpected error: %s\nexpected: %s" , test . name , str , test . expectedErr )
2017-02-16 08:21:19 +00:00
}
2017-11-14 04:01:51 +00:00
} )
2017-02-16 08:21:19 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , _ := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApplySetLastApplied ( tf , ioStreams )
2017-11-14 04:01:51 +00:00
cmd . Flags ( ) . Set ( "filename" , test . filePath )
cmd . Flags ( ) . Set ( "output" , test . output )
cmd . Run ( cmd , [ ] string { } )
if buf . String ( ) != test . expectedOut {
t . Fatalf ( "%s: unexpected output: %s\nexpected: %s" , test . name , buf . String ( ) , test . expectedOut )
2017-02-16 08:21:19 +00:00
}
} )
}
2017-05-24 22:31:34 +00:00
cmdutil . BehaviorOnFatal ( func ( str string , code int ) { } )
2017-02-16 08:21:19 +00:00
}
func checkPatchString ( t * testing . T , req * http . Request ) {
checkString := string ( readBytesFromFile ( t , filenameRCPatchTest ) )
patch , err := ioutil . ReadAll ( req . Body )
if err != nil {
t . Fatal ( err )
}
patchMap := map [ string ] interface { } { }
if err := json . Unmarshal ( patch , & patchMap ) ; err != nil {
t . Fatal ( err )
}
annotationsMap := walkMapPath ( t , patchMap , [ ] string { "metadata" , "annotations" } )
2018-08-03 11:51:44 +00:00
if _ , ok := annotationsMap [ corev1 . LastAppliedConfigAnnotation ] ; ! ok {
2017-02-16 08:21:19 +00:00
t . Fatalf ( "patch does not contain annotation:\n%s\n" , patch )
}
resultString := annotationsMap [ "kubectl.kubernetes.io/last-applied-configuration" ]
if resultString != checkString {
t . Fatalf ( "patch annotation is not correct, expect:%s\n but got:%s\n" , checkString , resultString )
}
}
2017-07-05 01:30:36 +00:00
func TestForceApply ( t * testing . T ) {
2018-10-05 12:38:38 +00:00
cmdtesting . InitTestErrorHandler ( t )
2018-05-17 15:27:44 +00:00
scheme := runtime . NewScheme ( )
2017-07-05 01:30:36 +00:00
nameRC , currentRC := readAndAnnotateReplicationController ( t , filenameRC )
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
pathRCList := "/namespaces/test/replicationcontrollers"
expected := map [ string ] int {
2018-05-17 15:27:44 +00:00
"getOk" : 6 ,
2017-07-05 01:30:36 +00:00
"getNotFound" : 1 ,
2018-05-17 15:27:44 +00:00
"getList" : 0 ,
2017-07-05 01:30:36 +00:00
"patch" : 6 ,
"delete" : 1 ,
"post" : 1 ,
}
2017-11-21 18:21:55 +00:00
for _ , fn := range testingOpenAPISchemaFns {
2018-03-08 22:23:55 +00:00
t . Run ( "test apply with --force" , func ( t * testing . T ) {
deleted := false
isScaledDownToZero := false
counts := map [ string ] int { }
2018-05-24 13:33:36 +00:00
tf := cmdtesting . NewTestFactory ( ) . WithNamespace ( "test" )
2018-03-08 22:23:55 +00:00
defer tf . Cleanup ( )
2018-10-05 12:38:38 +00:00
tf . ClientConfigVal = cmdtesting . DefaultClientConfig ( )
2018-03-08 22:23:55 +00:00
tf . UnstructuredClient = & fake . RESTClient {
2018-10-05 12:38:38 +00:00
NegotiatedSerializer : resource . UnstructuredPlusDefaultContentConfig ( ) . NegotiatedSerializer ,
2018-03-08 22:23:55 +00:00
Client : fake . CreateHTTPClient ( func ( req * http . Request ) ( * http . Response , error ) {
switch p , m := req . URL . Path , req . Method ; {
case strings . HasSuffix ( p , pathRC ) && m == "GET" :
if deleted {
counts [ "getNotFound" ] ++
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 404 , Header : cmdtesting . DefaultHeader ( ) , Body : ioutil . NopCloser ( bytes . NewReader ( [ ] byte { } ) ) } , nil
2018-03-08 22:23:55 +00:00
}
counts [ "getOk" ] ++
var bodyRC io . ReadCloser
if isScaledDownToZero {
rcObj := readReplicationControllerFromFile ( t , filenameRC )
2018-10-05 12:38:38 +00:00
rcObj . Spec . Replicas = cmdtesting . Int32ptr ( 0 )
2018-10-02 18:58:57 +00:00
rcBytes , err := runtime . Encode ( codec , rcObj )
2018-03-08 22:23:55 +00:00
if err != nil {
t . Fatal ( err )
}
bodyRC = ioutil . NopCloser ( bytes . NewReader ( rcBytes ) )
} else {
bodyRC = ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
}
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case strings . HasSuffix ( p , pathRCList ) && m == "GET" :
counts [ "getList" ] ++
rcObj := readUnstructuredFromFile ( t , filenameRC )
list := & unstructured . UnstructuredList {
Object : map [ string ] interface { } {
"apiVersion" : "v1" ,
"kind" : "ReplicationControllerList" ,
} ,
Items : [ ] unstructured . Unstructured { * rcObj } ,
}
2018-10-02 18:58:57 +00:00
listBytes , err := runtime . Encode ( codec , list )
2017-11-20 17:30:32 +00:00
if err != nil {
t . Fatal ( err )
}
2018-03-08 22:23:55 +00:00
bodyRCList := ioutil . NopCloser ( bytes . NewReader ( listBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRCList } , nil
2018-03-08 22:23:55 +00:00
case strings . HasSuffix ( p , pathRC ) && m == "PATCH" :
counts [ "patch" ] ++
if counts [ "patch" ] <= 6 {
2018-10-30 10:35:24 +00:00
statusErr := kubeerr . NewConflict ( schema . GroupResource { Group : "" , Resource : "rc" } , "test-rc" , fmt . Errorf ( "the object has been modified. Please apply at first" ) )
2018-03-08 22:23:55 +00:00
bodyBytes , _ := json . Marshal ( statusErr )
bodyErr := ioutil . NopCloser ( bytes . NewReader ( bodyBytes ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : http . StatusConflict , Header : cmdtesting . DefaultHeader ( ) , Body : bodyErr } , nil
2018-03-08 22:23:55 +00:00
}
t . Fatalf ( "unexpected request: %#v after %v tries\n%#v" , req . URL , counts [ "patch" ] , req )
return nil , nil
case strings . HasSuffix ( p , pathRC ) && m == "PUT" :
counts [ "put" ] ++
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
isScaledDownToZero = true
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
case strings . HasSuffix ( p , pathRCList ) && m == "POST" :
counts [ "post" ] ++
deleted = false
isScaledDownToZero = false
bodyRC := ioutil . NopCloser ( bytes . NewReader ( currentRC ) )
2018-10-05 12:38:38 +00:00
return & http . Response { StatusCode : 200 , Header : cmdtesting . DefaultHeader ( ) , Body : bodyRC } , nil
2018-03-08 22:23:55 +00:00
default :
t . Fatalf ( "unexpected request: %#v\n%#v" , req . URL , req )
return nil , nil
2017-11-21 18:21:55 +00:00
}
2018-03-08 22:23:55 +00:00
} ) ,
}
2018-05-17 15:27:44 +00:00
fakeDynamicClient := dynamicfakeclient . NewSimpleDynamicClient ( scheme )
fakeDynamicClient . PrependReactor ( "delete" , "replicationcontrollers" , func ( action clienttesting . Action ) ( bool , runtime . Object , error ) {
if deleteAction , ok := action . ( clienttesting . DeleteAction ) ; ok {
if deleteAction . GetName ( ) == nameRC {
counts [ "delete" ] ++
deleted = true
return true , nil , nil
}
2018-02-26 20:23:33 +00:00
}
2018-05-17 15:27:44 +00:00
return false , nil , nil
2018-02-26 20:23:33 +00:00
} )
2018-05-17 15:27:44 +00:00
tf . FakeDynamicClient = fakeDynamicClient
2018-03-08 22:23:55 +00:00
tf . OpenAPISchemaFunc = fn
tf . Client = tf . UnstructuredClient
tf . ClientConfigVal = & restclient . Config { }
2018-02-26 20:23:33 +00:00
2018-04-19 21:43:28 +00:00
ioStreams , _ , buf , errBuf := genericclioptions . NewTestIOStreams ( )
cmd := NewCmdApply ( "kubectl" , tf , ioStreams )
2018-03-08 22:23:55 +00:00
cmd . Flags ( ) . Set ( "filename" , filenameRC )
cmd . Flags ( ) . Set ( "output" , "name" )
cmd . Flags ( ) . Set ( "force" , "true" )
cmd . Run ( cmd , [ ] string { } )
for method , exp := range expected {
if exp != counts [ method ] {
t . Errorf ( "Unexpected amount of %q API calls, wanted %v got %v" , method , exp , counts [ method ] )
2017-07-05 01:30:36 +00:00
}
2017-11-21 18:21:55 +00:00
}
2017-07-05 01:30:36 +00:00
2018-03-08 22:23:55 +00:00
if expected := "replicationcontroller/" + nameRC + "\n" ; buf . String ( ) != expected {
t . Fatalf ( "unexpected output: %s\nexpected: %s" , buf . String ( ) , expected )
}
if errBuf . String ( ) != "" {
t . Fatalf ( "unexpected error output: %s" , errBuf . String ( ) )
}
} )
2017-07-05 01:30:36 +00:00
}
}
2018-10-05 00:10:08 +00:00
func TestDryRunVerifier ( t * testing . T ) {
dryRunVerifier := DryRunVerifier {
Finder : cmdutil . NewCRDFinder ( func ( ) ( [ ] schema . GroupKind , error ) {
return [ ] schema . GroupKind {
{
Group : "crd.com" ,
Kind : "MyCRD" ,
} ,
{
Group : "crd.com" ,
Kind : "MyNewCRD" ,
} ,
} , nil
} ) ,
OpenAPIGetter : & fakeSchema ,
}
err := dryRunVerifier . HasSupport ( schema . GroupVersionKind { Group : "" , Version : "v1" , Kind : "NodeProxyOptions" } )
if err == nil {
t . Fatalf ( "NodeProxyOptions doesn't support dry-run, yet no error found" )
}
err = dryRunVerifier . HasSupport ( schema . GroupVersionKind { Group : "" , Version : "v1" , Kind : "Pod" } )
if err != nil {
t . Fatalf ( "Pod should support dry-run: %v" , err )
}
err = dryRunVerifier . HasSupport ( schema . GroupVersionKind { Group : "crd.com" , Version : "v1" , Kind : "MyCRD" } )
if err != nil {
t . Fatalf ( "MyCRD should support dry-run: %v" , err )
}
err = dryRunVerifier . HasSupport ( schema . GroupVersionKind { Group : "crd.com" , Version : "v1" , Kind : "Random" } )
if err == nil {
t . Fatalf ( "Random doesn't support dry-run, yet no error found" )
}
}
type EmptyOpenAPI struct { }
func ( EmptyOpenAPI ) OpenAPISchema ( ) ( * openapi_v2 . Document , error ) {
return & openapi_v2 . Document { } , nil
}
func TestDryRunVerifierNoOpenAPI ( t * testing . T ) {
dryRunVerifier := DryRunVerifier {
Finder : cmdutil . NewCRDFinder ( func ( ) ( [ ] schema . GroupKind , error ) {
return [ ] schema . GroupKind {
{
Group : "crd.com" ,
Kind : "MyCRD" ,
} ,
{
Group : "crd.com" ,
Kind : "MyNewCRD" ,
} ,
} , nil
} ) ,
OpenAPIGetter : EmptyOpenAPI { } ,
}
err := dryRunVerifier . HasSupport ( schema . GroupVersionKind { Group : "" , Version : "v1" , Kind : "Pod" } )
if err == nil {
t . Fatalf ( "Pod doesn't support dry-run, yet no error found" )
}
err = dryRunVerifier . HasSupport ( schema . GroupVersionKind { Group : "crd.com" , Version : "v1" , Kind : "MyCRD" } )
if err == nil {
t . Fatalf ( "MyCRD doesn't support dry-run, yet no error found" )
}
}