Add support for v1 CSRs to kubectl certificate commands
Kubernetes-commit: ca234db60151ccffdd4dc8ceb2ec3c69fd83af69
This commit is contained in:
parent
17351e39d1
commit
fe32c3454c
|
@ -23,14 +23,18 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/printers"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
certificatesv1beta1client "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
|
@ -63,7 +67,7 @@ type CertificateOptions struct {
|
|||
csrNames []string
|
||||
outputStyle string
|
||||
|
||||
clientSet certificatesv1beta1client.CertificatesV1beta1Interface
|
||||
clientSet clientset.Interface
|
||||
builder *resource.Builder
|
||||
|
||||
genericclioptions.IOStreams
|
||||
|
@ -92,11 +96,7 @@ func (o *CertificateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
|
|||
|
||||
o.builder = f.NewBuilder()
|
||||
|
||||
clientConfig, err := f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.clientSet, err = certificatesv1beta1client.NewForConfig(clientConfig)
|
||||
o.clientSet, err = f.KubernetesClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -146,24 +146,12 @@ func NewCmdCertificateApprove(f cmdutil.Factory, ioStreams genericclioptions.IOS
|
|||
}
|
||||
|
||||
func (o *CertificateOptions) RunCertificateApprove(force bool) error {
|
||||
return o.modifyCertificateCondition(o.builder, o.clientSet, force, func(csr *certificatesv1beta1.CertificateSigningRequest) (*certificatesv1beta1.CertificateSigningRequest, bool) {
|
||||
var alreadyApproved bool
|
||||
for _, c := range csr.Status.Conditions {
|
||||
if c.Type == certificatesv1beta1.CertificateApproved {
|
||||
alreadyApproved = true
|
||||
}
|
||||
}
|
||||
if alreadyApproved {
|
||||
return csr, true
|
||||
}
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1beta1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1beta1.CertificateApproved,
|
||||
Reason: "KubectlApprove",
|
||||
Message: "This CSR was approved by kubectl certificate approve.",
|
||||
LastUpdateTime: metav1.Now(),
|
||||
})
|
||||
return csr, false
|
||||
})
|
||||
return o.modifyCertificateCondition(
|
||||
o.builder,
|
||||
o.clientSet,
|
||||
force,
|
||||
addConditionIfNeeded(string(certificatesv1.CertificateDenied), string(certificatesv1.CertificateApproved), "KubectlApprove", "This CSR was approved by kubectl certificate approve."),
|
||||
)
|
||||
}
|
||||
|
||||
func NewCmdCertificateDeny(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
|
@ -196,33 +184,21 @@ func NewCmdCertificateDeny(f cmdutil.Factory, ioStreams genericclioptions.IOStre
|
|||
}
|
||||
|
||||
func (o *CertificateOptions) RunCertificateDeny(force bool) error {
|
||||
return o.modifyCertificateCondition(o.builder, o.clientSet, force, func(csr *certificatesv1beta1.CertificateSigningRequest) (*certificatesv1beta1.CertificateSigningRequest, bool) {
|
||||
var alreadyDenied bool
|
||||
for _, c := range csr.Status.Conditions {
|
||||
if c.Type == certificatesv1beta1.CertificateDenied {
|
||||
alreadyDenied = true
|
||||
}
|
||||
}
|
||||
if alreadyDenied {
|
||||
return csr, true
|
||||
}
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1beta1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1beta1.CertificateDenied,
|
||||
Reason: "KubectlDeny",
|
||||
Message: "This CSR was denied by kubectl certificate deny.",
|
||||
LastUpdateTime: metav1.Now(),
|
||||
})
|
||||
return csr, false
|
||||
})
|
||||
return o.modifyCertificateCondition(
|
||||
o.builder,
|
||||
o.clientSet,
|
||||
force,
|
||||
addConditionIfNeeded(string(certificatesv1.CertificateApproved), string(certificatesv1.CertificateDenied), "KubectlDeny", "This CSR was denied by kubectl certificate deny."),
|
||||
)
|
||||
}
|
||||
|
||||
func (o *CertificateOptions) modifyCertificateCondition(builder *resource.Builder, clientSet certificatesv1beta1client.CertificatesV1beta1Interface, force bool, modify func(csr *certificatesv1beta1.CertificateSigningRequest) (*certificatesv1beta1.CertificateSigningRequest, bool)) error {
|
||||
func (o *CertificateOptions) modifyCertificateCondition(builder *resource.Builder, clientSet clientset.Interface, force bool, modify func(csr runtime.Object) (runtime.Object, bool, error)) error {
|
||||
var found int
|
||||
r := builder.
|
||||
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
|
||||
Unstructured().
|
||||
ContinueOnError().
|
||||
FilenameParam(false, &o.FilenameOptions).
|
||||
ResourceNames("certificatesigningrequests.v1beta1.certificates.k8s.io", o.csrNames...).
|
||||
ResourceNames("certificatesigningrequests", o.csrNames...).
|
||||
RequireObject(true).
|
||||
Flatten().
|
||||
Latest().
|
||||
|
@ -232,13 +208,41 @@ func (o *CertificateOptions) modifyCertificateCondition(builder *resource.Builde
|
|||
return err
|
||||
}
|
||||
for i := 0; ; i++ {
|
||||
csr, ok := info.Object.(*certificatesv1beta1.CertificateSigningRequest)
|
||||
obj, ok := info.Object.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("can only handle certificates.k8s.io/v1beta1 certificate signing requests")
|
||||
return fmt.Errorf("expected *unstructured.Unstructured, got %T", obj)
|
||||
}
|
||||
if want, got := certificatesv1.Kind("CertificateSigningRequest"), obj.GetObjectKind().GroupVersionKind().GroupKind(); want != got {
|
||||
return fmt.Errorf("can only handle %s objects, got %s", want.String(), got.String())
|
||||
}
|
||||
var csr runtime.Object
|
||||
// get a typed object
|
||||
// first try v1
|
||||
csr, err = clientSet.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), obj.GetName(), metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
// fall back to v1beta1
|
||||
csr, err = clientSet.CertificatesV1beta1().CertificateSigningRequests().Get(context.TODO(), obj.GetName(), metav1.GetOptions{})
|
||||
}
|
||||
if apierrors.IsNotFound(err) {
|
||||
return fmt.Errorf("could not find v1 or v1beta1 version of %s: %v", obj.GetName(), err)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
modifiedCSR, hasCondition, err := modify(csr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
csr, hasCondition := modify(csr)
|
||||
if !hasCondition || force {
|
||||
_, err = clientSet.CertificateSigningRequests().UpdateApproval(context.TODO(), csr, metav1.UpdateOptions{})
|
||||
switch modifiedCSR := modifiedCSR.(type) {
|
||||
case *certificatesv1.CertificateSigningRequest:
|
||||
_, err = clientSet.CertificatesV1().CertificateSigningRequests().UpdateApproval(context.TODO(), modifiedCSR.Name, modifiedCSR, metav1.UpdateOptions{})
|
||||
case *certificatesv1beta1.CertificateSigningRequest:
|
||||
_, err = clientSet.CertificatesV1beta1().CertificateSigningRequests().UpdateApproval(context.TODO(), modifiedCSR, metav1.UpdateOptions{})
|
||||
default:
|
||||
return fmt.Errorf("can only handle certificates.k8s.io CertificateSigningRequest objects, got %T", modifiedCSR)
|
||||
}
|
||||
if errors.IsConflict(err) && i < 10 {
|
||||
if err := info.Get(); err != nil {
|
||||
return err
|
||||
|
@ -255,8 +259,61 @@ func (o *CertificateOptions) modifyCertificateCondition(builder *resource.Builde
|
|||
|
||||
return o.PrintObj(info.Object, o.Out)
|
||||
})
|
||||
if found == 0 {
|
||||
if found == 0 && err == nil {
|
||||
fmt.Fprintf(o.Out, "No resources found\n")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func addConditionIfNeeded(mustNotHaveConditionType, conditionType, reason, message string) func(runtime.Object) (runtime.Object, bool, error) {
|
||||
return func(csr runtime.Object) (runtime.Object, bool, error) {
|
||||
switch csr := csr.(type) {
|
||||
case *certificatesv1.CertificateSigningRequest:
|
||||
var alreadyHasCondition bool
|
||||
for _, c := range csr.Status.Conditions {
|
||||
if string(c.Type) == mustNotHaveConditionType {
|
||||
return nil, false, fmt.Errorf("certificate signing request %q is already %s", csr.Name, c.Type)
|
||||
}
|
||||
if string(c.Type) == conditionType {
|
||||
alreadyHasCondition = true
|
||||
}
|
||||
}
|
||||
if alreadyHasCondition {
|
||||
return csr, true, nil
|
||||
}
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.RequestConditionType(conditionType),
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
LastUpdateTime: metav1.Now(),
|
||||
})
|
||||
return csr, false, nil
|
||||
|
||||
case *certificatesv1beta1.CertificateSigningRequest:
|
||||
var alreadyHasCondition bool
|
||||
for _, c := range csr.Status.Conditions {
|
||||
if string(c.Type) == mustNotHaveConditionType {
|
||||
return nil, false, fmt.Errorf("certificate signing request %q is already %s", csr.Name, c.Type)
|
||||
}
|
||||
if string(c.Type) == conditionType {
|
||||
alreadyHasCondition = true
|
||||
}
|
||||
}
|
||||
if alreadyHasCondition {
|
||||
return csr, true, nil
|
||||
}
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1beta1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1beta1.RequestConditionType(conditionType),
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
LastUpdateTime: metav1.Now(),
|
||||
})
|
||||
return csr, false, nil
|
||||
|
||||
default:
|
||||
return csr, false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
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 certificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
)
|
||||
|
||||
func TestCertificates(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
nov1 bool
|
||||
nov1beta1 bool
|
||||
command string
|
||||
force bool
|
||||
args []string
|
||||
expectFailure bool
|
||||
expectActions []string
|
||||
expectOutput string
|
||||
expectErrOutput string
|
||||
}{
|
||||
{
|
||||
name: "approve existing",
|
||||
command: "approve",
|
||||
args: []string{"existing"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/existing`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/existing`, // typed get
|
||||
`PUT /apis/certificates.k8s.io/v1/certificatesigningrequests/existing/approval`,
|
||||
},
|
||||
expectOutput: `approved`,
|
||||
},
|
||||
{
|
||||
name: "approve existing, no v1",
|
||||
nov1: true,
|
||||
command: "approve",
|
||||
args: []string{"certificatesigningrequests.v1beta1.certificates.k8s.io/existing"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1beta1/certificatesigningrequests/existing`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/existing`, // typed get, 404
|
||||
`GET /apis/certificates.k8s.io/v1beta1/certificatesigningrequests/existing`, // typed get fallback
|
||||
`PUT /apis/certificates.k8s.io/v1beta1/certificatesigningrequests/existing/approval`,
|
||||
},
|
||||
expectOutput: `approved`,
|
||||
},
|
||||
{
|
||||
name: "approve existing, no v1 or v1beta1",
|
||||
nov1: true,
|
||||
nov1beta1: true,
|
||||
command: "approve",
|
||||
args: []string{"existing"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/existing`, // unstructured get, 404
|
||||
},
|
||||
expectFailure: true,
|
||||
expectErrOutput: `could not find the requested resource`,
|
||||
},
|
||||
{
|
||||
name: "approve already approved",
|
||||
command: "approve",
|
||||
args: []string{"approved"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/approved`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/approved`, // typed get
|
||||
},
|
||||
expectOutput: `approved`,
|
||||
},
|
||||
{
|
||||
name: "approve already approved, force",
|
||||
command: "approve",
|
||||
args: []string{"approved"},
|
||||
force: true,
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/approved`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/approved`, // typed get
|
||||
`PUT /apis/certificates.k8s.io/v1/certificatesigningrequests/approved/approval`,
|
||||
},
|
||||
expectOutput: `approved`,
|
||||
},
|
||||
{
|
||||
name: "approve already denied",
|
||||
command: "approve",
|
||||
args: []string{"denied"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/denied`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/denied`, // typed get
|
||||
},
|
||||
expectFailure: true,
|
||||
expectErrOutput: `is already Denied`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "deny existing",
|
||||
command: "deny",
|
||||
args: []string{"existing"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/existing`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/existing`, // typed get
|
||||
`PUT /apis/certificates.k8s.io/v1/certificatesigningrequests/existing/approval`,
|
||||
},
|
||||
expectOutput: `denied`,
|
||||
},
|
||||
{
|
||||
name: "deny existing, no v1",
|
||||
nov1: true,
|
||||
command: "deny",
|
||||
args: []string{"certificatesigningrequests.v1beta1.certificates.k8s.io/existing"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1beta1/certificatesigningrequests/existing`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/existing`, // typed get, 404
|
||||
`GET /apis/certificates.k8s.io/v1beta1/certificatesigningrequests/existing`, // typed get fallback
|
||||
`PUT /apis/certificates.k8s.io/v1beta1/certificatesigningrequests/existing/approval`,
|
||||
},
|
||||
expectOutput: `denied`,
|
||||
},
|
||||
{
|
||||
name: "deny existing, no v1 or v1beta1",
|
||||
nov1: true,
|
||||
nov1beta1: true,
|
||||
command: "deny",
|
||||
args: []string{"existing"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/existing`, // unstructured get, 404
|
||||
},
|
||||
expectFailure: true,
|
||||
expectErrOutput: `could not find the requested resource`,
|
||||
},
|
||||
{
|
||||
name: "deny already denied",
|
||||
command: "deny",
|
||||
args: []string{"denied"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/denied`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/denied`, // typed get
|
||||
},
|
||||
expectOutput: `denied`,
|
||||
},
|
||||
{
|
||||
name: "deny already denied, force",
|
||||
command: "deny",
|
||||
args: []string{"denied"},
|
||||
force: true,
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/denied`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/denied`, // typed get
|
||||
`PUT /apis/certificates.k8s.io/v1/certificatesigningrequests/denied/approval`,
|
||||
},
|
||||
expectOutput: `denied`,
|
||||
},
|
||||
{
|
||||
name: "deny already approved",
|
||||
command: "deny",
|
||||
args: []string{"approved"},
|
||||
expectActions: []string{
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/approved`, // unstructured get
|
||||
`GET /apis/certificates.k8s.io/v1/certificatesigningrequests/approved`, // typed get
|
||||
},
|
||||
expectFailure: true,
|
||||
expectErrOutput: `is already Approved`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
existingV1 := &certificatesv1.CertificateSigningRequest{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "certificates.k8s.io/v1", Kind: "CertificateSigningRequest"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "existing"},
|
||||
}
|
||||
existingV1beta1 := &certificatesv1beta1.CertificateSigningRequest{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "certificates.k8s.io/v1beta1", Kind: "CertificateSigningRequest"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "existing"},
|
||||
}
|
||||
|
||||
approvedV1 := &certificatesv1.CertificateSigningRequest{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "certificates.k8s.io/v1", Kind: "CertificateSigningRequest"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "approved"},
|
||||
Status: certificatesv1.CertificateSigningRequestStatus{Conditions: []certificatesv1.CertificateSigningRequestCondition{{Type: certificatesv1.CertificateApproved}}},
|
||||
}
|
||||
approvedV1beta1 := &certificatesv1beta1.CertificateSigningRequest{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "certificates.k8s.io/v1beta1", Kind: "CertificateSigningRequest"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "existing"},
|
||||
Status: certificatesv1beta1.CertificateSigningRequestStatus{Conditions: []certificatesv1beta1.CertificateSigningRequestCondition{{Type: certificatesv1beta1.CertificateApproved}}},
|
||||
}
|
||||
|
||||
deniedV1 := &certificatesv1.CertificateSigningRequest{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "certificates.k8s.io/v1", Kind: "CertificateSigningRequest"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "denied"},
|
||||
Status: certificatesv1.CertificateSigningRequestStatus{Conditions: []certificatesv1.CertificateSigningRequestCondition{{Type: certificatesv1.CertificateDenied}}},
|
||||
}
|
||||
deniedV1beta1 := &certificatesv1beta1.CertificateSigningRequest{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "certificates.k8s.io/v1beta1", Kind: "CertificateSigningRequest"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "denied"},
|
||||
Status: certificatesv1beta1.CertificateSigningRequestStatus{Conditions: []certificatesv1beta1.CertificateSigningRequestCondition{{Type: certificatesv1beta1.CertificateDenied}}},
|
||||
}
|
||||
|
||||
actions := []string{}
|
||||
fakeClient := fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
actions = append(actions, req.Method+" "+req.URL.Path)
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case tc.nov1 && strings.HasPrefix(p, "/apis/certificates.k8s.io/v1/"):
|
||||
return &http.Response{StatusCode: http.StatusNotFound, Body: ioutil.NopCloser(bytes.NewBuffer([]byte{}))}, nil
|
||||
case tc.nov1beta1 && strings.HasPrefix(p, "/apis/certificates.k8s.io/v1beta1/"):
|
||||
return &http.Response{StatusCode: http.StatusNotFound, Body: ioutil.NopCloser(bytes.NewBuffer([]byte{}))}, nil
|
||||
|
||||
case p == "/apis/certificates.k8s.io/v1/certificatesigningrequests/missing" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusNotFound}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests/missing" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusNotFound}, nil
|
||||
|
||||
case p == "/apis/certificates.k8s.io/v1/certificatesigningrequests/existing" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, existingV1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1/certificatesigningrequests/existing/approval" && m == http.MethodPut:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, existingV1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests/existing" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, existingV1beta1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests/existing/approval" && m == http.MethodPut:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, existingV1beta1)}, nil
|
||||
|
||||
case p == "/apis/certificates.k8s.io/v1/certificatesigningrequests/approved" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, approvedV1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1/certificatesigningrequests/approved/approval" && m == http.MethodPut:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, approvedV1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests/approved" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, approvedV1beta1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests/approved/approval" && m == http.MethodPut:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, approvedV1beta1)}, nil
|
||||
|
||||
case p == "/apis/certificates.k8s.io/v1/certificatesigningrequests/denied" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, deniedV1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1/certificatesigningrequests/denied/approval" && m == http.MethodPut:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, deniedV1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests/denied" && m == http.MethodGet:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, deniedV1beta1)}, nil
|
||||
case p == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests/denied/approval" && m == http.MethodPut:
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, deniedV1beta1)}, nil
|
||||
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
})
|
||||
tf.UnstructuredClientForMappingFunc = func(gv schema.GroupVersion) (resource.RESTClient, error) {
|
||||
versionedAPIPath := ""
|
||||
if gv.Group == "" {
|
||||
versionedAPIPath = "/api/" + gv.Version
|
||||
} else {
|
||||
versionedAPIPath = "/apis/" + gv.Group + "/" + gv.Version
|
||||
}
|
||||
return &fake.RESTClient{
|
||||
VersionedAPIPath: versionedAPIPath,
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fakeClient,
|
||||
}, nil
|
||||
}
|
||||
tf.Client = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fakeClient,
|
||||
}
|
||||
streams, _, buf, errbuf := genericclioptions.NewTestIOStreams()
|
||||
|
||||
defer func() {
|
||||
// Restore cmdutil behavior.
|
||||
cmdutil.DefaultBehaviorOnFatal()
|
||||
}()
|
||||
// Check exit code.
|
||||
cmdutil.BehaviorOnFatal(func(e string, code int) {
|
||||
if !tc.expectFailure {
|
||||
t.Log(e)
|
||||
t.Errorf("unexpected failure exit code %d", code)
|
||||
}
|
||||
errbuf.Write([]byte(e))
|
||||
})
|
||||
|
||||
var cmd *cobra.Command
|
||||
switch tc.command {
|
||||
case "approve":
|
||||
cmd = NewCmdCertificateApprove(tf, streams)
|
||||
case "deny":
|
||||
cmd = NewCmdCertificateDeny(tf, streams)
|
||||
default:
|
||||
t.Errorf("unknown command: %s", tc.command)
|
||||
}
|
||||
|
||||
if tc.force {
|
||||
cmd.Flags().Set("force", "true")
|
||||
}
|
||||
cmd.Run(cmd, tc.args)
|
||||
|
||||
if !strings.Contains(buf.String(), tc.expectOutput) {
|
||||
t.Errorf("expected output to contain %q:\n%s", tc.expectOutput, buf.String())
|
||||
}
|
||||
if !strings.Contains(errbuf.String(), tc.expectErrOutput) {
|
||||
t.Errorf("expected error output to contain %q:\n%s", tc.expectErrOutput, errbuf.String())
|
||||
}
|
||||
if !reflect.DeepEqual(tc.expectActions, actions) {
|
||||
t.Logf("stdout:\n%s", buf.String())
|
||||
t.Logf("stderr:\n%s", errbuf.String())
|
||||
t.Errorf("expected\n%s\ngot\n%s", strings.Join(tc.expectActions, "\n"), strings.Join(actions, "\n"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -519,6 +519,7 @@ func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
|
|||
clientset.AutoscalingV2beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||
clientset.BatchV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||
clientset.BatchV2alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||
clientset.CertificatesV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||
clientset.CertificatesV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||
clientset.ExtensionsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||
clientset.RbacV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||
|
|
Loading…
Reference in New Issue