mirror of https://github.com/k3s-io/k3s
Protect against nil panic in apply
parent
4103f40fc2
commit
183ff5237d
|
@ -313,6 +313,7 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
|
|||
decoder: decoder,
|
||||
mapping: info.Mapping,
|
||||
helper: helper,
|
||||
clientFunc: f.UnstructuredClientForMapping,
|
||||
clientsetFunc: f.ClientSet,
|
||||
overwrite: overwrite,
|
||||
backOff: clockwork.NewRealClock(),
|
||||
|
@ -496,7 +497,7 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput
|
|||
return err
|
||||
}
|
||||
if !p.dryRun {
|
||||
if err := p.delete(namespace, name, mapping, c); err != nil {
|
||||
if err := p.delete(namespace, name, mapping); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -505,7 +506,12 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput
|
|||
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)
|
||||
}
|
||||
|
||||
|
@ -538,7 +544,11 @@ func runDelete(namespace, name string, mapping *meta.RESTMapping, c resource.RES
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -547,6 +557,7 @@ type patcher struct {
|
|||
|
||||
mapping *meta.RESTMapping
|
||||
helper *resource.Helper
|
||||
clientFunc resource.ClientMapperFunc
|
||||
clientsetFunc func() (internalclientset.Interface, error)
|
||||
|
||||
overwrite bool
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue