mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #91558 from liggitt/csr-e2e
Add e2e coverage for the CertificateSigningRequest API, enable patch support for approval subresource
This commit is contained in:
commit
84861d2102
132
api/openapi-spec/swagger.json
generated
132
api/openapi-spec/swagger.json
generated
@ -60148,21 +60148,42 @@
|
||||
}
|
||||
},
|
||||
"/apis/certificates.k8s.io/v1beta1/certificatesigningrequests/{name}/approval": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"description": "read approval of the specified CertificateSigningRequest",
|
||||
"operationId": "readCertificatesV1beta1CertificateSigningRequestApproval",
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.certificates.v1beta1.CertificateSigningRequest"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"certificates_v1beta1"
|
||||
],
|
||||
"x-kubernetes-action": "get",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "certificates.k8s.io",
|
||||
"kind": "CertificateSigningRequest",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
|
||||
"in": "query",
|
||||
"name": "dryRun",
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
},
|
||||
{
|
||||
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
|
||||
"in": "query",
|
||||
"name": "fieldManager",
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
},
|
||||
{
|
||||
"description": "name of the CertificateSigningRequest",
|
||||
"in": "path",
|
||||
@ -60179,6 +60200,75 @@
|
||||
"uniqueItems": true
|
||||
}
|
||||
],
|
||||
"patch": {
|
||||
"consumes": [
|
||||
"application/json-patch+json",
|
||||
"application/merge-patch+json",
|
||||
"application/strategic-merge-patch+json",
|
||||
"application/apply-patch+yaml"
|
||||
],
|
||||
"description": "partially update approval of the specified CertificateSigningRequest",
|
||||
"operationId": "patchCertificatesV1beta1CertificateSigningRequestApproval",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
|
||||
"in": "query",
|
||||
"name": "dryRun",
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
},
|
||||
{
|
||||
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).",
|
||||
"in": "query",
|
||||
"name": "fieldManager",
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
},
|
||||
{
|
||||
"description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.",
|
||||
"in": "query",
|
||||
"name": "force",
|
||||
"type": "boolean",
|
||||
"uniqueItems": true
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.certificates.v1beta1.CertificateSigningRequest"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
},
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
"certificates_v1beta1"
|
||||
],
|
||||
"x-kubernetes-action": "patch",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "certificates.k8s.io",
|
||||
"kind": "CertificateSigningRequest",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"consumes": [
|
||||
"*/*"
|
||||
@ -60193,6 +60283,20 @@
|
||||
"schema": {
|
||||
"$ref": "#/definitions/io.k8s.api.certificates.v1beta1.CertificateSigningRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
|
||||
"in": "query",
|
||||
"name": "dryRun",
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
},
|
||||
{
|
||||
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
|
||||
"in": "query",
|
||||
"name": "fieldManager",
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
|
@ -109,9 +109,16 @@ func (r *ApprovalREST) New() runtime.Object {
|
||||
return &certificates.CertificateSigningRequest{}
|
||||
}
|
||||
|
||||
// Get retrieves the object from the storage. It is required to support Patch.
|
||||
func (r *ApprovalREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return r.store.Get(ctx, name, options)
|
||||
}
|
||||
|
||||
// Update alters the approval subset of an object.
|
||||
func (r *ApprovalREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
|
||||
// subresources should never allow create on update.
|
||||
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
|
||||
}
|
||||
|
||||
var _ = rest.Patcher(&ApprovalREST{})
|
||||
|
@ -20,13 +20,20 @@ import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
v1beta1client "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
"k8s.io/kubernetes/test/utils"
|
||||
@ -37,9 +44,18 @@ import (
|
||||
var _ = SIGDescribe("Certificates API", func() {
|
||||
f := framework.NewDefaultFramework("certificates")
|
||||
|
||||
/*
|
||||
Release: v1.19
|
||||
Testname: CertificateSigningRequest API Client Certificate
|
||||
Description:
|
||||
- The certificatesigningrequests resource must accept a request for a certificate signed by kubernetes.io/kube-apiserver-client.
|
||||
- The issued certificate must be valid as a client certificate used to authenticate to the kube-apiserver.
|
||||
*/
|
||||
ginkgo.It("should support building a client with a CSR", func() {
|
||||
const commonName = "tester-csr"
|
||||
|
||||
csrClient := f.ClientSet.CertificatesV1beta1().CertificateSigningRequests()
|
||||
|
||||
pk, err := utils.NewPrivateKey()
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
@ -49,29 +65,59 @@ var _ = SIGDescribe("Certificates API", func() {
|
||||
Bytes: pkder,
|
||||
})
|
||||
|
||||
csrb, err := cert.MakeCSR(pk, &pkix.Name{CommonName: commonName, Organization: []string{"system:masters"}}, nil, nil)
|
||||
csrb, err := cert.MakeCSR(pk, &pkix.Name{CommonName: commonName}, nil, nil)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
csr := &certificatesv1beta1.CertificateSigningRequest{
|
||||
apiserverClientSigner := certificatesv1beta1.KubeAPIServerClientSignerName
|
||||
csrTemplate := &certificatesv1beta1.CertificateSigningRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: commonName + "-",
|
||||
},
|
||||
Spec: certificatesv1beta1.CertificateSigningRequestSpec{
|
||||
Request: csrb,
|
||||
Usages: []certificatesv1beta1.KeyUsage{
|
||||
certificatesv1beta1.UsageSigning,
|
||||
certificatesv1beta1.UsageDigitalSignature,
|
||||
certificatesv1beta1.UsageKeyEncipherment,
|
||||
certificatesv1beta1.UsageClientAuth,
|
||||
},
|
||||
SignerName: &apiserverClientSigner,
|
||||
},
|
||||
}
|
||||
csrs := f.ClientSet.CertificatesV1beta1().CertificateSigningRequests()
|
||||
|
||||
// Grant permissions to the new user
|
||||
clusterRole, err := f.ClientSet.RbacV1().ClusterRoles().Create(context.TODO(), &rbacv1.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{GenerateName: commonName + "-"},
|
||||
Rules: []rbacv1.PolicyRule{{Verbs: []string{"create"}, APIGroups: []string{"certificates.k8s.io"}, Resources: []string{"certificatesigningrequests"}}},
|
||||
}, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
// Tolerate RBAC not being enabled
|
||||
framework.Logf("error granting permissions to %s, create certificatesigningrequests permissions must be granted out of band: %v", commonName, err)
|
||||
} else {
|
||||
defer func() {
|
||||
framework.ExpectNoError(f.ClientSet.RbacV1().ClusterRoles().Delete(context.TODO(), clusterRole.Name, metav1.DeleteOptions{}))
|
||||
}()
|
||||
}
|
||||
|
||||
clusterRoleBinding, err := f.ClientSet.RbacV1().ClusterRoleBindings().Create(context.TODO(), &rbacv1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{GenerateName: commonName + "-"},
|
||||
RoleRef: rbacv1.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: clusterRole.Name},
|
||||
Subjects: []rbacv1.Subject{{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: commonName}},
|
||||
}, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
// Tolerate RBAC not being enabled
|
||||
framework.Logf("error granting permissions to %s, create certificatesigningrequests permissions must be granted out of band: %v", commonName, err)
|
||||
} else {
|
||||
defer func() {
|
||||
framework.ExpectNoError(f.ClientSet.RbacV1().ClusterRoleBindings().Delete(context.TODO(), clusterRoleBinding.Name, metav1.DeleteOptions{}))
|
||||
}()
|
||||
}
|
||||
|
||||
framework.Logf("creating CSR")
|
||||
csr, err = csrs.Create(context.TODO(), csr, metav1.CreateOptions{})
|
||||
csr, err := csrClient.Create(context.TODO(), csrTemplate, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
csrName := csr.Name
|
||||
defer func() {
|
||||
framework.ExpectNoError(csrClient.Delete(context.TODO(), csr.Name, metav1.DeleteOptions{}))
|
||||
}()
|
||||
|
||||
framework.Logf("approving CSR")
|
||||
framework.ExpectNoError(wait.Poll(5*time.Second, time.Minute, func() (bool, error) {
|
||||
@ -82,9 +128,9 @@ var _ = SIGDescribe("Certificates API", func() {
|
||||
Message: "Set from an e2e test",
|
||||
},
|
||||
}
|
||||
csr, err = csrs.UpdateApproval(context.TODO(), csr, metav1.UpdateOptions{})
|
||||
csr, err = csrClient.UpdateApproval(context.TODO(), csr, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
csr, _ = csrs.Get(context.TODO(), csrName, metav1.GetOptions{})
|
||||
csr, _ = csrClient.Get(context.TODO(), csr.Name, metav1.GetOptions{})
|
||||
framework.Logf("err updating approval: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
@ -93,7 +139,7 @@ var _ = SIGDescribe("Certificates API", func() {
|
||||
|
||||
framework.Logf("waiting for CSR to be signed")
|
||||
framework.ExpectNoError(wait.Poll(5*time.Second, time.Minute, func() (bool, error) {
|
||||
csr, err = csrs.Get(context.TODO(), csrName, metav1.GetOptions{})
|
||||
csr, err = csrClient.Get(context.TODO(), csr.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
framework.Logf("error getting csr: %v", err)
|
||||
return false, nil
|
||||
@ -108,17 +154,247 @@ var _ = SIGDescribe("Certificates API", func() {
|
||||
framework.Logf("testing the client")
|
||||
rcfg, err := framework.LoadConfig()
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
rcfg = rest.AnonymousClientConfig(rcfg)
|
||||
rcfg.TLSClientConfig.CertData = csr.Status.Certificate
|
||||
rcfg.TLSClientConfig.KeyData = pkpem
|
||||
rcfg.TLSClientConfig.CertFile = ""
|
||||
rcfg.BearerToken = ""
|
||||
rcfg.AuthProvider = nil
|
||||
rcfg.Username = ""
|
||||
rcfg.Password = ""
|
||||
|
||||
newClient, err := v1beta1client.NewForConfig(rcfg)
|
||||
newClient, err := certificatesclient.NewForConfig(rcfg)
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectNoError(newClient.CertificateSigningRequests().Delete(context.TODO(), csrName, metav1.DeleteOptions{}))
|
||||
|
||||
framework.Logf("creating CSR as new client")
|
||||
newCSR, err := newClient.CertificateSigningRequests().Create(context.TODO(), csrTemplate, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
defer func() {
|
||||
framework.ExpectNoError(csrClient.Delete(context.TODO(), newCSR.Name, metav1.DeleteOptions{}))
|
||||
}()
|
||||
framework.ExpectEqual(newCSR.Spec.Username, commonName)
|
||||
})
|
||||
|
||||
/*
|
||||
Release: v1.19
|
||||
Testname: CertificateSigningRequest API
|
||||
Description:
|
||||
- The certificates.k8s.io API group MUST exists in the /apis discovery document.
|
||||
- The certificates.k8s.io/v1beta1 API group/version MUST exist in the /apis/certificates.k8s.io discovery document.
|
||||
- The certificatesigningrequests, certificatesigningrequests/approval, and certificatesigningrequests/status
|
||||
resources MUST exist in the /apis/certificates.k8s.io/v1beta1 discovery document.
|
||||
- The certificatesigningrequests resource must support create, get, list, watch, update, patch, delete, and deletecollection.
|
||||
- The certificatesigningrequests/approval resource must support get, update, patch.
|
||||
- The certificatesigningrequests/status resource must support get, update, patch.
|
||||
*/
|
||||
ginkgo.It("should support CSR API operations [Privileged:ClusterAdmin]", func() {
|
||||
|
||||
// Setup
|
||||
csrVersion := "v1beta1"
|
||||
csrClient := f.ClientSet.CertificatesV1beta1().CertificateSigningRequests()
|
||||
csrResource := certificatesv1beta1.SchemeGroupVersion.WithResource("certificatesigningrequests")
|
||||
|
||||
pk, err := utils.NewPrivateKey()
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
csrData, err := cert.MakeCSR(pk, &pkix.Name{CommonName: "e2e.example.com"}, []string{"e2e.example.com"}, nil)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
certificateData, _, err := cert.GenerateSelfSignedCertKey("e2e.example.com", nil, []string{"e2e.example.com"})
|
||||
framework.ExpectNoError(err)
|
||||
certificateDataJSON, err := json.Marshal(certificateData)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
signerName := "example.com/e2e-" + f.UniqueName
|
||||
csrTemplate := &certificatesv1beta1.CertificateSigningRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{GenerateName: "e2e-example-csr-"},
|
||||
Spec: certificatesv1beta1.CertificateSigningRequestSpec{
|
||||
Request: csrData,
|
||||
SignerName: &signerName,
|
||||
Usages: []certificatesv1beta1.KeyUsage{certificatesv1beta1.UsageDigitalSignature, certificatesv1beta1.UsageKeyEncipherment, certificatesv1beta1.UsageServerAuth},
|
||||
},
|
||||
}
|
||||
|
||||
// Discovery
|
||||
|
||||
ginkgo.By("getting /apis")
|
||||
{
|
||||
discoveryGroups, err := f.ClientSet.Discovery().ServerGroups()
|
||||
framework.ExpectNoError(err)
|
||||
found := false
|
||||
for _, group := range discoveryGroups.Groups {
|
||||
if group.Name == certificatesv1beta1.GroupName {
|
||||
for _, version := range group.Versions {
|
||||
if version.Version == csrVersion {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
framework.ExpectEqual(found, true, fmt.Sprintf("expected certificates API group/version, got %#v", discoveryGroups.Groups))
|
||||
}
|
||||
|
||||
ginkgo.By("getting /apis/certificates.k8s.io")
|
||||
{
|
||||
group := &metav1.APIGroup{}
|
||||
err := f.ClientSet.Discovery().RESTClient().Get().AbsPath("/apis/certificates.k8s.io").Do(context.TODO()).Into(group)
|
||||
framework.ExpectNoError(err)
|
||||
found := false
|
||||
for _, version := range group.Versions {
|
||||
if version.Version == csrVersion {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
framework.ExpectEqual(found, true, fmt.Sprintf("expected certificates API version, got %#v", group.Versions))
|
||||
}
|
||||
|
||||
ginkgo.By("getting /apis/certificates.k8s.io/" + csrVersion)
|
||||
{
|
||||
resources, err := f.ClientSet.Discovery().ServerResourcesForGroupVersion(certificatesv1beta1.SchemeGroupVersion.String())
|
||||
framework.ExpectNoError(err)
|
||||
foundCSR, foundApproval, foundStatus := false, false, false
|
||||
for _, resource := range resources.APIResources {
|
||||
switch resource.Name {
|
||||
case "certificatesigningrequests":
|
||||
foundCSR = true
|
||||
case "certificatesigningrequests/approval":
|
||||
foundApproval = true
|
||||
case "certificatesigningrequests/status":
|
||||
foundStatus = true
|
||||
}
|
||||
}
|
||||
framework.ExpectEqual(foundCSR, true, fmt.Sprintf("expected certificatesigningrequests, got %#v", resources.APIResources))
|
||||
framework.ExpectEqual(foundApproval, true, fmt.Sprintf("expected certificatesigningrequests/approval, got %#v", resources.APIResources))
|
||||
framework.ExpectEqual(foundStatus, true, fmt.Sprintf("expected certificatesigningrequests/status, got %#v", resources.APIResources))
|
||||
}
|
||||
|
||||
// Main resource create/read/update/watch operations
|
||||
|
||||
ginkgo.By("creating")
|
||||
_, err = csrClient.Create(context.TODO(), csrTemplate, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
_, err = csrClient.Create(context.TODO(), csrTemplate, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
createdCSR, err := csrClient.Create(context.TODO(), csrTemplate, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("getting")
|
||||
gottenCSR, err := csrClient.Get(context.TODO(), createdCSR.Name, metav1.GetOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(gottenCSR.UID, createdCSR.UID)
|
||||
|
||||
ginkgo.By("listing")
|
||||
csrs, err := csrClient.List(context.TODO(), metav1.ListOptions{FieldSelector: "spec.signerName=" + signerName})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(len(csrs.Items), 3, "filtered list should have 3 items")
|
||||
|
||||
ginkgo.By("watching")
|
||||
framework.Logf("starting watch")
|
||||
csrWatch, err := csrClient.Watch(context.TODO(), metav1.ListOptions{ResourceVersion: csrs.ResourceVersion, FieldSelector: "metadata.name=" + createdCSR.Name})
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("patching")
|
||||
patchedCSR, err := csrClient.Patch(context.TODO(), createdCSR.Name, types.MergePatchType, []byte(`{"metadata":{"annotations":{"patched":"true"}}}`), metav1.PatchOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(patchedCSR.Annotations["patched"], "true", "patched object should have the applied annotation")
|
||||
|
||||
ginkgo.By("updating")
|
||||
csrToUpdate := patchedCSR.DeepCopy()
|
||||
csrToUpdate.Annotations["updated"] = "true"
|
||||
updatedCSR, err := csrClient.Update(context.TODO(), csrToUpdate, metav1.UpdateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(updatedCSR.Annotations["updated"], "true", "updated object should have the applied annotation")
|
||||
|
||||
framework.Logf("waiting for watch events with expected annotations")
|
||||
for sawAnnotations := false; !sawAnnotations; {
|
||||
select {
|
||||
case evt, ok := <-csrWatch.ResultChan():
|
||||
framework.ExpectEqual(ok, true, "watch channel should not close")
|
||||
framework.ExpectEqual(evt.Type, watch.Modified)
|
||||
watchedCSR, isCSR := evt.Object.(*certificatesv1beta1.CertificateSigningRequest)
|
||||
framework.ExpectEqual(isCSR, true, fmt.Sprintf("expected CSR, got %T", evt.Object))
|
||||
if watchedCSR.Annotations["patched"] == "true" {
|
||||
framework.Logf("saw patched and updated annotations")
|
||||
sawAnnotations = true
|
||||
csrWatch.Stop()
|
||||
} else {
|
||||
framework.Logf("missing expected annotations, waiting: %#v", watchedCSR.Annotations)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
framework.Fail("timed out waiting for watch event")
|
||||
}
|
||||
}
|
||||
|
||||
// /approval subresource operations
|
||||
|
||||
ginkgo.By("getting /approval")
|
||||
gottenApproval, err := f.DynamicClient.Resource(csrResource).Get(context.TODO(), createdCSR.Name, metav1.GetOptions{}, "approval")
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(gottenApproval.GetObjectKind().GroupVersionKind(), certificatesv1beta1.SchemeGroupVersion.WithKind("CertificateSigningRequest"))
|
||||
framework.ExpectEqual(gottenApproval.GetUID(), createdCSR.UID)
|
||||
|
||||
ginkgo.By("patching /approval")
|
||||
patchedApproval, err := csrClient.Patch(context.TODO(), createdCSR.Name, types.MergePatchType,
|
||||
[]byte(`{"metadata":{"annotations":{"patchedapproval":"true"}},"status":{"conditions":[{"type":"ApprovalPatch","status":"True","reason":"e2e"}]}}`),
|
||||
metav1.PatchOptions{}, "approval")
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(len(patchedApproval.Status.Conditions), 1, fmt.Sprintf("patched object should have the applied condition, got %#v", patchedApproval.Status.Conditions))
|
||||
framework.ExpectEqual(string(patchedApproval.Status.Conditions[0].Type), "ApprovalPatch", fmt.Sprintf("patched object should have the applied condition, got %#v", patchedApproval.Status.Conditions))
|
||||
framework.ExpectEqual(patchedApproval.Annotations["patchedapproval"], "true", "patched object should have the applied annotation")
|
||||
|
||||
ginkgo.By("updating /approval")
|
||||
approvalToUpdate := patchedApproval.DeepCopy()
|
||||
approvalToUpdate.Status.Conditions = append(approvalToUpdate.Status.Conditions, certificatesv1beta1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1beta1.CertificateApproved,
|
||||
Reason: "E2E",
|
||||
Message: "Set from an e2e test",
|
||||
})
|
||||
updatedApproval, err := csrClient.UpdateApproval(context.TODO(), approvalToUpdate, metav1.UpdateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(len(updatedApproval.Status.Conditions), 2, fmt.Sprintf("updated object should have the applied condition, got %#v", updatedApproval.Status.Conditions))
|
||||
framework.ExpectEqual(updatedApproval.Status.Conditions[1].Type, certificatesv1beta1.CertificateApproved, fmt.Sprintf("updated object should have the approved condition, got %#v", updatedApproval.Status.Conditions))
|
||||
|
||||
// /status subresource operations
|
||||
|
||||
ginkgo.By("getting /status")
|
||||
gottenStatus, err := f.DynamicClient.Resource(csrResource).Get(context.TODO(), createdCSR.Name, metav1.GetOptions{}, "status")
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(gottenStatus.GetObjectKind().GroupVersionKind(), certificatesv1beta1.SchemeGroupVersion.WithKind("CertificateSigningRequest"))
|
||||
framework.ExpectEqual(gottenStatus.GetUID(), createdCSR.UID)
|
||||
|
||||
ginkgo.By("patching /status")
|
||||
patchedStatus, err := csrClient.Patch(context.TODO(), createdCSR.Name, types.MergePatchType,
|
||||
[]byte(`{"metadata":{"annotations":{"patchedstatus":"true"}},"status":{"certificate":`+string(certificateDataJSON)+`}}`),
|
||||
metav1.PatchOptions{}, "status")
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(patchedStatus.Status.Certificate, certificateData, "patched object should have the applied certificate")
|
||||
framework.ExpectEqual(patchedStatus.Annotations["patchedstatus"], "true", "patched object should have the applied annotation")
|
||||
|
||||
ginkgo.By("updating /status")
|
||||
statusToUpdate := patchedStatus.DeepCopy()
|
||||
statusToUpdate.Status.Conditions = append(statusToUpdate.Status.Conditions, certificatesv1beta1.CertificateSigningRequestCondition{
|
||||
Type: "StatusUpdate",
|
||||
Reason: "E2E",
|
||||
Message: "Set from an e2e test",
|
||||
})
|
||||
updatedStatus, err := csrClient.UpdateStatus(context.TODO(), statusToUpdate, metav1.UpdateOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(len(updatedStatus.Status.Conditions), len(statusToUpdate.Status.Conditions), fmt.Sprintf("updated object should have the applied condition, got %#v", updatedStatus.Status.Conditions))
|
||||
framework.ExpectEqual(string(updatedStatus.Status.Conditions[len(updatedStatus.Status.Conditions)-1].Type), "StatusUpdate", fmt.Sprintf("updated object should have the approved condition, got %#v", updatedStatus.Status.Conditions))
|
||||
|
||||
// main resource delete operations
|
||||
|
||||
ginkgo.By("deleting")
|
||||
err = csrClient.Delete(context.TODO(), createdCSR.Name, metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
_, err = csrClient.Get(context.TODO(), createdCSR.Name, metav1.GetOptions{})
|
||||
framework.ExpectEqual(apierrors.IsNotFound(err), true, fmt.Sprintf("expected 404, got %#v", err))
|
||||
csrs, err = csrClient.List(context.TODO(), metav1.ListOptions{FieldSelector: "spec.signerName=" + signerName})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(len(csrs.Items), 2, "filtered list should have 2 items")
|
||||
|
||||
ginkgo.By("deleting a collection")
|
||||
err = csrClient.DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{FieldSelector: "spec.signerName=" + signerName})
|
||||
framework.ExpectNoError(err)
|
||||
csrs, err = csrClient.List(context.TODO(), metav1.ListOptions{FieldSelector: "spec.signerName=" + signerName})
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectEqual(len(csrs.Items), 0, "filtered list should have 0 items")
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user