diff --git a/docs/tasks/upgrading/upgrading-0.10-0.11.rst b/docs/tasks/upgrading/upgrading-0.10-0.11.rst index c3263d70a..4ea5072b9 100644 --- a/docs/tasks/upgrading/upgrading-0.10-0.11.rst +++ b/docs/tasks/upgrading/upgrading-0.10-0.11.rst @@ -2,8 +2,6 @@ Upgrading from v0.10 to v0.11 ============================= -** NOTE: THIS UPGRADE GUIDE IS PROVISIONAL AND MAY NOT BE COMPLETE WHILST THE v0.11 RELEASE SERIES IS IN ALPHA** - The v0.11 release marks the removal of the v1alpha1 API that was used in previous versions of cert-manager, as well as our API group changing to be ``cert-manager.io`` instead of ``certmanager.k8s.io``. @@ -23,7 +21,7 @@ This upgrade should be performed in a few steps: 1) Back up existing cert-manager resources, as per the :doc:`backup and restore guide <../backup-restore-crds>`. -2) Uninstall cert-manager (by running ``kubectl delete -f`` or ``helm delete --purge``) +2) :doc: `Uninstall cert-manager`<../uninstalling/index>`. 3) Ensure the old cert-manager CRD resources have also been deleted: ``kubectl get crd | grep certmanager.k8s.io`` @@ -47,7 +45,19 @@ you will need to update them to reflect the new API group. A full table of annotations, including the old and new equivalents: -.. TODO: create a table mapping old annotations to new +| Old Annotation | New Annotation | +|----------------------------------------------|-------------------------------------------| +| certmanager.k8s.io/acme-http01-edit-in-place | acme.cert-manager.io/http01-edit-in-place | +| certmanager.k8s.io/acme-http01-ingress-class | acme.cert-manager.io/http01-ingress-class | +| certmanager.k8s.io/issuer | cert-manager.io/issuer | +| certmanager.k8s.io/cluster-issuer | cert-manager.io/cluster-issuer | +| certmanager.k8s.io/acme-challenge-type | DEPRECATED | +| certmanager.k8s.io/acme-dns01-provider | DEPRECATED | +| certmanager.k8s.io/alt-names | cert-manager.io/alt-names | +| certmanager.k8s.io/ip-sans | cert-manager.io/ip-sans | +| certmanager.k8s.io/common-name | cert-manager.io/common-name | +| certmanager.k8s.io/issuer-name | cert-manager.io/issuer-name | +| certmanager.k8s.io/issuer-kind | cert-manager.io/issuer-kind | You can use the following bash magic to print a list of Ingress resources that still contain an old annotation: @@ -62,5 +72,25 @@ still contain an old annotation: Ingress resource "demo/testcrt contains old annotations: (certmanager.k8s.io/cluster-issuer)" Ingress resource "example/ingress-resource contains old annotations: (certmanager.k8s.io/cluster-issuer)" +In order to help with this migration, the following CLI tool will automatically +migrate these annotations for you. Note that it *will not* make any changes to +your cluster for you. + +.. code-block:: shell + + # Firstly, download the binary for your given platform + $ wget -O api-migration https://github.com/jetstack/cert-manager/releases/download/v0.11.0/api-migration-linux + # or for Darwin + $ wget -O api-migration https://github.com/jetstack/cert-manager/releases/download/v0.11.0/api-migration-darwin + + # Mark the binary as executable and run the binary against your cluster + $ chmod +x api-migration && ./api-migration --kubeconfig /path/to/my/kubeconfig.yaml + + # Follow the CLI ouput and check for the difference that has been made in files + $ diff ingress.yaml ingress-migrated.yaml + + # Finally, once the new ingress resources have been reviewed, apply the manifests + $ kubectl apply -f ingress-migrated.yaml --kubeconfig /path/to/my/kubeconfig.yaml + You should make sure to update _all_ Ingress resources to ensure that your certificates continue to be kept up to date. diff --git a/hack/BUILD.bazel b/hack/BUILD.bazel index 6db028ad7..d5599e8a4 100644 --- a/hack/BUILD.bazel +++ b/hack/BUILD.bazel @@ -296,6 +296,7 @@ filegroup( name = "all-srcs", srcs = [ ":package-srcs", + "//hack/api-migration:all-srcs", "//hack/bin:all-srcs", "//hack/boilerplate:all-srcs", "//hack/build:all-srcs", diff --git a/hack/api-migration/BUILD.bazel b/hack/api-migration/BUILD.bazel new file mode 100644 index 000000000..693207fc2 --- /dev/null +++ b/hack/api-migration/BUILD.bazel @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "go_default_library", + srcs = ["main.go"], + importpath = "github.com/jetstack/cert-manager/hack/api-migration", + visibility = ["//visibility:private"], + deps = [ + "@com_github_spf13_cobra//:go_default_library", + "@io_k8s_api//extensions/v1beta1:go_default_library", + "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", + "@io_k8s_apimachinery//pkg/runtime/serializer/json:go_default_library", + "@io_k8s_client_go//kubernetes:go_default_library", + "@io_k8s_client_go//kubernetes/scheme:go_default_library", + "@io_k8s_client_go//plugin/pkg/client/auth:go_default_library", + "@io_k8s_client_go//tools/clientcmd:go_default_library", + ], +) + +go_binary( + name = "api-migration", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/hack/api-migration/main.go b/hack/api-migration/main.go new file mode 100644 index 000000000..6be79294b --- /dev/null +++ b/hack/api-migration/main.go @@ -0,0 +1,220 @@ +/* +Copyright 2019 The Jetstack cert-manager contributors. + +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 main + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "strings" + + "github.com/spf13/cobra" + networkingv1beta "k8s.io/api/extensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/serializer/json" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/clientcmd" + + // This package is required to be imported to register all client + // plugins. + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +const ( + oldGroupName = "certmanager.k8s.io" +) + +var ( + kubeconfig string + origFile string + newFile string + migrations = map[string]string{ + "certmanager.k8s.io/acme-http01-edit-in-place": "acme.cert-manager.io/http01-edit-in-place", + "certmanager.k8s.io/acme-http01-ingress-class": "acme.cert-manager.io/http01-ingress-class", + + "certmanager.k8s.io/issuer": "cert-manager.io/issuer", + "certmanager.k8s.io/cluster-issuer": "cert-manager.io/cluster-issuer", + "certmanager.k8s.io/alt-names": "cert-manager.io/alt-names", + "certmanager.k8s.io/ip-sans": "cert-manager.io/ip-sans", + "certmanager.k8s.io/common-name": "cert-manager.io/common-name", + "certmanager.k8s.io/issuer-name": "cert-manager.io/issuer-name", + "certmanager.k8s.io/issuer-kind": "cert-manager.io/issuer-kind", + "certmanager.k8s.io/certificate-name": "cert-manager.io/certificate-name", + } + + deprecations = []string{ + "certmanager.k8s.io/acme-challenge-type", + "certmanager.k8s.io/acme-dns01-provider", + } +) + +var cmd = &cobra.Command{ + Use: "cert-manager-api-migration", + Long: "A tool to download Ingress resources and convert cert-manager annotations from certmanager.k8s.io to cert-manager.io. This tool will not modify resources in the API server.", + RunE: func(cmd *cobra.Command, args []string) error { + if len(kubeconfig) == 0 { + kubeconfig = os.Getenv("KUBECONFIG") + } + + if len(kubeconfig) == 0 { + return errors.New("flag --kubeconfig or environment variable $KUBECONFIG must be set") + } + + client, err := clientSet(kubeconfig) + if err != nil { + return err + } + + ingList, err := client.ExtensionsV1beta1().Ingresses("").List(metav1.ListOptions{}) + if err != nil { + return err + } + + ingStr, err := writeIngressToFile(ingList, origFile) + if err != nil { + return err + } + + fmt.Printf("written current ingresses to file %q\n", origFile) + fmt.Printf("searching ingress resources for occurrences of old API annotations...\n") + + for oldA, newA := range migrations { + count := strings.Count(ingStr, oldA) + if count == 0 { + continue + } + + fmt.Printf("found %d instances of %q\tmigrating to %q\n", count, oldA, newA) + + ingStr = strings.ReplaceAll(ingStr, oldA, newA) + } + + for _, d := range deprecations { + count := strings.Count(ingStr, d) + if count == 0 { + continue + } + + fmt.Printf("found %d instances of %q\tthis field is DEPRECATED and will be deleted\n", count, d) + + lines := strings.Split(ingStr, "\n") + + for i, line := range lines { + if strings.Contains(line, d) { + lines = append(lines[:i], lines[i+1:]...) + } + } + + ingStr = strings.Join(lines, "\n") + } + + count := strings.Count(ingStr, oldGroupName) + if count > 0 { + fmt.Fprintf(os.Stderr, + "found %d more instances of the group %q with unrecognised paths\n", count, oldGroupName) + } + + if err := ioutil.WriteFile(newFile, []byte(ingStr), 0644); err != nil { + return err + } + + fmt.Printf("wrote new ingresses to file %q\n", newFile) + + var buff bytes.Buffer + diffFile := fmt.Sprintf("%s.%s.diff", origFile, newFile) + eCmd := exec.Command("diff", origFile, newFile) + eCmd.Stdout = &buff + + if err := eCmd.Run(); err != nil { + if exitErr, ok := err.(*exec.ExitError); !ok || exitErr.ExitCode() != 1 { + return err + } + } + + if err := ioutil.WriteFile(diffFile, buff.Bytes(), 0644); err != nil { + return err + } + + fmt.Printf("wrote diff to file %q\n", diffFile) + + fmt.Printf("\nPlease check for any missing or incorrect annotations in your newly generated ingress manifests.\n") + fmt.Printf("You should now check the diff of the two files to determine what has changed - either by inspecting the diff file %q, or running the command yourself:\n\n", + diffFile) + fmt.Printf("$ diff %s %s\n", origFile, newFile) + + return nil + }, +} + +func writeIngressToFile(ingList *networkingv1beta.IngressList, path string) (string, error) { + s := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme, + scheme.Scheme) + + var buff bytes.Buffer + + for _, ing := range ingList.Items { + ing.Kind = "Ingress" + ing.APIVersion = "extensions/v1beta1" + + if err := s.Encode(&ing, &buff); err != nil { + return "", err + } + + if _, err := buff.WriteString("---\n"); err != nil { + return "", err + } + } + + if err := ioutil.WriteFile(path, buff.Bytes(), 0644); err != nil { + return "", err + } + + return buff.String(), nil +} + +func main() { + cmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path location to Kubeconfig") + cmd.PersistentFlags().StringVarP(&origFile, "original-file", "o", "ingress.yaml", "File path to store the current list of Ingress resources") + cmd.PersistentFlags().StringVarP(&newFile, "new-file", "n", "ingress-migrated.yaml", "File path to store the migrated Ingress resources") + + if err := cmd.Execute(); err != nil { + fmt.Fprint(os.Stderr, err.Error()) + } +} + +func clientSet(kubeconfig string) (*kubernetes.Clientset, error) { + kubeconfigBytes, err := ioutil.ReadFile(kubeconfig) + if err != nil { + return nil, err + } + + restConfig, err := clientcmd.RESTConfigFromKubeConfig(kubeconfigBytes) + if err != nil { + return nil, err + } + + clientset, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return nil, err + } + + return clientset, nil +}