Merge pull request #2165 from JoshVanL/0.11-cli-migration

Adds CLI migration tool with docs on how to use it
This commit is contained in:
jetstack-bot 2019-10-09 17:45:27 +01:00 committed by GitHub
commit d3a418642e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 293 additions and 4 deletions

View File

@ -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.

View File

@ -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",

View File

@ -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"],
)

220
hack/api-migration/main.go Normal file
View File

@ -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
}