diff --git a/.generated_docs b/.generated_docs index 2ae3d57dfcd..354466cea2b 100644 --- a/.generated_docs +++ b/.generated_docs @@ -15,6 +15,9 @@ docs/man/man1/kubectl-api-versions.1 docs/man/man1/kubectl-apply.1 docs/man/man1/kubectl-attach.1 docs/man/man1/kubectl-autoscale.1 +docs/man/man1/kubectl-certificate-approve.1 +docs/man/man1/kubectl-certificate-deny.1 +docs/man/man1/kubectl-certificate.1 docs/man/man1/kubectl-cluster-info-dump.1 docs/man/man1/kubectl-cluster-info.1 docs/man/man1/kubectl-completion.1 @@ -90,6 +93,9 @@ docs/user-guide/kubectl/kubectl_api-versions.md docs/user-guide/kubectl/kubectl_apply.md docs/user-guide/kubectl/kubectl_attach.md docs/user-guide/kubectl/kubectl_autoscale.md +docs/user-guide/kubectl/kubectl_certificate.md +docs/user-guide/kubectl/kubectl_certificate_approve.md +docs/user-guide/kubectl/kubectl_certificate_deny.md docs/user-guide/kubectl/kubectl_cluster-info.md docs/user-guide/kubectl/kubectl_cluster-info_dump.md docs/user-guide/kubectl/kubectl_completion.md @@ -162,6 +168,7 @@ docs/yaml/kubectl/kubectl_api-versions.yaml docs/yaml/kubectl/kubectl_apply.yaml docs/yaml/kubectl/kubectl_attach.yaml docs/yaml/kubectl/kubectl_autoscale.yaml +docs/yaml/kubectl/kubectl_certificate.yaml docs/yaml/kubectl/kubectl_cluster-info.yaml docs/yaml/kubectl/kubectl_completion.yaml docs/yaml/kubectl/kubectl_config.yaml diff --git a/docs/man/man1/kubectl-certificate-approve.1 b/docs/man/man1/kubectl-certificate-approve.1 new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/man/man1/kubectl-certificate-approve.1 @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/docs/man/man1/kubectl-certificate-deny.1 b/docs/man/man1/kubectl-certificate-deny.1 new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/man/man1/kubectl-certificate-deny.1 @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/docs/man/man1/kubectl-certificate.1 b/docs/man/man1/kubectl-certificate.1 new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/man/man1/kubectl-certificate.1 @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/docs/user-guide/kubectl/kubectl_certificate.md b/docs/user-guide/kubectl/kubectl_certificate.md new file mode 100644 index 00000000000..08bbf8b8392 --- /dev/null +++ b/docs/user-guide/kubectl/kubectl_certificate.md @@ -0,0 +1,36 @@ + + + + +WARNING +WARNING +WARNING +WARNING +WARNING + +

PLEASE NOTE: This document applies to the HEAD of the source tree

+ +If you are using a released version of Kubernetes, you should +refer to the docs that go with that version. + +Documentation for other releases can be found at +[releases.k8s.io](http://releases.k8s.io). + +-- + + + + + +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. + + +[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_certificate.md?pixel)]() + diff --git a/docs/user-guide/kubectl/kubectl_certificate_approve.md b/docs/user-guide/kubectl/kubectl_certificate_approve.md new file mode 100644 index 00000000000..d16f538e4e9 --- /dev/null +++ b/docs/user-guide/kubectl/kubectl_certificate_approve.md @@ -0,0 +1,36 @@ + + + + +WARNING +WARNING +WARNING +WARNING +WARNING + +

PLEASE NOTE: This document applies to the HEAD of the source tree

+ +If you are using a released version of Kubernetes, you should +refer to the docs that go with that version. + +Documentation for other releases can be found at +[releases.k8s.io](http://releases.k8s.io). + +-- + + + + + +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. + + +[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_certificate_approve.md?pixel)]() + diff --git a/docs/user-guide/kubectl/kubectl_certificate_deny.md b/docs/user-guide/kubectl/kubectl_certificate_deny.md new file mode 100644 index 00000000000..1af69ff0dcd --- /dev/null +++ b/docs/user-guide/kubectl/kubectl_certificate_deny.md @@ -0,0 +1,36 @@ + + + + +WARNING +WARNING +WARNING +WARNING +WARNING + +

PLEASE NOTE: This document applies to the HEAD of the source tree

+ +If you are using a released version of Kubernetes, you should +refer to the docs that go with that version. + +Documentation for other releases can be found at +[releases.k8s.io](http://releases.k8s.io). + +-- + + + + + +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. + + +[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_certificate_deny.md?pixel)]() + diff --git a/docs/yaml/kubectl/kubectl_certificate.yaml b/docs/yaml/kubectl/kubectl_certificate.yaml new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/yaml/kubectl/kubectl_certificate.yaml @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/hack/make-rules/test-cmd.sh b/hack/make-rules/test-cmd.sh index fb2cdc91028..439f1ef2da8 100755 --- a/hack/make-rules/test-cmd.sh +++ b/hack/make-rules/test-cmd.sh @@ -2884,6 +2884,44 @@ __EOF__ # Post-condition: valid-pod doesn't exist kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" '' + ################ + # Certificates # + ################ + + # approve + kubectl create -f hack/testdata/csr.yml "${kube_flags[@]}" + kube::test::get_object_assert 'csr/foo' '{{range.status.conditions}}{{.type}}{{end}}' '' + kubectl certificate approve foo "${kube_flags[@]}" + kubectl get csr "${kube_flags[@]}" -o json + kube::test::get_object_assert 'csr/foo' '{{range.status.conditions}}{{.type}}{{end}}' 'Approved' + kubectl delete -f hack/testdata/csr.yml "${kube_flags[@]}" + kube::test::get_object_assert csr "{{range.items}}{{$id_field}}{{end}}" '' + + kubectl create -f hack/testdata/csr.yml "${kube_flags[@]}" + kube::test::get_object_assert 'csr/foo' '{{range.status.conditions}}{{.type}}{{end}}' '' + kubectl certificate approve -f hack/testdata/csr.yml "${kube_flags[@]}" + kubectl get csr "${kube_flags[@]}" -o json + kube::test::get_object_assert 'csr/foo' '{{range.status.conditions}}{{.type}}{{end}}' 'Approved' + kubectl delete -f hack/testdata/csr.yml "${kube_flags[@]}" + kube::test::get_object_assert csr "{{range.items}}{{$id_field}}{{end}}" '' + + # deny + kubectl create -f hack/testdata/csr.yml "${kube_flags[@]}" + kube::test::get_object_assert 'csr/foo' '{{range.status.conditions}}{{.type}}{{end}}' '' + kubectl certificate deny foo "${kube_flags[@]}" + kubectl get csr "${kube_flags[@]}" -o json + kube::test::get_object_assert 'csr/foo' '{{range.status.conditions}}{{.type}}{{end}}' 'Denied' + kubectl delete -f hack/testdata/csr.yml "${kube_flags[@]}" + kube::test::get_object_assert csr "{{range.items}}{{$id_field}}{{end}}" '' + + kubectl create -f hack/testdata/csr.yml "${kube_flags[@]}" + kube::test::get_object_assert 'csr/foo' '{{range.status.conditions}}{{.type}}{{end}}' '' + kubectl certificate deny -f hack/testdata/csr.yml "${kube_flags[@]}" + kubectl get csr "${kube_flags[@]}" -o json + kube::test::get_object_assert 'csr/foo' '{{range.status.conditions}}{{.type}}{{end}}' 'Denied' + kubectl delete -f hack/testdata/csr.yml "${kube_flags[@]}" + kube::test::get_object_assert csr "{{range.items}}{{$id_field}}{{end}}" '' + kube::test::clear_all } diff --git a/hack/testdata/csr.yml b/hack/testdata/csr.yml new file mode 100644 index 00000000000..7e84246053f --- /dev/null +++ b/hack/testdata/csr.yml @@ -0,0 +1,6 @@ +apiVersion: certificates.k8s.io/v1alpha1 +kind: CertificateSigningRequest +metadata: + name: foo +spec: + request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ2d6Q0NBV3NDQVFBd0ZURVRNQkVHQTFVRUF4TUthM1ZpWlMxaFpHMXBiakNDQVNJd0RRWUpLb1pJaHZjTgpBUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTlJ5dFhkcWV6ZTFBdXFjZkpWYlFBY1BJejZWY2pXSTZ5WmlQa3lrCjAzUW9GaHJGRXhUQXNPTGVFUHlrQXc1YndUOWZiajRXMzZmR2k4RGxsd1FzVGoyYzVUTnBnQkkwbElDbzI4aGcKbHYvTDJsMnRsWUVKdDdTbVhjblNvaGJ5S0h4TERRUHVmTVBBTkZsaEFmTUdCWEhRcmZMajhrTk1MUDA4UlBsbAp0N3V4RDVRdFA0cHlGL1Nhbm1XVEtRNU56WlJ4TC82UmhJMEpxSHJmNFFjQmg2dlR5bnFaRGVmMWVxNjBnQXllClNPRkpKYWRuK3h2VEFqLzgxZk1TbjdOSlNnaktDYkNEeXQ1eS9UZHd0SzZnVUQzM01paE5uNXhKTVF0MUZXUVAKRzY3eTA1QVh6b0pqTm5sWVA1MnJsTlhvNzh6aVMrN1E4RklxQzY0c05vWWhxeGNDQXdFQUFhQXBNQ2NHQ1NxRwpTSWIzRFFFSkRqRWFNQmd3Q1FZRFZSMFRCQUl3QURBTEJnTlZIUThFQkFNQ0JlQXdEUVlKS29aSWh2Y05BUUVMCkJRQURnZ0VCQU5CazlwaHpWYUJBci9xZHN4bXdPR1NQa094UkZlR1lyemRvaW5LTzVGUGZER2JkU0VWQ0o1K0wKeWJTNUtmaUZYU1EvNmk0RE9WRWtxcnFrVElIc1JNSlJwbTZ5Zjk1TU4zSWVLak9jQlV2b2VWVlpxMUNOUU8zagp2dklmK1A1NStLdXpvK0NIT1F5RWlvTlRPaUtGWTJseStEZEEwMXMxbU9FMTZSWGlWeFhGcFhGeGRJVmRPK0oxClZ1MW5yWG5ZVFJQRmtyaG80MTlpaDQzNjRPcGZqYXFXVCtmd20ySVZQSlBoaUJpYi9RRzRhUGJJcFh3amlCUUMKemV6WlM2L01nQkt1bUdMZ3Z5MitXNU9UWTJ5ZFFMZFVxbERFNEU2MFhmdVZ6bk5zWjZDS0tYY1pVaW1ZTkkwNgpKa0t4bGRjd0V2cmI0SmN3M2RFQjdOOUwvSW9ZNXFBPQotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index e0eb83dc3bc..d7dc2e82e6d 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -18,6 +18,7 @@ go_library( "apply.go", "attach.go", "autoscale.go", + "certificates.go", "clusterinfo.go", "clusterinfo_dump.go", "cmd.go", @@ -69,6 +70,7 @@ go_library( "//pkg/api/validation:go_default_library", "//pkg/apimachinery/registered:go_default_library", "//pkg/apis/batch/v1:go_default_library", + "//pkg/apis/certificates:go_default_library", "//pkg/apis/extensions/v1beta1:go_default_library", "//pkg/apis/policy:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", diff --git a/pkg/kubectl/cmd/certificates.go b/pkg/kubectl/cmd/certificates.go new file mode 100644 index 00000000000..315bf952673 --- /dev/null +++ b/pkg/kubectl/cmd/certificates.go @@ -0,0 +1,196 @@ +/* +Copyright 2016 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 cmd + +import ( + "fmt" + "io" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/certificates" + "k8s.io/kubernetes/pkg/kubectl/cmd/templates" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/kubectl/resource" + + "github.com/spf13/cobra" +) + +func NewCmdCertificate(f cmdutil.Factory, out io.Writer) *cobra.Command { + cmd := &cobra.Command{ + Use: "certificate SUBCOMMAND", + Short: "Modify certificate resources.", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, + } + + cmd.AddCommand(NewCmdCertificateApprove(f, out)) + cmd.AddCommand(NewCmdCertificateDeny(f, out)) + + return cmd +} + +type CertificateOptions struct { + resource.FilenameOptions + csrNames []string + outputStyle string +} + +func (options *CertificateOptions) Complete(cmd *cobra.Command, args []string) error { + options.csrNames = args + options.outputStyle = cmdutil.GetFlagString(cmd, "output") + return nil +} + +func (options *CertificateOptions) Validate() error { + if len(options.csrNames) < 1 && cmdutil.IsFilenameEmpty(options.Filenames) { + return fmt.Errorf("one or more CSRs must be specified as or -f ") + } + return nil +} + +func NewCmdCertificateApprove(f cmdutil.Factory, out io.Writer) *cobra.Command { + options := CertificateOptions{} + cmd := &cobra.Command{ + Use: "approve (-f FILENAME | NAME)", + Short: "Approve a certificate signing request", + Long: templates.LongDesc(` + Approve a certificate signing request. + + kubectl certificate approve allows a cluster admin to approve a certificate + signing request (CSR). This action tells a certificate signing controller to + issue a certificate to the requestor with the attributes requested in the CSR. + + SECURITY NOTICE: Depending on the requested attributes, the issued certificate + can potentially grant a requester access to cluster resources or to authenticate + as a requested identity. Before approving a CSR, ensure you understand what the + signed certificate can do. + `), + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(options.Complete(cmd, args)) + cmdutil.CheckErr(options.Validate()) + cmdutil.CheckErr(options.RunCertificateApprove(f, out)) + }, + } + cmdutil.AddOutputFlagsForMutation(cmd) + cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to update") + + return cmd +} + +func (options *CertificateOptions) RunCertificateApprove(f cmdutil.Factory, out io.Writer) error { + return options.modifyCertificateCondition(f, out, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, string) { + var alreadyApproved bool + for _, c := range csr.Status.Conditions { + if c.Type == certificates.CertificateApproved { + alreadyApproved = true + } + } + if alreadyApproved { + return csr, "approved" + } + csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{ + Type: certificates.CertificateApproved, + Reason: "KubectlApprove", + Message: "This CSR was approved by kubectl certificate approve.", + LastUpdateTime: unversioned.Now(), + }) + return csr, "approved" + }) +} + +func NewCmdCertificateDeny(f cmdutil.Factory, out io.Writer) *cobra.Command { + options := CertificateOptions{} + cmd := &cobra.Command{ + Use: "deny (-f FILENAME | NAME)", + Short: "Deny a certificate signing request", + Long: templates.LongDesc(` + Deny a certificate signing request. + + kubectl certificate deny allows a cluster admin to deny a certificate + signing request (CSR). This action tells a certificate signing controller to + not to issue a certificate to the requestor. + `), + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(options.Complete(cmd, args)) + cmdutil.CheckErr(options.Validate()) + cmdutil.CheckErr(options.RunCertificateDeny(f, out)) + }, + } + cmdutil.AddOutputFlagsForMutation(cmd) + cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to update") + + return cmd +} + +func (options *CertificateOptions) RunCertificateDeny(f cmdutil.Factory, out io.Writer) error { + return options.modifyCertificateCondition(f, out, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, string) { + var alreadyDenied bool + for _, c := range csr.Status.Conditions { + if c.Type == certificates.CertificateDenied { + alreadyDenied = true + } + } + if alreadyDenied { + return csr, "denied" + } + csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{ + Type: certificates.CertificateDenied, + Reason: "KubectlDeny", + Message: "This CSR was approved by kubectl certificate deny.", + LastUpdateTime: unversioned.Now(), + }) + return csr, "denied" + }) +} + +func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory, out io.Writer, modify func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, string)) error { + var found int + mapper, typer := f.Object() + c, err := f.ClientSet() + if err != nil { + return err + } + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). + ContinueOnError(). + FilenameParam(false, &options.FilenameOptions). + ResourceNames("certificatesigningrequest", options.csrNames...). + RequireObject(true). + Flatten(). + Latest(). + Do() + err = r.Visit(func(info *resource.Info, err error) error { + if err != nil { + return err + } + csr := info.Object.(*certificates.CertificateSigningRequest) + csr, verb := modify(csr) + csr, err = c.Certificates(). + CertificateSigningRequests(). + UpdateApproval(csr) + if err != nil { + return err + } + found++ + cmdutil.PrintSuccess(mapper, options.outputStyle == "name", out, info.Mapping.Resource, info.Name, false, verb) + return nil + }) + if found == 0 { + fmt.Fprintf(out, "No resources found\n") + } + return err +} diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index 4b5bd32e135..f8833a69a54 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -251,6 +251,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob { Message: "Cluster Management Commands:", Commands: []*cobra.Command{ + NewCmdCertificate(f, out), NewCmdClusterInfo(f, out), NewCmdTop(f, out, err), NewCmdCordon(f, out),