mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Protect against nil panic in apply
This commit is contained in:
		@@ -313,6 +313,7 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
 | 
				
			|||||||
				decoder:       decoder,
 | 
									decoder:       decoder,
 | 
				
			||||||
				mapping:       info.Mapping,
 | 
									mapping:       info.Mapping,
 | 
				
			||||||
				helper:        helper,
 | 
									helper:        helper,
 | 
				
			||||||
 | 
									clientFunc:    f.UnstructuredClientForMapping,
 | 
				
			||||||
				clientsetFunc: f.ClientSet,
 | 
									clientsetFunc: f.ClientSet,
 | 
				
			||||||
				overwrite:     overwrite,
 | 
									overwrite:     overwrite,
 | 
				
			||||||
				backOff:       clockwork.NewRealClock(),
 | 
									backOff:       clockwork.NewRealClock(),
 | 
				
			||||||
@@ -496,7 +497,7 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput
 | 
				
			|||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !p.dryRun {
 | 
							if !p.dryRun {
 | 
				
			||||||
			if err := p.delete(namespace, name, mapping, c); err != nil {
 | 
								if err := p.delete(namespace, name, mapping); err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -505,7 +506,12 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping, c resource.RESTClient) error {
 | 
					func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error {
 | 
				
			||||||
 | 
						c, err := p.clientFunc(mapping)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return runDelete(namespace, name, mapping, c, nil, p.cascade, p.gracePeriod, p.clientsetFunc)
 | 
						return runDelete(namespace, name, mapping, c, nil, p.cascade, p.gracePeriod, p.clientsetFunc)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -538,7 +544,11 @@ func runDelete(namespace, name string, mapping *meta.RESTMapping, c resource.RES
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *patcher) delete(namespace, name string) error {
 | 
					func (p *patcher) delete(namespace, name string) error {
 | 
				
			||||||
	return runDelete(namespace, name, p.mapping, nil, p.helper, p.cascade, p.gracePeriod, p.clientsetFunc)
 | 
						c, err := p.clientFunc(p.mapping)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return runDelete(namespace, name, p.mapping, c, p.helper, p.cascade, p.gracePeriod, p.clientsetFunc)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type patcher struct {
 | 
					type patcher struct {
 | 
				
			||||||
@@ -547,6 +557,7 @@ type patcher struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	mapping       *meta.RESTMapping
 | 
						mapping       *meta.RESTMapping
 | 
				
			||||||
	helper        *resource.Helper
 | 
						helper        *resource.Helper
 | 
				
			||||||
 | 
						clientFunc    resource.ClientMapperFunc
 | 
				
			||||||
	clientsetFunc func() (internalclientset.Interface, error)
 | 
						clientsetFunc func() (internalclientset.Interface, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	overwrite bool
 | 
						overwrite bool
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ import (
 | 
				
			|||||||
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
						"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
						"k8s.io/apimachinery/pkg/runtime/schema"
 | 
				
			||||||
 | 
						restclient "k8s.io/client-go/rest"
 | 
				
			||||||
	"k8s.io/client-go/rest/fake"
 | 
						"k8s.io/client-go/rest/fake"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api"
 | 
						"k8s.io/kubernetes/pkg/api"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/testapi"
 | 
						"k8s.io/kubernetes/pkg/api/testapi"
 | 
				
			||||||
@@ -1012,3 +1013,103 @@ func checkPatchString(t *testing.T, req *http.Request) {
 | 
				
			|||||||
		t.Fatalf("patch annotation is not correct, expect:%s\n but got:%s\n", checkString, resultString)
 | 
							t.Fatalf("patch annotation is not correct, expect:%s\n but got:%s\n", checkString, resultString)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestForceApply(t *testing.T) {
 | 
				
			||||||
 | 
						initTestErrorHandler(t)
 | 
				
			||||||
 | 
						nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC)
 | 
				
			||||||
 | 
						pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
 | 
				
			||||||
 | 
						pathRCList := "/namespaces/test/replicationcontrollers"
 | 
				
			||||||
 | 
						deleted := false
 | 
				
			||||||
 | 
						counts := map[string]int{}
 | 
				
			||||||
 | 
						expected := map[string]int{
 | 
				
			||||||
 | 
							"getOk":       9,
 | 
				
			||||||
 | 
							"getNotFound": 1,
 | 
				
			||||||
 | 
							"getList":     1,
 | 
				
			||||||
 | 
							"patch":       6,
 | 
				
			||||||
 | 
							"delete":      1,
 | 
				
			||||||
 | 
							"put":         1,
 | 
				
			||||||
 | 
							"post":        1,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f, tf, _, _ := cmdtesting.NewAPIFactory()
 | 
				
			||||||
 | 
						tf.Printer = &testPrinter{}
 | 
				
			||||||
 | 
						tf.UnstructuredClient = &fake.RESTClient{
 | 
				
			||||||
 | 
							APIRegistry:          api.Registry,
 | 
				
			||||||
 | 
							NegotiatedSerializer: unstructuredSerializer,
 | 
				
			||||||
 | 
							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"]++
 | 
				
			||||||
 | 
										return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte{}))}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									counts["getOk"]++
 | 
				
			||||||
 | 
									bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
 | 
				
			||||||
 | 
									return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
 | 
				
			||||||
 | 
								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},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									listBytes, err := runtime.Encode(testapi.Default.Codec(), list)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										t.Fatal(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									bodyRCList := ioutil.NopCloser(bytes.NewReader(listBytes))
 | 
				
			||||||
 | 
									return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRCList}, nil
 | 
				
			||||||
 | 
								case strings.HasSuffix(p, pathRC) && m == "PATCH":
 | 
				
			||||||
 | 
									counts["patch"]++
 | 
				
			||||||
 | 
									if counts["patch"] <= 6 {
 | 
				
			||||||
 | 
										statusErr := kubeerr.NewConflict(schema.GroupResource{Group: "", Resource: "rc"}, "test-rc", fmt.Errorf("the object has been modified. Please apply at first."))
 | 
				
			||||||
 | 
										bodyBytes, _ := json.Marshal(statusErr)
 | 
				
			||||||
 | 
										bodyErr := ioutil.NopCloser(bytes.NewReader(bodyBytes))
 | 
				
			||||||
 | 
										return &http.Response{StatusCode: http.StatusConflict, Header: defaultHeader(), Body: bodyErr}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									t.Fatalf("unexpected request: %#v after %v tries\n%#v", req.URL, counts["patch"], req)
 | 
				
			||||||
 | 
									return nil, nil
 | 
				
			||||||
 | 
								case strings.HasSuffix(p, pathRC) && m == "DELETE":
 | 
				
			||||||
 | 
									counts["delete"]++
 | 
				
			||||||
 | 
									deleted = true
 | 
				
			||||||
 | 
									return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte{}))}, nil
 | 
				
			||||||
 | 
								case strings.HasSuffix(p, pathRC) && m == "PUT":
 | 
				
			||||||
 | 
									counts["put"]++
 | 
				
			||||||
 | 
									bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
 | 
				
			||||||
 | 
									return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
 | 
				
			||||||
 | 
								case strings.HasSuffix(p, pathRCList) && m == "POST":
 | 
				
			||||||
 | 
									counts["post"]++
 | 
				
			||||||
 | 
									deleted = false
 | 
				
			||||||
 | 
									bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
 | 
				
			||||||
 | 
									return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
 | 
				
			||||||
 | 
									return nil, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tf.Client = tf.UnstructuredClient
 | 
				
			||||||
 | 
						tf.ClientConfig = &restclient.Config{}
 | 
				
			||||||
 | 
						tf.Namespace = "test"
 | 
				
			||||||
 | 
						buf := bytes.NewBuffer([]byte{})
 | 
				
			||||||
 | 
						errBuf := bytes.NewBuffer([]byte{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := NewCmdApply("kubectl", f, buf, errBuf)
 | 
				
			||||||
 | 
						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])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if expected := "replicationcontroller/" + nameRC + "\n"; buf.String() != expected {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected output: %s\nexpected: %s", buf.String(), expected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user