Repository: jetstack/cert-manager
Pull request #3018 (2020-06-18 10:01:41)

Fix ctl convert cmd
<!--  Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, read our contributor guidelines https://git.k8s.io/community/contributors/guide#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
3. Follow the instructions for writing a release note: https://git.k8s.io/community/contributors/guide/release-notes.md
4. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

**What this PR does / why we need it**:
From PR 2956: So it turns out kubectl cert-manager convert does not actually work if there are real meaningful conversions to be performed (i.e. not just a 1:1 conversion).

PR 2956 initially adds a test case to show this, and this PR fixes the bug
**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

**Special notes for your reviewer**:

**Release note**:
<!--  Steps to write your release note:
1. Use the release-note-* labels to set the release note state (if you have access)
2. Enter your extended release note in the below block; leaving it blank means using the PR title as the release note. If no release note is required, just write `NONE`.
-->
```
Fix bug causing `kubectl cert-manager convert` to not work when conversions need to be performed
```
/kind bug
/priority important-soon
/area ctl
/cc @munnerz 


Patch:
diff --git a/BUILD.bazel b/BUILD.bazel
index 73649b701..27514ee82 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -74,6 +74,7 @@ filegroup(
         "//pkg/client/listers/certmanager/v1alpha2:all-srcs",
         "//pkg/client/listers/certmanager/v1alpha3:all-srcs",
         "//pkg/controller:all-srcs",
+        "//pkg/ctl:all-srcs",
         "//pkg/feature:all-srcs",
         "//pkg/internal:all-srcs",
         "//pkg/issuer:all-srcs",
diff --git a/LICENSES b/LICENSES
index a611259ef..a1b1729d7 100644
--- a/LICENSES
+++ b/LICENSES
@@ -10595,6 +10595,10 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
 ================================================================================
 = vendor/github.com/miekg/dns licensed under: =
 
+Extensions of the original work are copyright (c) 2011 Miek Gieben
+
+As this is fork of the official Go code the same license applies:
+
 Copyright (c) 2009 The Go Authors. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -10623,10 +10627,8 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-As this is fork of the official Go code the same license applies.
-Extensions of the original work are copyright (c) 2011 Miek Gieben
 
-= vendor/github.com/miekg/dns/LICENSE 567c1ad6c08ca0ee8d7e0a0cf790aff9
+= vendor/github.com/miekg/dns/LICENSE 147353de6868a20caa562d26eab7b3c5
 ================================================================================
 
 
diff --git a/WORKSPACE b/WORKSPACE
index 4532afa6e..f6355c213 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -59,7 +59,7 @@ http_archive(
 git_repository(
     name = "io_bazel_rules_docker",
     remote = "https://github.com/bazelbuild/rules_docker.git",
-    commit = "f1557ebc5381e2a662314e21585aa8080e9508be",
+    commit = "80ea3aae060077e5fe0cdef1a5c570d4b7622100",
     shallow_since = "1561646721 -0700",
 )
 
diff --git a/build/container.bzl b/build/container.bzl
index b8fe8c1a1..ce3f417f7 100644
--- a/build/container.bzl
+++ b/build/container.bzl
@@ -61,10 +61,6 @@ def multi_arch_container(
             go_platform_constraint(os = "linux", arch = arch): base.format(ARCH = arch)
             for arch in architectures
         }),
-        architecture = select({
-            go_platform_constraint(os = "linux", arch = arch): arch
-            for arch in architectures
-        }),
         stamp = stamp,
         tags = tags,
         user = user,
@@ -86,10 +82,6 @@ def multi_arch_container(
         stamp = stamp,
         tags = tags,
         user = user,
-        architecture = select({
-            go_platform_constraint(os = "linux", arch = arch): arch
-            for arch in architectures
-        }),
         visibility = ["//visibility:public"],
     )
 
diff --git a/cmd/controller/app/controller.go b/cmd/controller/app/controller.go
index 8d8d78b24..65129a695 100644
--- a/cmd/controller/app/controller.go
+++ b/cmd/controller/app/controller.go
@@ -74,13 +74,14 @@ func Run(opts *options.ControllerOptions, stopCh <-chan struct{}) {
 		os.Exit(1)
 	}
 
-	metricsServer, err := ctx.Metrics.Start(opts.MetricsListenAddress)
-	if err != nil {
-		log.Error(err, "failed to listen on prometheus address", "address", opts.MetricsListenAddress)
-		os.Exit(1)
-	}
-
 	var wg sync.WaitGroup
+	wg.Add(1)
+
+	go func() {
+		defer wg.Done()
+		metrics.Default.Start(stopCh)
+	}()
+
 	var experimentalCertificateControllers = []string{
 		trigger.ControllerName,
 		issuing.ControllerName,
@@ -145,7 +146,6 @@ func Run(opts *options.ControllerOptions, stopCh <-chan struct{}) {
 		ctx.KubeSharedInformerFactory.Start(stopCh)
 		wg.Wait()
 		log.Info("control loops exited")
-		ctx.Metrics.Shutdown(metricsServer)
 		os.Exit(0)
 	}
 
@@ -225,9 +225,6 @@ func buildControllerContext(ctx context.Context, stopCh <-chan struct{}, opts *o
 
 	sharedInformerFactory := informers.NewSharedInformerFactoryWithOptions(intcl, time.Second*30, informers.WithNamespace(opts.Namespace))
 	kubeSharedInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(cl, time.Second*30, kubeinformers.WithNamespace(opts.Namespace))
-
-	acmeAccountRegistry := accounts.NewDefaultRegistry()
-
 	return &controller.Context{
 		RootContext:               ctx,
 		StopCh:                    stopCh,
@@ -239,7 +236,6 @@ func buildControllerContext(ctx context.Context, stopCh <-chan struct{}, opts *o
 		SharedInformerFactory:     sharedInformerFactory,
 		Namespace:                 opts.Namespace,
 		Clock:                     clock.RealClock{},
-		Metrics:                   metrics.New(log),
 		ACMEOptions: controller.ACMEOptions{
 			HTTP01SolverImage:                 opts.ACMEHTTP01SolverImage,
 			HTTP01SolverResourceRequestCPU:    HTTP01SolverResourceRequestCPU,
@@ -248,7 +244,7 @@ func buildControllerContext(ctx context.Context, stopCh <-chan struct{}, opts *o
 			HTTP01SolverResourceLimitsMemory:  HTTP01SolverResourceLimitsMemory,
 			DNS01CheckAuthoritative:           !opts.DNS01RecursiveNameserversOnly,
 			DNS01Nameservers:                  nameservers,
-			AccountRegistry:                   acmeAccountRegistry,
+			AccountRegistry:                   accounts.NewDefaultRegistry(),
 		},
 		IssuerOptions: controller.IssuerOptions{
 			ClusterIssuerAmbientCredentials: opts.ClusterIssuerAmbientCredentials,
diff --git a/cmd/controller/app/options/BUILD.bazel b/cmd/controller/app/options/BUILD.bazel
index 4a94788e8..aa4ff1071 100644
--- a/cmd/controller/app/options/BUILD.bazel
+++ b/cmd/controller/app/options/BUILD.bazel
@@ -16,7 +16,6 @@ go_library(
         "//pkg/controller/certificaterequests/vault:go_default_library",
         "//pkg/controller/certificaterequests/venafi:go_default_library",
         "//pkg/controller/certificates:go_default_library",
-        "//pkg/controller/certificates/metrics:go_default_library",
         "//pkg/controller/clusterissuers:go_default_library",
         "//pkg/controller/ingress-shim:go_default_library",
         "//pkg/controller/issuers:go_default_library",
diff --git a/cmd/controller/app/options/options.go b/cmd/controller/app/options/options.go
index 8d61f6d6a..77249376b 100644
--- a/cmd/controller/app/options/options.go
+++ b/cmd/controller/app/options/options.go
@@ -33,7 +33,6 @@ import (
 	crvaultcontroller "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/vault"
 	crvenaficontroller "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/venafi"
 	certificatescontroller "github.com/jetstack/cert-manager/pkg/controller/certificates"
-	certificatesmetricscontroller "github.com/jetstack/cert-manager/pkg/controller/certificates/metrics"
 	clusterissuerscontroller "github.com/jetstack/cert-manager/pkg/controller/clusterissuers"
 	ingressshimcontroller "github.com/jetstack/cert-manager/pkg/controller/ingress-shim"
 	issuerscontroller "github.com/jetstack/cert-manager/pkg/controller/issuers"
@@ -79,10 +78,6 @@ type ControllerOptions struct {
 	EnableCertificateOwnerRef bool
 
 	MaxConcurrentChallenges int
-
-	// The host and port address, separated by a ':', that the Prometheus server
-	// should expose metrics on.
-	MetricsListenAddress string
 }
 
 const (
@@ -109,8 +104,6 @@ const (
 	defaultDNS01RecursiveNameserversOnly = false
 
 	defaultMaxConcurrentChallenges = 60
-
-	defaultPrometheusMetricsServerAddress = "0.0.0.0:9402"
 )
 
 var (
@@ -126,7 +119,6 @@ var (
 		issuerscontroller.ControllerName,
 		clusterissuerscontroller.ControllerName,
 		certificatescontroller.ControllerName,
-		certificatesmetricscontroller.ControllerName,
 		ingressshimcontroller.ControllerName,
 		orderscontroller.ControllerName,
 		challengescontroller.ControllerName,
@@ -160,7 +152,6 @@ func NewControllerOptions() *ControllerOptions {
 		DNS01RecursiveNameservers:         []string{},
 		DNS01RecursiveNameserversOnly:     defaultDNS01RecursiveNameserversOnly,
 		EnableCertificateOwnerRef:         defaultEnableCertificateOwnerRef,
-		MetricsListenAddress:              defaultPrometheusMetricsServerAddress,
 	}
 }
 
@@ -256,9 +247,6 @@ func (s *ControllerOptions) AddFlags(fs *pflag.FlagSet) {
 		"When this flag is enabled, the secret will be automatically removed when the certificate resource is deleted.")
 	fs.IntVar(&s.MaxConcurrentChallenges, "max-concurrent-challenges", defaultMaxConcurrentChallenges, ""+
 		"The maximum number of challenges that can be scheduled as 'processing' at once.")
-
-	fs.StringVar(&s.MetricsListenAddress, "metrics-listen-address", defaultPrometheusMetricsServerAddress, ""+
-		"The host and port that the metrics endpoint should listen on.")
 }
 
 func (o *ControllerOptions) Validate() error {
diff --git a/cmd/ctl/pkg/convert/BUILD.bazel b/cmd/ctl/pkg/convert/BUILD.bazel
index e80618b29..c6b21659d 100644
--- a/cmd/ctl/pkg/convert/BUILD.bazel
+++ b/cmd/ctl/pkg/convert/BUILD.bazel
@@ -6,28 +6,18 @@ go_library(
     importpath = "github.com/jetstack/cert-manager/cmd/ctl/pkg/convert",
     visibility = ["//visibility:public"],
     deps = [
-        "//pkg/acme/webhook/apis/acme/v1alpha1:go_default_library",
-        "//pkg/apis/acme/v1alpha2:go_default_library",
-        "//pkg/apis/acme/v1alpha3:go_default_library",
         "//pkg/apis/certmanager/v1alpha2:go_default_library",
-        "//pkg/apis/certmanager/v1alpha3:go_default_library",
-        "//pkg/apis/meta/v1:go_default_library",
+        "//pkg/ctl:go_default_library",
         "@com_github_spf13_cobra//:go_default_library",
-        "@io_k8s_api//auditregistration/v1alpha1:go_default_library",
-        "@io_k8s_apiextensions_apiserver//pkg/apis/apiextensions/v1beta1:go_default_library",
         "@io_k8s_apimachinery//pkg/apis/meta/internalversion:go_default_library",
-        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
         "@io_k8s_apimachinery//pkg/runtime:go_default_library",
         "@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
         "@io_k8s_apimachinery//pkg/runtime/serializer:go_default_library",
         "@io_k8s_apimachinery//pkg/runtime/serializer/json:go_default_library",
-        "@io_k8s_apimachinery//pkg/util/runtime:go_default_library",
         "@io_k8s_cli_runtime//pkg/genericclioptions:go_default_library",
         "@io_k8s_cli_runtime//pkg/printers:go_default_library",
         "@io_k8s_cli_runtime//pkg/resource:go_default_library",
-        "@io_k8s_client_go//kubernetes/scheme:go_default_library",
         "@io_k8s_klog//:go_default_library",
-        "@io_k8s_kube_aggregator//pkg/apis/apiregistration/v1beta1:go_default_library",
         "@io_k8s_kubectl//pkg/cmd/util:go_default_library",
         "@io_k8s_kubectl//pkg/util/i18n:go_default_library",
         "@io_k8s_kubectl//pkg/util/templates:go_default_library",
diff --git a/cmd/ctl/pkg/convert/convert.go b/cmd/ctl/pkg/convert/convert.go
index 2a6f45ce2..5a061c153 100644
--- a/cmd/ctl/pkg/convert/convert.go
+++ b/cmd/ctl/pkg/convert/convert.go
@@ -20,31 +20,21 @@ import (
 	"fmt"
 
 	"github.com/spf13/cobra"
-	auditreg "k8s.io/api/auditregistration/v1alpha1"
-	apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/runtime/serializer"
 	apijson "k8s.io/apimachinery/pkg/runtime/serializer/json"
-	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	"k8s.io/cli-runtime/pkg/printers"
 	"k8s.io/cli-runtime/pkg/resource"
-	kscheme "k8s.io/client-go/kubernetes/scheme"
 	"k8s.io/klog"
-	apireg "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
 	cmdutil "k8s.io/kubectl/pkg/cmd/util"
 	"k8s.io/kubectl/pkg/util/i18n"
 	"k8s.io/kubectl/pkg/util/templates"
 
-	whapi "github.com/jetstack/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
-	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2"
-	cmacmev1alpha3 "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha3"
-	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
-	cmapiv1alpha3 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha3"
-	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
+	cmapiv1alpha2 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
+	"github.com/jetstack/cert-manager/pkg/ctl"
 )
 
 var (
@@ -68,35 +58,11 @@ to change to output destination.`))
 )
 
 var (
-	scheme             = runtime.NewScheme()
-	codecs             = serializer.NewCodecFactory(scheme)
-	parameterCodec     = runtime.NewParameterCodec(scheme)
-	localSchemeBuilder = runtime.SchemeBuilder{
-		cmapi.AddToScheme,
-		cmapiv1alpha3.AddToScheme,
-		cmacme.AddToScheme,
-		cmacmev1alpha3.AddToScheme,
-		cmmeta.AddToScheme,
-		whapi.AddToScheme,
-		kscheme.AddToScheme,
-		apireg.AddToScheme,
-		apiext.AddToScheme,
-		auditreg.AddToScheme,
-	}
-
-	addToScheme = localSchemeBuilder.AddToScheme
+	// Use this scheme as it has the internal cert-manager types
+	// and their conversion functions registered.
+	scheme = ctl.Scheme
 )
 
-func init() {
-	// This is used to add the List object type for outputing multiple input
-	// objects.
-	coreGroupVersion := schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal}
-	scheme.AddKnownTypes(coreGroupVersion, &metainternalversion.List{})
-
-	metav1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
-	utilruntime.Must(addToScheme(scheme))
-}
-
 // Options is a struct to support convert command
 type Options struct {
 	PrintFlags *genericclioptions.PrintFlags
@@ -159,8 +125,9 @@ func (o *Options) Complete() error {
 func (o *Options) Run() error {
 	builder := new(resource.Builder)
 
-	r := builder.Unstructured().LocalParam(true).ContinueOnError().
-		FilenameParam(false, &o.FilenameOptions).Flatten().Do()
+	r := builder.
+		WithScheme(scheme, schema.GroupVersion{Group: cmapiv1alpha2.SchemeGroupVersion.Group, Version: runtime.APIVersionInternal}).
+		LocalParam(true).FilenameParam(false, &o.FilenameOptions).Flatten().Do()
 
 	if err := r.Err(); err != nil {
 		return fmt.Errorf("error here: %s", err)
diff --git a/deploy/charts/cert-manager/values.yaml b/deploy/charts/cert-manager/values.yaml
index 3b9d0de23..e240185ed 100644
--- a/deploy/charts/cert-manager/values.yaml
+++ b/deploy/charts/cert-manager/values.yaml
@@ -112,7 +112,6 @@ volumeMounts: []
 # podAnnotations: {}
 
 podLabels: {}
-
 # Optional DNS settings, useful if you have a public and private DNS zone for
 # the same domain on Route 53. What follows is an example of ensuring
 # cert-manager can access an ingress or DNS TXT records at all times.
diff --git a/devel/addon/ingressnginx/BUILD.bazel b/devel/addon/ingressnginx/BUILD.bazel
index ed436f248..9bb21871c 100644
--- a/devel/addon/ingressnginx/BUILD.bazel
+++ b/devel/addon/ingressnginx/BUILD.bazel
@@ -3,7 +3,7 @@ load("@io_bazel_rules_docker//container:bundle.bzl", "container_bundle")
 container_bundle(
     name = "bundle",
     images = {
-        "quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.33.0": "@io_kubernetes_ingress-nginx//image",
+        "quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1": "@io_kubernetes_ingress-nginx//image",
         "k8s.gcr.io/defaultbackend-amd64:bazel": "@io_gcr_k8s_defaultbackend//image",
     },
     tags = ["manual"],
diff --git a/devel/addon/ingressnginx/install.sh b/devel/addon/ingressnginx/install.sh
index de5e381cd..e72008542 100755
--- a/devel/addon/ingressnginx/install.sh
+++ b/devel/addon/ingressnginx/install.sh
@@ -34,29 +34,28 @@ SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")
 # Require helm available on PATH
 check_tool kubectl
 check_tool helm
-require_image "quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.33.0" "//devel/addon/ingressnginx:bundle"
+require_image "quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1" "//devel/addon/ingressnginx:bundle"
 require_image "k8s.gcr.io/defaultbackend-amd64:bazel" "//devel/addon/ingressnginx:bundle"
 
 # Ensure the pebble namespace exists
 kubectl get namespace "${NAMESPACE}" || kubectl create namespace "${NAMESPACE}"
 
-helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
+helm repo add stable https://kubernetes-charts.storage.googleapis.com
 
 helm repo update
 
-# Upgrade or install nginx-ingress
+# Upgrade or install Pebble
 helm upgrade \
     --install \
     --wait \
-    --version 2.4.0 \
+    --version 1.23.0 \
     --namespace "${NAMESPACE}" \
-    --set controller.image.tag=0.33.0 \
+    --set controller.image.tag=0.26.1 \
     --set controller.image.pullPolicy=Never \
     --set defaultBackend.image.tag=bazel \
     --set defaultBackend.image.pullPolicy=Never \
     --set controller.service.clusterIP=10.0.0.15 \
     --set controller.service.type=ClusterIP \
     --set controller.config.no-tls-redirect-locations="" \
-    --set admissionWebhooks.enabled=false \
     "$RELEASE_NAME" \
-    ingress-nginx/ingress-nginx
+    stable/nginx-ingress
diff --git a/go.mod b/go.mod
index 2a5dbe7ad..042181615 100644
--- a/go.mod
+++ b/go.mod
@@ -26,7 +26,7 @@ require (
 	github.com/hashicorp/vault/sdk v0.1.13
 	github.com/kr/pretty v0.1.0
 	github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a
-	github.com/miekg/dns v1.1.29
+	github.com/miekg/dns v0.0.0-20170721150254-0f3adef2e220
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/munnerz/crd-schema-fuzz v1.0.0
 	github.com/onsi/ginkgo v1.11.0
@@ -37,10 +37,9 @@ require (
 	github.com/spf13/cobra v0.0.5
 	github.com/spf13/pflag v1.0.5
 	github.com/stretchr/testify v1.5.1
-	golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5
-	golang.org/x/net v0.0.0-20200421231249-e086a090c8fd
+	golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
+	golang.org/x/net v0.0.0-20200202094626-16171245cfb2
 	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
-	golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect
 	google.golang.org/api v0.4.0
 	gopkg.in/ini.v1 v1.52.0 // indirect
 	gopkg.in/yaml.v2 v2.2.8
diff --git a/go.sum b/go.sum
index e32f3e3bf..7fcd5ce4e 100644
--- a/go.sum
+++ b/go.sum
@@ -332,8 +332,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
-github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
+github.com/miekg/dns v0.0.0-20170721150254-0f3adef2e220 h1:XMdRBCTXUEnj/+IMBiv3IsARZaMcx3KUkRUdxZn5t4I=
+github.com/miekg/dns v0.0.0-20170721150254-0f3adef2e220/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -383,6 +383,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR
 github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -439,6 +440,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -474,7 +476,6 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -492,11 +493,10 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=
-golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -529,11 +529,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190425045458-9f0b1ff7b46a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
 golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
-golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -558,12 +555,10 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72 h1:bw9doJza/SFBEweII/rHQh338oozWyiFsBRHtrflcws=
 golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
-golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
 gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
 google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
diff --git a/hack/build/repos.bzl b/hack/build/repos.bzl
index f29012306..c6aba7a61 100644
--- a/hack/build/repos.bzl
+++ b/hack/build/repos.bzl
@@ -1222,8 +1222,8 @@ def go_repositories():
         build_file_generation = "on",
         build_file_proto_mode = "disable",
         importpath = "github.com/miekg/dns",
-        sum = "h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=",
-        version = "v1.1.29",
+        sum = "h1:XMdRBCTXUEnj/+IMBiv3IsARZaMcx3KUkRUdxZn5t4I=",
+        version = "v0.0.0-20170721150254-0f3adef2e220",
     )
     go_repository(
         name = "com_github_mitchellh_copystructure",
@@ -2008,8 +2008,8 @@ def go_repositories():
         build_file_generation = "on",
         build_file_proto_mode = "disable",
         importpath = "golang.org/x/net",
-        sum = "h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=",
-        version = "v0.0.0-20200421231249-e086a090c8fd",
+        sum = "h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=",
+        version = "v0.0.0-20200202094626-16171245cfb2",
     )
     go_repository(
         name = "org_golang_x_oauth2",
@@ -2032,8 +2032,8 @@ def go_repositories():
         build_file_generation = "on",
         build_file_proto_mode = "disable",
         importpath = "golang.org/x/sys",
-        sum = "h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=",
-        version = "v0.0.0-20200420163511-1957bb5e6d1f",
+        sum = "h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=",
+        version = "v0.0.0-20191022100944-742c48ecaeb7",
     )
     go_repository(
         name = "org_golang_x_text",
@@ -2056,8 +2056,8 @@ def go_repositories():
         build_file_generation = "on",
         build_file_proto_mode = "disable",
         importpath = "golang.org/x/tools",
-        sum = "h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=",
-        version = "v0.0.0-20191216052735-49a3e744a425",
+        sum = "h1:bw9doJza/SFBEweII/rHQh338oozWyiFsBRHtrflcws=",
+        version = "v0.0.0-20190920225731-5eefd052ad72",
     )
     go_repository(
         name = "org_gonum_v1_gonum",
@@ -2256,8 +2256,8 @@ def go_repositories():
         build_file_generation = "on",
         build_file_proto_mode = "disable",
         importpath = "golang.org/x/xerrors",
-        sum = "h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=",
-        version = "v0.0.0-20191011141410-1b5146add898",
+        sum = "h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=",
+        version = "v0.0.0-20190717185122-a985d3407aa7",
     )
     go_repository(
         name = "io_etcd_go_bbolt",
@@ -2683,11 +2683,3 @@ def go_repositories():
         sum = "h1:MksmcCZQWAQJCTA5T0jgI/0sJ51AVm4Z41MrmfczEoc=",
         version = "v0.0.0-20160121211510-db5cfe13f5cc",
     )
-    go_repository(
-        name = "org_golang_x_mod",
-        build_file_generation = "on",
-        build_file_proto_mode = "disable",
-        importpath = "golang.org/x/mod",
-        sum = "h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=",
-        version = "v0.1.1-0.20191105210325-c90efee705ee",
-    )
diff --git a/pkg/acme/accounts/BUILD.bazel b/pkg/acme/accounts/BUILD.bazel
index 10b0a4053..00f80fedd 100644
--- a/pkg/acme/accounts/BUILD.bazel
+++ b/pkg/acme/accounts/BUILD.bazel
@@ -2,16 +2,12 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 
 go_library(
     name = "go_default_library",
-    srcs = [
-        "client.go",
-        "registry.go",
-    ],
+    srcs = ["registry.go"],
     importpath = "github.com/jetstack/cert-manager/pkg/acme/accounts",
     visibility = ["//visibility:public"],
     deps = [
         "//pkg/acme/client:go_default_library",
         "//pkg/apis/acme/v1alpha2:go_default_library",
-        "//pkg/metrics:go_default_library",
         "//pkg/util:go_default_library",
         "@org_golang_x_crypto//acme:go_default_library",
     ],
diff --git a/pkg/acme/accounts/client.go b/pkg/acme/accounts/client.go
deleted file mode 100644
index e481e70ca..000000000
--- a/pkg/acme/accounts/client.go
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-Copyright 2020 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 accounts
-
-import (
-	"crypto/rsa"
-	"crypto/tls"
-	"net"
-	"net/http"
-	"time"
-
-	acmeapi "golang.org/x/crypto/acme"
-
-	acmecl "github.com/jetstack/cert-manager/pkg/acme/client"
-	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2"
-	"github.com/jetstack/cert-manager/pkg/metrics"
-	"github.com/jetstack/cert-manager/pkg/util"
-)
-
-// NewClient will return a new ACME client.
-func NewClient(client *http.Client, config cmacme.ACMEIssuer, privateKey *rsa.PrivateKey) acmecl.Interface {
-	return &acmeapi.Client{
-		Key:          privateKey,
-		HTTPClient:   client,
-		DirectoryURL: config.Server,
-		UserAgent:    util.CertManagerUserAgent,
-	}
-}
-
-// BuildHTTPClient returns a instramented HTTP client to be used by the ACME
-// client.
-// For the time being, we construct a new HTTP client on each invocation.
-// This is because we need to set the 'skipTLSVerify' flag on the HTTP client
-// itself.
-// In future, we may change to having two global HTTP clients - one that ignores
-// TLS connection errors, and the other that does not.
-func BuildHTTPClient(metrics *metrics.Metrics, skipTLSVerify bool) *http.Client {
-	return acmecl.NewInstrumentedClient(metrics,
-		&http.Client{
-			Transport: &http.Transport{
-				Proxy: http.ProxyFromEnvironment,
-				DialContext: (&net.Dialer{
-					Timeout:   30 * time.Second,
-					KeepAlive: 30 * time.Second,
-				}).DialContext,
-				TLSClientConfig:       &tls.Config{InsecureSkipVerify: skipTLSVerify},
-				MaxIdleConns:          100,
-				IdleConnTimeout:       90 * time.Second,
-				TLSHandshakeTimeout:   10 * time.Second,
-				ExpectContinueTimeout: 1 * time.Second,
-			},
-			Timeout: time.Second * 30,
-		})
-}
diff --git a/pkg/acme/accounts/registry.go b/pkg/acme/accounts/registry.go
index aae04cfc8..6bffdc2c1 100644
--- a/pkg/acme/accounts/registry.go
+++ b/pkg/acme/accounts/registry.go
@@ -18,12 +18,18 @@ package accounts
 
 import (
 	"crypto/rsa"
+	"crypto/tls"
 	"errors"
+	"net"
 	"net/http"
 	"sync"
+	"time"
+
+	acmeapi "golang.org/x/crypto/acme"
 
 	acmecl "github.com/jetstack/cert-manager/pkg/acme/client"
 	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2"
+	"github.com/jetstack/cert-manager/pkg/util"
 )
 
 // ErrNotFound is returned by GetClient if there is no ACME client registered.
@@ -35,7 +41,7 @@ var ErrNotFound = errors.New("ACME client for issuer not initialised/available")
 type Registry interface {
 	// AddClient will ensure the registry has a stored ACME client for the Issuer
 	// object with the given UID, configuration and private key.
-	AddClient(client *http.Client, uid string, config cmacme.ACMEIssuer, privateKey *rsa.PrivateKey)
+	AddClient(uid string, config cmacme.ACMEIssuer, privateKey *rsa.PrivateKey)
 
 	// RemoveClient will remove a registered client using the UID of the Issuer
 	// resource that constructed it.
@@ -110,9 +116,9 @@ type clientWithMeta struct {
 
 // AddClient will ensure the registry has a stored ACME client for the Issuer
 // object with the given UID, configuration and private key.
-func (r *registry) AddClient(client *http.Client, uid string, config cmacme.ACMEIssuer, privateKey *rsa.PrivateKey) {
+func (r *registry) AddClient(uid string, config cmacme.ACMEIssuer, privateKey *rsa.PrivateKey) {
 	// ensure the client is up to date for the current configuration
-	r.ensureClient(client, uid, config, privateKey)
+	r.ensureClient(uid, config, privateKey)
 }
 
 // ensureClient will ensure an ACME client with the given parameters is registered.
@@ -120,7 +126,7 @@ func (r *registry) AddClient(client *http.Client, uid string, config cmacme.ACME
 // the client will NOT be mutated or replaced, allowing this method to be called
 // even if the client does not need replacing/updating without causing issues for
 // consumers of the registry.
-func (r *registry) ensureClient(client *http.Client, uid string, config cmacme.ACMEIssuer, privateKey *rsa.PrivateKey) {
+func (r *registry) ensureClient(uid string, config cmacme.ACMEIssuer, privateKey *rsa.PrivateKey) {
 	// acquire a read-write lock even if we hit the fast-path where the client
 	// is already present to avoid having to RLock, RUnlock and Lock again,
 	// which could itself cause a race
@@ -134,7 +140,7 @@ func (r *registry) ensureClient(client *http.Client, uid string, config cmacme.A
 	// create a new client if one is not registered or if the
 	// 'metadata' does not match
 	r.clients[uid] = clientWithMeta{
-		Interface:     NewClient(client, config, privateKey),
+		Interface:     NewClient(config, privateKey),
 		stableOptions: newOpts,
 	}
 }
@@ -177,3 +183,36 @@ func (r *registry) ListClients() map[string]acmecl.Interface {
 	}
 	return out
 }
+
+func NewClient(config cmacme.ACMEIssuer, privateKey *rsa.PrivateKey) acmecl.Interface {
+	return &acmeapi.Client{
+		Key:          privateKey,
+		HTTPClient:   buildHTTPClient(config.SkipTLSVerify),
+		DirectoryURL: config.Server,
+		UserAgent:    util.CertManagerUserAgent,
+	}
+}
+
+// buildHTTPClient returns an HTTP client to be used by the ACME client.
+// For the time being, we construct a new HTTP client on each invocation.
+// This is because we need to set the 'skipTLSVerify' flag on the HTTP client
+// itself.
+// In future, we may change to having two global HTTP clients - one that ignores
+// TLS connection errors, and the other that does not.
+func buildHTTPClient(skipTLSVerify bool) *http.Client {
+	return acmecl.NewInstrumentedClient(&http.Client{
+		Transport: &http.Transport{
+			Proxy: http.ProxyFromEnvironment,
+			DialContext: (&net.Dialer{
+				Timeout:   30 * time.Second,
+				KeepAlive: 30 * time.Second,
+			}).DialContext,
+			TLSClientConfig:       &tls.Config{InsecureSkipVerify: skipTLSVerify},
+			MaxIdleConns:          100,
+			IdleConnTimeout:       90 * time.Second,
+			TLSHandshakeTimeout:   10 * time.Second,
+			ExpectContinueTimeout: 1 * time.Second,
+		},
+		Timeout: time.Second * 30,
+	})
+}
diff --git a/pkg/acme/client/http.go b/pkg/acme/client/http.go
index ead70621d..d344bc563 100644
--- a/pkg/acme/client/http.go
+++ b/pkg/acme/client/http.go
@@ -36,41 +36,30 @@ import (
 // request it processes. It allows to be configured with callbacks that process
 // request path and query into a suitable label value.
 type Transport struct {
-	metrics *metrics.Metrics
-
-	wrappedRT http.RoundTripper
+	next http.RoundTripper
 }
 
-// NewInstrumentedClient takes a *http.Client and returns a *http.Client that
-// has its RoundTripper wrapped with instrumentation.
-func NewInstrumentedClient(metrics *metrics.Metrics, client *http.Client) *http.Client {
-	// If next client is not defined we'll use http.DefaultClient.
-	if client == nil {
-		client = http.DefaultClient
-	}
-
-	if client.Transport == nil {
-		client.Transport = http.DefaultTransport
-	}
-
-	client.Transport = &Transport{
-		wrappedRT: client.Transport,
-		metrics:   metrics,
+// pathProcessor will trim the provided path to only include the first 2
+// segments in order to reduce the number of prometheus labels generated
+func pathProcessor(path string) string {
+	p := strings.Split(path, "/")
+	// only record the first two path segments as a prometheus label value
+	if len(p) > 3 {
+		p = p[:3]
 	}
-
-	return client
+	return strings.Join(p, "/")
 }
 
 // RoundTrip implements http.RoundTripper. It forwards the request to the
-// wrapped RoundTripper and measures the time it took in Prometheus summary.
+// next RoundTripper and measures the time it took in Prometheus summary.
 func (it *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
 	statusCode := 999
 
 	// Remember the current time.
-	start := time.Now()
+	now := time.Now()
 
-	// Make the request using the wrappd RoundTripper.
-	resp, err := it.wrappedRT.RoundTrip(req)
+	// Make the request using the next RoundTripper.
+	resp, err := it.next.RoundTrip(req)
 	if resp != nil {
 		statusCode = resp.StatusCode
 	}
@@ -83,20 +72,37 @@ func (it *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
 		fmt.Sprintf("%d", statusCode),
 	}
 	// Observe the time it took to make the request.
-	it.metrics.ObserveACMERequestDuration(time.Since(start), labels...)
-	it.metrics.IncrementACMERequestCount(labels...)
+	metrics.Default.ACMEClientRequestDurationSeconds.
+		WithLabelValues(labels...).
+		Observe(time.Since(now).Seconds())
+
+	metrics.Default.ACMEClientRequestCount.
+		WithLabelValues(labels...).Inc()
 
 	// return the response and error reported from the next RoundTripper.
 	return resp, err
 }
 
-// pathProcessor will trim the provided path to only include the first 2
-// segments in order to reduce the number of prometheus labels generated
-func pathProcessor(path string) string {
-	p := strings.Split(path, "/")
-	// only record the first two path segments as a prometheus label value
-	if len(p) > 3 {
-		p = p[:3]
+// NewInstrumentedClient takes a *http.Client and returns a *http.Client that
+// has its RoundTripper wrapped with instrumentation.
+func NewInstrumentedClient(next *http.Client) *http.Client {
+	// If next client is not defined we'll use http.DefaultClient.
+	if next == nil {
+		next = http.DefaultClient
 	}
-	return strings.Join(p, "/")
+
+	next.Transport = newTransport(next.Transport)
+
+	return next
+}
+
+// NewTransport takes a http.RoundTripper, wraps it with instrumentation and
+// returns it as a new http.RoundTripper.
+func newTransport(next http.RoundTripper) http.RoundTripper {
+	// If next RoundTripper is not defined we'll use http.DefaultTransport.
+	if next == nil {
+		next = http.DefaultTransport
+	}
+
+	return &Transport{next: next}
 }
diff --git a/pkg/controller/BUILD.bazel b/pkg/controller/BUILD.bazel
index d6bd464f7..75b4c43f2 100644
--- a/pkg/controller/BUILD.bazel
+++ b/pkg/controller/BUILD.bazel
@@ -18,7 +18,6 @@ go_library(
         "//pkg/client/clientset/versioned:go_default_library",
         "//pkg/client/informers/externalversions:go_default_library",
         "//pkg/logs:go_default_library",
-        "//pkg/metrics:go_default_library",
         "@com_github_go_logr_logr//:go_default_library",
         "@io_k8s_apimachinery//pkg/api/resource:go_default_library",
         "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
diff --git a/pkg/controller/acmechallenges/BUILD.bazel b/pkg/controller/acmechallenges/BUILD.bazel
index d561e2454..68b84f9eb 100644
--- a/pkg/controller/acmechallenges/BUILD.bazel
+++ b/pkg/controller/acmechallenges/BUILD.bazel
@@ -26,6 +26,7 @@ go_library(
         "//pkg/issuer/acme/dns/util:go_default_library",
         "//pkg/issuer/acme/http:go_default_library",
         "//pkg/logs:go_default_library",
+        "//pkg/metrics:go_default_library",
         "//pkg/util/feature:go_default_library",
         "@com_github_go_logr_logr//:go_default_library",
         "@io_k8s_api//core/v1:go_default_library",
diff --git a/pkg/controller/acmechallenges/sync.go b/pkg/controller/acmechallenges/sync.go
index af3729ed0..10ce66c96 100644
--- a/pkg/controller/acmechallenges/sync.go
+++ b/pkg/controller/acmechallenges/sync.go
@@ -35,6 +35,7 @@ import (
 	"github.com/jetstack/cert-manager/pkg/feature"
 	dnsutil "github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
 	logf "github.com/jetstack/cert-manager/pkg/logs"
+	"github.com/jetstack/cert-manager/pkg/metrics"
 	utilfeature "github.com/jetstack/cert-manager/pkg/util/feature"
 )
 
@@ -58,9 +59,10 @@ type solver interface {
 // Sync will process this ACME Challenge.
 // It is the core control function for ACME challenges.
 func (c *controller) Sync(ctx context.Context, ch *cmacme.Challenge) (err error) {
+	metrics.Default.IncrementSyncCallCount(ControllerName)
+
 	log := logf.FromContext(ctx).WithValues("dnsName", ch.Spec.DNSName, "type", ch.Spec.Type)
 	ctx = logf.NewContext(ctx, log)
-
 	oldChal := ch
 	ch = ch.DeepCopy()
 
diff --git a/pkg/controller/acmeorders/BUILD.bazel b/pkg/controller/acmeorders/BUILD.bazel
index a06ef591b..ce5c76690 100644
--- a/pkg/controller/acmeorders/BUILD.bazel
+++ b/pkg/controller/acmeorders/BUILD.bazel
@@ -23,6 +23,7 @@ go_library(
         "//pkg/controller/acmeorders/selectors:go_default_library",
         "//pkg/issuer:go_default_library",
         "//pkg/logs:go_default_library",
+        "//pkg/metrics:go_default_library",
         "@com_github_go_logr_logr//:go_default_library",
         "@io_k8s_api//core/v1:go_default_library",
         "@io_k8s_apimachinery//pkg/api/errors:go_default_library",
diff --git a/pkg/controller/acmeorders/sync.go b/pkg/controller/acmeorders/sync.go
index 6e24d0dfc..6d61baac1 100644
--- a/pkg/controller/acmeorders/sync.go
+++ b/pkg/controller/acmeorders/sync.go
@@ -35,12 +35,15 @@ import (
 	acmecl "github.com/jetstack/cert-manager/pkg/acme/client"
 	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2"
 	logf "github.com/jetstack/cert-manager/pkg/logs"
+	"github.com/jetstack/cert-manager/pkg/metrics"
 )
 
 func (c *controller) Sync(ctx context.Context, o *cmacme.Order) (err error) {
 	log := logf.FromContext(ctx)
 	dbg := log.V(logf.DebugLevel)
 
+	metrics.Default.IncrementSyncCallCount(ControllerName)
+
 	oldOrder := o
 	o = o.DeepCopy()
 
diff --git a/pkg/controller/builder.go b/pkg/controller/builder.go
index 039144529..15b1d7886 100644
--- a/pkg/controller/builder.go
+++ b/pkg/controller/builder.go
@@ -31,9 +31,6 @@ type Builder struct {
 	// the queueingController
 	context *Context
 
-	// name is the name for this controller
-	name string
-
 	// a reference to the root context for this controller, used
 	// as a basis for other contexts and for logging
 	ctx context.Context
@@ -57,7 +54,6 @@ func NewBuilder(controllerctx *Context, name string) *Builder {
 	return &Builder{
 		context: controllerctx,
 		ctx:     ctx,
-		name:    name,
 	}
 }
 
@@ -97,5 +93,5 @@ func (b *Builder) Complete() (Interface, error) {
 		return nil, fmt.Errorf("error registering controller: %v", err)
 	}
 
-	return NewController(b.ctx, b.name, b.context.Metrics, b.impl.ProcessItem, mustSync, b.runDurationFuncs, queue), nil
+	return NewController(b.ctx, b.impl.ProcessItem, mustSync, b.runDurationFuncs, queue), nil
 }
diff --git a/pkg/controller/certificaterequests/BUILD.bazel b/pkg/controller/certificaterequests/BUILD.bazel
index eff087989..517b84281 100644
--- a/pkg/controller/certificaterequests/BUILD.bazel
+++ b/pkg/controller/certificaterequests/BUILD.bazel
@@ -21,6 +21,7 @@ go_library(
         "//pkg/internal/apis/certmanager:go_default_library",
         "//pkg/issuer:go_default_library",
         "//pkg/logs:go_default_library",
+        "//pkg/metrics:go_default_library",
         "//pkg/util/pki:go_default_library",
         "//pkg/webhook:go_default_library",
         "@com_github_go_logr_logr//:go_default_library",
diff --git a/pkg/controller/certificaterequests/controller.go b/pkg/controller/certificaterequests/controller.go
index bd4928ca8..955d23d1f 100644
--- a/pkg/controller/certificaterequests/controller.go
+++ b/pkg/controller/certificaterequests/controller.go
@@ -33,6 +33,7 @@ import (
 	"github.com/jetstack/cert-manager/pkg/controller/certificaterequests/util"
 	"github.com/jetstack/cert-manager/pkg/issuer"
 	logf "github.com/jetstack/cert-manager/pkg/logs"
+	"github.com/jetstack/cert-manager/pkg/metrics"
 )
 
 const (
@@ -53,7 +54,8 @@ type Controller struct {
 
 	certificateRequestLister cmlisters.CertificateRequestLister
 
-	queue workqueue.RateLimitingInterface
+	queue   workqueue.RateLimitingInterface
+	metrics *metrics.Metrics
 
 	// logger to be used by this controller
 	log logr.Logger
@@ -154,6 +156,9 @@ func (c *Controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
 		})
 	}
 
+	// instantiate metrics interface with default metrics implementation
+	c.metrics = metrics.Default
+
 	// create an issuer helper for reading generic issuers
 	c.helper = issuer.NewHelper(c.issuerLister, c.clusterIssuerLister)
 
diff --git a/pkg/controller/certificaterequests/sync.go b/pkg/controller/certificaterequests/sync.go
index 599ec4952..53512fa39 100644
--- a/pkg/controller/certificaterequests/sync.go
+++ b/pkg/controller/certificaterequests/sync.go
@@ -42,6 +42,8 @@ var (
 )
 
 func (c *Controller) Sync(ctx context.Context, cr *v1alpha2.CertificateRequest) (err error) {
+	c.metrics.IncrementSyncCallCount(ControllerName)
+
 	log := logf.FromContext(ctx)
 	dbg := log.V(logf.DebugLevel)
 
@@ -124,6 +126,8 @@ func (c *Controller) Sync(ctx context.Context, cr *v1alpha2.CertificateRequest)
 		return nil
 	}
 
+	// TODO: Metrics??
+
 	dbg.Info("invoking sign function as existing certificate does not exist")
 
 	// Attempt to call the Sign function on our issuer
diff --git a/pkg/controller/certificates/BUILD.bazel b/pkg/controller/certificates/BUILD.bazel
index 83b739592..c582d5ce1 100644
--- a/pkg/controller/certificates/BUILD.bazel
+++ b/pkg/controller/certificates/BUILD.bazel
@@ -19,6 +19,7 @@ go_library(
         "//pkg/client/listers/certmanager/v1alpha2:go_default_library",
         "//pkg/controller:go_default_library",
         "//pkg/logs:go_default_library",
+        "//pkg/metrics:go_default_library",
         "//pkg/scheduler:go_default_library",
         "//pkg/util:go_default_library",
         "//pkg/util/errors:go_default_library",
@@ -78,10 +79,7 @@ filegroup(
 
 filegroup(
     name = "all-srcs",
-    srcs = [
-        ":package-srcs",
-        "//pkg/controller/certificates/metrics:all-srcs",
-    ],
+    srcs = [":package-srcs"],
     tags = ["automanaged"],
     visibility = ["//visibility:public"],
 )
diff --git a/pkg/controller/certificates/metrics/BUILD.bazel b/pkg/controller/certificates/metrics/BUILD.bazel
deleted file mode 100644
index fe615c47d..000000000
--- a/pkg/controller/certificates/metrics/BUILD.bazel
+++ /dev/null
@@ -1,32 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["controller.go"],
-    importpath = "github.com/jetstack/cert-manager/pkg/controller/certificates/metrics",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//pkg/client/informers/externalversions:go_default_library",
-        "//pkg/client/listers/certmanager/v1alpha2:go_default_library",
-        "//pkg/controller:go_default_library",
-        "//pkg/metrics:go_default_library",
-        "@io_k8s_apimachinery//pkg/api/errors:go_default_library",
-        "@io_k8s_client_go//informers:go_default_library",
-        "@io_k8s_client_go//tools/cache:go_default_library",
-        "@io_k8s_client_go//util/workqueue:go_default_library",
-    ],
-)
-
-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/pkg/controller/certificates/metrics/controller.go b/pkg/controller/certificates/metrics/controller.go
deleted file mode 100644
index 479e360cd..000000000
--- a/pkg/controller/certificates/metrics/controller.go
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-Copyright 2020 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 metrics
-
-import (
-	"context"
-	"time"
-
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	"k8s.io/client-go/informers"
-	"k8s.io/client-go/tools/cache"
-	"k8s.io/client-go/util/workqueue"
-
-	cminformers "github.com/jetstack/cert-manager/pkg/client/informers/externalversions"
-	cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha2"
-	controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
-	"github.com/jetstack/cert-manager/pkg/metrics"
-)
-
-const (
-	ControllerName = "CertificateMetrics"
-)
-
-// controllerWrapper wraps the `controller` structure to make it implement
-// the controllerpkg.queueingController interface
-type controllerWrapper struct {
-	*controller
-}
-
-// This controller is synced on all Certificate 'create', 'update', and
-// 'delete' events which will update the metrics for that Certificate.
-type controller struct {
-	certificateLister cmlisters.CertificateLister
-
-	metrics *metrics.Metrics
-}
-
-func NewController(
-	factory informers.SharedInformerFactory,
-	cmFactory cminformers.SharedInformerFactory,
-	metrics *metrics.Metrics,
-) (*controller, workqueue.RateLimitingInterface, []cache.InformerSynced) {
-	// create a queue used to queue up items to be processed
-	queue := workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(time.Second*1, time.Second*30), ControllerName)
-
-	// obtain references to all the informers used by this controller
-	certificateInformer := cmFactory.Certmanager().V1alpha2().Certificates()
-
-	// Reconcile over all Certificate events. We do _not_ reconcile on Secret
-	// events that are related to Certificates. It is the responsibility of the
-	// Certificates controllers to update accordingly.
-	certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue})
-
-	// build a list of InformerSynced functions that will be returned by the
-	// Register method.  the controller will only begin processing items once all
-	// of these informers have synced.
-	mustSync := []cache.InformerSynced{
-		certificateInformer.Informer().HasSynced,
-	}
-
-	return &controller{
-		certificateLister: certificateInformer.Lister(),
-		metrics:           metrics,
-	}, queue, mustSync
-}
-
-func (c *controller) ProcessItem(ctx context.Context, key string) error {
-	// Set context deadline for full sync in 10 seconds
-	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
-	defer cancel()
-
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
-	if err != nil {
-		return nil
-	}
-
-	crt, err := c.certificateLister.Certificates(namespace).Get(name)
-	if apierrors.IsNotFound(err) {
-		// If the Certificate no longer exists, remove it's metrics from being exposed.
-		c.metrics.RemoveCertificate(key)
-		return nil
-	}
-	if err != nil {
-		return err
-	}
-
-	// Update that Certificates metrics
-	c.metrics.UpdateCertificate(ctx, crt)
-
-	return nil
-}
-
-func (c *controllerWrapper) Register(ctx *controllerpkg.Context) (workqueue.RateLimitingInterface, []cache.InformerSynced, error) {
-	ctrl, queue, mustSync := NewController(
-		ctx.KubeSharedInformerFactory,
-		ctx.SharedInformerFactory,
-		ctx.Metrics,
-	)
-	c.controller = ctrl
-
-	return queue, mustSync, nil
-}
-
-func init() {
-	controllerpkg.Register(ControllerName, func(ctx *controllerpkg.Context) (controllerpkg.Interface, error) {
-		return controllerpkg.NewBuilder(ctx, ControllerName).
-			For(&controllerWrapper{}).
-			Complete()
-	})
-}
diff --git a/pkg/controller/certificates/sync.go b/pkg/controller/certificates/sync.go
index 0f49855cf..e3a566bc9 100644
--- a/pkg/controller/certificates/sync.go
+++ b/pkg/controller/certificates/sync.go
@@ -40,6 +40,7 @@ import (
 	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
 	cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha2"
 	logf "github.com/jetstack/cert-manager/pkg/logs"
+	"github.com/jetstack/cert-manager/pkg/metrics"
 	"github.com/jetstack/cert-manager/pkg/util/errors"
 	"github.com/jetstack/cert-manager/pkg/util/kube"
 	"github.com/jetstack/cert-manager/pkg/util/pki"
@@ -65,6 +66,9 @@ func (c *certificateRequestManager) ProcessItem(ctx context.Context, key string)
 	ctx = logf.NewContext(ctx, log)
 	updatedCert := crt.DeepCopy()
 
+	defer metrics.Default.UpdateCertificateExpiry(updatedCert, c.secretLister)
+	defer metrics.Default.UpdateCertificateStatus(updatedCert)
+
 	err = c.processCertificate(ctx, updatedCert)
 	log.V(logf.DebugLevel).Info("check if certificate status update is required")
 	updateStatusErr := c.updateCertificateStatus(ctx, crt, updatedCert)
diff --git a/pkg/controller/clusterissuers/BUILD.bazel b/pkg/controller/clusterissuers/BUILD.bazel
index 4b07ef47a..2f601c51c 100644
--- a/pkg/controller/clusterissuers/BUILD.bazel
+++ b/pkg/controller/clusterissuers/BUILD.bazel
@@ -19,6 +19,7 @@ go_library(
         "//pkg/internal/apis/certmanager:go_default_library",
         "//pkg/issuer:go_default_library",
         "//pkg/logs:go_default_library",
+        "//pkg/metrics:go_default_library",
         "//pkg/webhook:go_default_library",
         "@com_github_go_logr_logr//:go_default_library",
         "@io_k8s_api//core/v1:go_default_library",
diff --git a/pkg/controller/clusterissuers/sync.go b/pkg/controller/clusterissuers/sync.go
index 15665ee5f..65fb29b23 100644
--- a/pkg/controller/clusterissuers/sync.go
+++ b/pkg/controller/clusterissuers/sync.go
@@ -31,6 +31,7 @@ import (
 	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
 	internalapi "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager"
 	logf "github.com/jetstack/cert-manager/pkg/logs"
+	"github.com/jetstack/cert-manager/pkg/metrics"
 	"github.com/jetstack/cert-manager/pkg/webhook"
 )
 
@@ -42,6 +43,8 @@ const (
 )
 
 func (c *controller) Sync(ctx context.Context, iss *v1alpha2.ClusterIssuer) (err error) {
+	metrics.Default.IncrementSyncCallCount(ControllerName)
+
 	log := logf.FromContext(ctx)
 
 	issuerCopy := iss.DeepCopy()
diff --git a/pkg/controller/context.go b/pkg/controller/context.go
index c3f370aff..d1ac4283e 100644
--- a/pkg/controller/context.go
+++ b/pkg/controller/context.go
@@ -30,7 +30,6 @@ import (
 	"github.com/jetstack/cert-manager/pkg/acme/accounts"
 	clientset "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
 	informers "github.com/jetstack/cert-manager/pkg/client/informers/externalversions"
-	"github.com/jetstack/cert-manager/pkg/metrics"
 )
 
 // Context contains various types that are used by controller implementations.
@@ -68,9 +67,6 @@ type Context struct {
 	// time.Now, to make it easier to test controllers that utilise time
 	Clock clock.Clock
 
-	// Metrics is used for exposing Prometheus metrics across the controllers
-	Metrics *metrics.Metrics
-
 	IssuerOptions
 	ACMEOptions
 	IngressShimOptions
diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go
index 7f9b2573e..f2c13f282 100644
--- a/pkg/controller/controller.go
+++ b/pkg/controller/controller.go
@@ -27,7 +27,6 @@ import (
 	"k8s.io/client-go/util/workqueue"
 
 	logf "github.com/jetstack/cert-manager/pkg/logs"
-	"github.com/jetstack/cert-manager/pkg/metrics"
 )
 
 type runFunc func(context.Context)
@@ -44,8 +43,6 @@ type queueingController interface {
 
 func NewController(
 	ctx context.Context,
-	name string,
-	metrics *metrics.Metrics,
 	syncFunc func(ctx context.Context, key string) error,
 	mustSync []cache.InformerSynced,
 	runDurationFuncs []runDurationFunc,
@@ -53,8 +50,6 @@ func NewController(
 ) Interface {
 	return &controller{
 		ctx:              ctx,
-		name:             name,
-		metrics:          metrics,
 		syncHandler:      syncFunc,
 		mustSync:         mustSync,
 		runDurationFuncs: runDurationFuncs,
@@ -66,9 +61,6 @@ type controller struct {
 	// ctx is the root golang context for the controller
 	ctx context.Context
 
-	// name is the name for this controller
-	name string
-
 	// the function that should be called when an item is popped
 	// off the workqueue
 	syncHandler func(ctx context.Context, key string) error
@@ -86,9 +78,6 @@ type controller struct {
 	// queue is a reference to the queue used to enqueue resources
 	// to be processed
 	queue workqueue.RateLimitingInterface
-
-	// metrics is used to expose Prometheus, shared by all controllers
-	metrics *metrics.Metrics
 }
 
 // Run starts the controller loop
@@ -150,10 +139,6 @@ func (b *controller) worker(ctx context.Context) {
 			}
 			log := log.WithValues("key", key)
 			log.Info("syncing item")
-
-			// Increase sync count for this controller
-			b.metrics.IncrementSyncCallCount(b.name)
-
 			if err := b.syncHandler(ctx, key); err != nil {
 				log.Error(err, "re-queuing item  due to error processing")
 				b.queue.AddRateLimited(obj)
diff --git a/pkg/controller/expcertificates/issuing/issuing_controller.go b/pkg/controller/expcertificates/issuing/issuing_controller.go
index c44d28206..233961c82 100644
--- a/pkg/controller/expcertificates/issuing/issuing_controller.go
+++ b/pkg/controller/expcertificates/issuing/issuing_controller.go
@@ -18,7 +18,6 @@ package issuing
 
 import (
 	"context"
-	"crypto"
 	"fmt"
 	"time"
 
@@ -147,7 +146,6 @@ func (c *controller) ProcessItem(ctx context.Context, key string) error {
 	defer cancel()
 
 	log := logf.FromContext(ctx).WithValues("key", key)
-
 	namespace, name, err := cache.SplitMetaNamespaceKey(key)
 	if err != nil {
 		return nil
@@ -193,7 +191,7 @@ func (c *controller) ProcessItem(ctx context.Context, key string) error {
 		logf.WithResource(log, nextPrivateKeySecret).Info("Next private key secret does not contain any private key data, waiting for keymanager controller")
 		return nil
 	}
-	pk, _, err := utilkube.ParseTLSKeyFromSecret(nextPrivateKeySecret, corev1.TLSPrivateKeyKey)
+	pk, pkData, err := utilkube.ParseTLSKeyFromSecret(nextPrivateKeySecret, corev1.TLSPrivateKeyKey)
 	if err != nil {
 		// If the private key cannot be parsed here, do nothing as the key manager will handle this.
 		logf.WithResource(log, nextPrivateKeySecret).Error(err, "failed to parse next private key, waiting for keymanager controller")
@@ -271,13 +269,13 @@ func (c *controller) ProcessItem(ctx context.Context, key string) error {
 	// If the CertificateRequest is valid and ready, verify its status and issue
 	// accordingly.
 	if cond.Reason == cmapi.CertificateRequestReasonIssued {
-		return c.issueCertificate(ctx, nextRevision, crt, req, pk)
+		return c.issueCertificate(ctx, nextRevision, crt, req, pkData)
 	}
 
 	// Issue temporary certificate if needed. If a certificate was issued, then
 	// return early - we will sync again since the target Secret has been
 	// updated.
-	if issued, err := c.ensureTemporaryCertificate(ctx, crt, pk); err != nil || issued {
+	if issued, err := c.ensureTemporaryCertificate(ctx, crt, pkData); err != nil || issued {
 		return err
 	}
 
@@ -316,18 +314,14 @@ func (c *controller) failIssueCertificate(ctx context.Context, log logr.Logger,
 // issueCertificate will ensure the public key of the CSR matches the signed
 // certificate, and then store the certificate, CA and private key into the
 // Secret in the appropriate format type.
-func (c *controller) issueCertificate(ctx context.Context, nextRevision int, crt *cmapi.Certificate, req *cmapi.CertificateRequest, pk crypto.Signer) error {
-	pkData, err := utilpki.EncodePrivateKey(pk, crt.Spec.KeyEncoding)
-	if err != nil {
-		return err
-	}
+func (c *controller) issueCertificate(ctx context.Context, nextRevision int, crt *cmapi.Certificate, req *cmapi.CertificateRequest, pkData []byte) error {
 	secretData := secretsmanager.SecretData{
 		PrivateKey:  pkData,
 		Certificate: req.Status.Certificate,
 		CA:          req.Status.CA,
 	}
 
-	err = c.secretsManager.UpdateData(ctx, crt, secretData)
+	err := c.secretsManager.UpdateData(ctx, crt, secretData)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/controller/expcertificates/issuing/temporary.go b/pkg/controller/expcertificates/issuing/temporary.go
index 3a7d59471..c92f2f45b 100644
--- a/pkg/controller/expcertificates/issuing/temporary.go
+++ b/pkg/controller/expcertificates/issuing/temporary.go
@@ -18,7 +18,6 @@ package issuing
 
 import (
 	"context"
-	"crypto"
 
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -26,7 +25,6 @@ import (
 	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
 	"github.com/jetstack/cert-manager/pkg/controller/expcertificates/internal/secretsmanager"
 	"github.com/jetstack/cert-manager/pkg/controller/expcertificates/trigger/policies"
-	utilpki "github.com/jetstack/cert-manager/pkg/util/pki"
 )
 
 var temporaryCertificatePolicyChain = policies.Chain{
@@ -41,7 +39,7 @@ var temporaryCertificatePolicyChain = policies.Chain{
 // - The target Secret does not exist yet, or the certificate/key data there is not valid
 // - If the Certificate/Key pair does not match the 'NextPrivateKey'
 // Returns true is a temporary certificate was issued
-func (c *controller) ensureTemporaryCertificate(ctx context.Context, crt *cmapi.Certificate, pk crypto.Signer) (bool, error) {
+func (c *controller) ensureTemporaryCertificate(ctx context.Context, crt *cmapi.Certificate, pkData []byte) (bool, error) {
 	// If certificate does not have temporary certificate annotation, do nothing
 	if !certificateHasTemporaryCertificateAnnotation(crt) {
 		return false, nil
@@ -60,10 +58,6 @@ func (c *controller) ensureTemporaryCertificate(ctx context.Context, crt *cmapi.
 	}
 
 	// Issue temporary certificate
-	pkData, err := utilpki.EncodePrivateKey(pk, crt.Spec.KeyEncoding)
-	if err != nil {
-		return false, err
-	}
 	certData, err := c.localTemporarySigner(crt, pkData)
 	if err != nil {
 		return false, err
diff --git a/pkg/controller/expcertificates/keymanager/keymanager_controller.go b/pkg/controller/expcertificates/keymanager/keymanager_controller.go
index 121b8ab0a..93249e3eb 100644
--- a/pkg/controller/expcertificates/keymanager/keymanager_controller.go
+++ b/pkg/controller/expcertificates/keymanager/keymanager_controller.go
@@ -125,7 +125,6 @@ func init() {
 func (c *controller) ProcessItem(ctx context.Context, key string) error {
 	log := logf.FromContext(ctx).WithValues("key", key)
 	ctx = logf.NewContext(ctx, log)
-
 	namespace, name, err := cache.SplitMetaNamespaceKey(key)
 	if err != nil {
 		log.Error(err, "invalid resource key passed to ProcessItem")
diff --git a/pkg/controller/expcertificates/readiness/readiness_controller.go b/pkg/controller/expcertificates/readiness/readiness_controller.go
index 9bf2106cc..82e01451d 100644
--- a/pkg/controller/expcertificates/readiness/readiness_controller.go
+++ b/pkg/controller/expcertificates/readiness/readiness_controller.go
@@ -121,7 +121,6 @@ func NewController(
 
 func (c *controller) ProcessItem(ctx context.Context, key string) error {
 	log := logf.FromContext(ctx).WithValues("key", key)
-
 	ctx = logf.NewContext(ctx, log)
 	namespace, name, err := cache.SplitMetaNamespaceKey(key)
 	if err != nil {
diff --git a/pkg/controller/expcertificates/requestmanager/requestmanager_controller.go b/pkg/controller/expcertificates/requestmanager/requestmanager_controller.go
index 2d2190231..20f76d6ca 100644
--- a/pkg/controller/expcertificates/requestmanager/requestmanager_controller.go
+++ b/pkg/controller/expcertificates/requestmanager/requestmanager_controller.go
@@ -114,7 +114,6 @@ func NewController(
 
 func (c *controller) ProcessItem(ctx context.Context, key string) error {
 	log := logf.FromContext(ctx).WithValues("key", key)
-
 	ctx = logf.NewContext(ctx, log)
 	namespace, name, err := cache.SplitMetaNamespaceKey(key)
 	if err != nil {
diff --git a/pkg/controller/ingress-shim/BUILD.bazel b/pkg/controller/ingress-shim/BUILD.bazel
index a2866877c..855b95012 100644
--- a/pkg/controller/ingress-shim/BUILD.bazel
+++ b/pkg/controller/ingress-shim/BUILD.bazel
@@ -18,6 +18,7 @@ go_library(
         "//pkg/controller:go_default_library",
         "//pkg/issuer:go_default_library",
         "//pkg/logs:go_default_library",
+        "//pkg/metrics:go_default_library",
         "@com_github_go_logr_logr//:go_default_library",
         "@io_k8s_api//core/v1:go_default_library",
         "@io_k8s_api//extensions/v1beta1:go_default_library",
diff --git a/pkg/controller/ingress-shim/sync.go b/pkg/controller/ingress-shim/sync.go
index 4f223c443..d561b9c49 100644
--- a/pkg/controller/ingress-shim/sync.go
+++ b/pkg/controller/ingress-shim/sync.go
@@ -34,6 +34,7 @@ import (
 	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
 	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
 	"github.com/jetstack/cert-manager/pkg/logs"
+	"github.com/jetstack/cert-manager/pkg/metrics"
 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 )
 
@@ -43,6 +44,8 @@ func (c *controller) Sync(ctx context.Context, ing *extv1beta1.Ingress) error {
 	log := logs.WithResource(logs.FromContext(ctx), ing)
 	ctx = logs.NewContext(ctx, log)
 
+	metrics.Default.IncrementSyncCallCount(ControllerName)
+
 	if !shouldSync(ing, c.defaults.autoCertificateAnnotations) {
 		log.Info(fmt.Sprintf("not syncing ingress resource as it does not contain a %q or %q annotation",
 			cmapi.IngressIssuerNameAnnotationKey, cmapi.IngressClusterIssuerNameAnnotationKey))
diff --git a/pkg/controller/issuers/BUILD.bazel b/pkg/controller/issuers/BUILD.bazel
index 2ad81e033..37542edd7 100644
--- a/pkg/controller/issuers/BUILD.bazel
+++ b/pkg/controller/issuers/BUILD.bazel
@@ -19,6 +19,7 @@ go_library(
         "//pkg/internal/apis/certmanager:go_default_library",
         "//pkg/issuer:go_default_library",
         "//pkg/logs:go_default_library",
+        "//pkg/metrics:go_default_library",
         "//pkg/webhook:go_default_library",
         "@com_github_go_logr_logr//:go_default_library",
         "@io_k8s_api//core/v1:go_default_library",
diff --git a/pkg/controller/issuers/sync.go b/pkg/controller/issuers/sync.go
index 146fc9502..71cc6a155 100644
--- a/pkg/controller/issuers/sync.go
+++ b/pkg/controller/issuers/sync.go
@@ -31,6 +31,7 @@ import (
 	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
 	internalapi "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager"
 	logf "github.com/jetstack/cert-manager/pkg/logs"
+	"github.com/jetstack/cert-manager/pkg/metrics"
 	"github.com/jetstack/cert-manager/pkg/webhook"
 )
 
@@ -42,6 +43,8 @@ const (
 )
 
 func (c *controller) Sync(ctx context.Context, iss *v1alpha2.Issuer) (err error) {
+	metrics.Default.IncrementSyncCallCount(ControllerName)
+
 	log := logf.FromContext(ctx)
 
 	issuerCopy := iss.DeepCopy()
diff --git a/pkg/ctl/BUILD.bazel b/pkg/ctl/BUILD.bazel
new file mode 100644
index 000000000..4f7f27ae4
--- /dev/null
+++ b/pkg/ctl/BUILD.bazel
@@ -0,0 +1,33 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["scheme.go"],
+    importpath = "github.com/jetstack/cert-manager/pkg/ctl",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//pkg/internal/apis/acme/install:go_default_library",
+        "//pkg/internal/apis/certmanager/install:go_default_library",
+        "//pkg/internal/apis/meta/install:go_default_library",
+        "@io_k8s_apimachinery//pkg/apis/meta/internalversion:go_default_library",
+        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime:go_default_library",
+        "@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
+        "@io_k8s_apimachinery//pkg/util/runtime:go_default_library",
+        "@io_k8s_client_go//kubernetes/scheme:go_default_library",
+    ],
+)
+
+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/pkg/ctl/scheme.go b/pkg/ctl/scheme.go
new file mode 100644
index 000000000..f4771142f
--- /dev/null
+++ b/pkg/ctl/scheme.go
@@ -0,0 +1,56 @@
+/*
+Copyright 2020 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.
+*/
+
+// This package was created to have a scheme that has the internal cert-manager types,
+// and their conversion functions as well as the List object type registered, which is needed for ctl command like
+// `convert` or `create certificaterequest`.
+package ctl
+
+import (
+	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	kscheme "k8s.io/client-go/kubernetes/scheme"
+
+	acmeinstall "github.com/jetstack/cert-manager/pkg/internal/apis/acme/install"
+	cminstall "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager/install"
+	metainstall "github.com/jetstack/cert-manager/pkg/internal/apis/meta/install"
+)
+
+// Define a Scheme that has all cert-manager API types registered, including
+// the internal API version, defaulting functions and conversion functions for
+// all external versions.
+
+var (
+	// Scheme is a Kubernetes runtime.Scheme with all internal and external API
+	// versions for cert-manager types registered.
+	Scheme = runtime.NewScheme()
+)
+
+func init() {
+	cminstall.Install(Scheme)
+	acmeinstall.Install(Scheme)
+	metainstall.Install(Scheme)
+
+	// This is used to add the List object type
+	coreGroupVersion := schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal}
+	Scheme.AddKnownTypes(coreGroupVersion, &metainternalversion.List{})
+
+	metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
+	utilruntime.Must(kscheme.AddToScheme(Scheme))
+}
diff --git a/pkg/issuer/acme/BUILD.bazel b/pkg/issuer/acme/BUILD.bazel
index 8229d947a..f1b78d6b6 100644
--- a/pkg/issuer/acme/BUILD.bazel
+++ b/pkg/issuer/acme/BUILD.bazel
@@ -18,7 +18,6 @@ go_library(
         "//pkg/controller:go_default_library",
         "//pkg/issuer:go_default_library",
         "//pkg/logs:go_default_library",
-        "//pkg/metrics:go_default_library",
         "//pkg/util/errors:go_default_library",
         "//pkg/util/kube:go_default_library",
         "//pkg/util/pki:go_default_library",
diff --git a/pkg/issuer/acme/acme.go b/pkg/issuer/acme/acme.go
index 1a1fba2c8..99a41d122 100644
--- a/pkg/issuer/acme/acme.go
+++ b/pkg/issuer/acme/acme.go
@@ -28,7 +28,6 @@ import (
 	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
 	"github.com/jetstack/cert-manager/pkg/controller"
 	"github.com/jetstack/cert-manager/pkg/issuer"
-	"github.com/jetstack/cert-manager/pkg/metrics"
 )
 
 // Acme is an issuer for an ACME server. It can be used to register and obtain
@@ -45,9 +44,6 @@ type Acme struct {
 	clusterResourceNamespace string
 	// used as a cache for ACME clients
 	accountRegistry accounts.Registry
-
-	// metrics is used to create instrumented ACME clients
-	metrics *metrics.Metrics
 }
 
 // New returns a new ACME issuer interface for the given issuer.
@@ -65,7 +61,6 @@ func New(ctx *controller.Context, issuer v1alpha2.GenericIssuer) (issuer.Interfa
 		recorder:                 ctx.Recorder,
 		clusterResourceNamespace: ctx.IssuerOptions.ClusterResourceNamespace,
 		accountRegistry:          ctx.ACMEOptions.AccountRegistry,
-		metrics:                  ctx.Metrics,
 	}
 
 	return a, nil
diff --git a/pkg/issuer/acme/dns/route53/BUILD.bazel b/pkg/issuer/acme/dns/route53/BUILD.bazel
index 7c0f0c4f9..c66d421ff 100644
--- a/pkg/issuer/acme/dns/route53/BUILD.bazel
+++ b/pkg/issuer/acme/dns/route53/BUILD.bazel
@@ -10,6 +10,7 @@ go_library(
         "//pkg/util:go_default_library",
         "@com_github_aws_aws_sdk_go//aws:go_default_library",
         "@com_github_aws_aws_sdk_go//aws/awserr:go_default_library",
+        "@com_github_aws_aws_sdk_go//aws/client:go_default_library",
         "@com_github_aws_aws_sdk_go//aws/credentials:go_default_library",
         "@com_github_aws_aws_sdk_go//aws/request:go_default_library",
         "@com_github_aws_aws_sdk_go//aws/session:go_default_library",
diff --git a/pkg/issuer/acme/dns/route53/route53.go b/pkg/issuer/acme/dns/route53/route53.go
index 14240285e..be7b5b134 100644
--- a/pkg/issuer/acme/dns/route53/route53.go
+++ b/pkg/issuer/acme/dns/route53/route53.go
@@ -12,11 +12,13 @@ package route53
 
 import (
 	"fmt"
+	"math/rand"
 	"strings"
 	"time"
 
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/awserr"
+	"github.com/aws/aws-sdk-go/aws/client"
 	"github.com/aws/aws-sdk-go/aws/credentials"
 	"github.com/aws/aws-sdk-go/aws/request"
 	"github.com/aws/aws-sdk-go/aws/session"
@@ -40,6 +42,28 @@ type DNSProvider struct {
 	hostedZoneID     string
 }
 
+// customRetryer implements the client.Retryer interface by composing the
+// DefaultRetryer. It controls the logic for retrying recoverable request
+// errors (e.g. when rate limits are exceeded).
+type customRetryer struct {
+	client.DefaultRetryer
+}
+
+// RetryRules overwrites the DefaultRetryer's method.
+// It uses a basic exponential backoff algorithm that returns an initial
+// delay of ~400ms with an upper limit of ~30 seconds which should prevent
+// causing a high number of consecutive throttling errors.
+// For reference: Route 53 enforces an account-wide(!) 5req/s query limit.
+func (d customRetryer) RetryRules(r *request.Request) time.Duration {
+	retryCount := r.RetryCount
+	if retryCount > 7 {
+		retryCount = 7
+	}
+
+	delay := (1 << uint(retryCount)) * (rand.Intn(50) + 200)
+	return time.Duration(delay) * time.Millisecond
+}
+
 type sessionProvider struct {
 	AccessKeyID     string
 	SecretAccessKey string
@@ -61,7 +85,9 @@ func (d *sessionProvider) GetSession() (*session.Session, error) {
 
 	useAmbientCredentials := d.Ambient && (d.AccessKeyID == "" && d.SecretAccessKey == "")
 
-	config := aws.NewConfig()
+	r := customRetryer{}
+	r.NumMaxRetries = maxRetries
+	config := request.WithRetryer(aws.NewConfig(), r)
 	sessionOpts := session.Options{
 		Config: *config,
 	}
diff --git a/pkg/issuer/acme/dns/util/wait.go b/pkg/issuer/acme/dns/util/wait.go
index a10c4edb2..c07fc898e 100644
--- a/pkg/issuer/acme/dns/util/wait.go
+++ b/pkg/issuer/acme/dns/util/wait.go
@@ -156,7 +156,7 @@ func DNSQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (
 		udp := &dns.Client{Net: "udp", Timeout: DNSTimeout}
 		in, _, err = udp.Exchange(m, ns)
 
-		if (in != nil && in.Truncated) ||
+		if err == dns.ErrTruncated ||
 			(err != nil && strings.HasPrefix(err.Error(), "read udp") && strings.HasSuffix(err.Error(), "i/o timeout")) {
 			klog.V(6).Infof("UDP dns lookup failed, retrying with TCP: %v", err)
 			tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout}
diff --git a/pkg/issuer/acme/setup.go b/pkg/issuer/acme/setup.go
index 40404adb6..b3acd669b 100644
--- a/pkg/issuer/acme/setup.go
+++ b/pkg/issuer/acme/setup.go
@@ -116,8 +116,7 @@ func (a *Acme) Setup(ctx context.Context) error {
 	//  In future we should intelligently manage items in the account cache
 	//  and remove them when the corresponding issuer is updated/deleted.
 	a.accountRegistry.RemoveClient(string(a.issuer.GetUID()))
-	httpClient := accounts.BuildHTTPClient(a.metrics, a.issuer.GetSpec().ACME.SkipTLSVerify)
-	cl := accounts.NewClient(httpClient, *a.issuer.GetSpec().ACME, rsaPk)
+	cl := accounts.NewClient(*a.issuer.GetSpec().ACME, rsaPk)
 
 	// TODO: perform a complex check to determine whether we need to verify
 	// the existing registration with the ACME server.
@@ -165,7 +164,7 @@ func (a *Acme) Setup(ctx context.Context) error {
 		log.Info("skipping re-verifying ACME account as cached registration " +
 			"details look sufficient")
 		// ensure the cached client in the account registry is up to date
-		a.accountRegistry.AddClient(httpClient, string(a.issuer.GetUID()), *a.issuer.GetSpec().ACME, rsaPk)
+		a.accountRegistry.AddClient(string(a.issuer.GetUID()), *a.issuer.GetSpec().ACME, rsaPk)
 		return nil
 	}
 
@@ -267,7 +266,7 @@ func (a *Acme) Setup(ctx context.Context) error {
 	a.issuer.GetStatus().ACMEStatus().URI = account.URI
 	a.issuer.GetStatus().ACMEStatus().LastRegisteredEmail = registeredEmail
 	// ensure the cached client in the account registry is up to date
-	a.accountRegistry.AddClient(httpClient, string(a.issuer.GetUID()), *a.issuer.GetSpec().ACME, rsaPk)
+	a.accountRegistry.AddClient(string(a.issuer.GetUID()), *a.issuer.GetSpec().ACME, rsaPk)
 
 	return nil
 }
diff --git a/pkg/metrics/BUILD.bazel b/pkg/metrics/BUILD.bazel
index b0057124c..fea6df740 100644
--- a/pkg/metrics/BUILD.bazel
+++ b/pkg/metrics/BUILD.bazel
@@ -2,21 +2,23 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 
 go_library(
     name = "go_default_library",
-    srcs = [
-        "acme.go",
-        "certificates.go",
-        "metrics.go",
-    ],
+    srcs = ["metrics.go"],
     importpath = "github.com/jetstack/cert-manager/pkg/metrics",
     visibility = ["//visibility:public"],
     deps = [
         "//pkg/apis/certmanager/v1alpha2:go_default_library",
         "//pkg/apis/meta/v1:go_default_library",
+        "//pkg/client/listers/certmanager/v1alpha2:go_default_library",
         "//pkg/logs:go_default_library",
-        "@com_github_go_logr_logr//:go_default_library",
+        "//pkg/util/errors:go_default_library",
+        "//pkg/util/kube:go_default_library",
         "@com_github_gorilla_mux//:go_default_library",
         "@com_github_prometheus_client_golang//prometheus:go_default_library",
         "@com_github_prometheus_client_golang//prometheus/promhttp:go_default_library",
+        "@io_k8s_apimachinery//pkg/api/errors:go_default_library",
+        "@io_k8s_apimachinery//pkg/labels:go_default_library",
+        "@io_k8s_apimachinery//pkg/util/wait:go_default_library",
+        "@io_k8s_client_go//listers/core/v1:go_default_library",
         "@io_k8s_client_go//tools/cache:go_default_library",
     ],
 )
@@ -37,13 +39,11 @@ filegroup(
 
 go_test(
     name = "go_default_test",
-    srcs = ["certificates_test.go"],
+    srcs = ["metrics_test.go"],
     embed = [":go_default_library"],
     deps = [
         "//pkg/apis/certmanager/v1alpha2:go_default_library",
         "//pkg/apis/meta/v1:go_default_library",
-        "//pkg/logs/testing:go_default_library",
-        "//test/unit/gen:go_default_library",
         "@com_github_prometheus_client_golang//prometheus/testutil:go_default_library",
         "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
     ],
diff --git a/pkg/metrics/acme.go b/pkg/metrics/acme.go
deleted file mode 100644
index ae4bf40ca..000000000
--- a/pkg/metrics/acme.go
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2020 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 metrics contains global structures related to metrics collection
-// cert-manager exposes the following metrics:
-// certificate_expiration_timestamp_seconds{name, namespace}
-// certificate_ready_status{name, namespace, condition}
-// acme_client_request_count{"scheme", "host", "path", "method", "status"}
-// acme_client_request_duration_seconds{"scheme", "host", "path", "method", "status"}
-// controller_sync_call_count{"controller"}
-package metrics
-
-import (
-	"time"
-)
-
-// ObserveACMERequestDuration increases bucket counters for that ACME client duration.
-func (m *Metrics) ObserveACMERequestDuration(duration time.Duration, labels ...string) {
-	m.acmeClientRequestDurationSeconds.WithLabelValues(labels...).Observe(duration.Seconds())
-}
-
-// IncrementACMERequestCount increases the acme client request counter.
-func (m *Metrics) IncrementACMERequestCount(labels ...string) {
-	m.acmeClientRequestCount.WithLabelValues(labels...).Inc()
-}
diff --git a/pkg/metrics/certificates.go b/pkg/metrics/certificates.go
deleted file mode 100644
index ea77f84c6..000000000
--- a/pkg/metrics/certificates.go
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
-Copyright 2020 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 metrics contains global structures related to metrics collection
-// cert-manager exposes the following metrics:
-// certificate_expiration_timestamp_seconds{name, namespace}
-// certificate_ready_status{name, namespace, condition}
-// acme_client_request_count{"scheme", "host", "path", "method", "status"}
-// acme_client_request_duration_seconds{"scheme", "host", "path", "method", "status"}
-// controller_sync_call_count{"controller"}
-package metrics
-
-import (
-	"context"
-
-	"github.com/prometheus/client_golang/prometheus"
-	"k8s.io/client-go/tools/cache"
-
-	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
-	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
-	logf "github.com/jetstack/cert-manager/pkg/logs"
-)
-
-// UpdateCertificate will update that Certificate metric with expiry and Ready
-// condition.
-func (m *Metrics) UpdateCertificate(ctx context.Context, crt *cmapi.Certificate) {
-	key, err := cache.MetaNamespaceKeyFunc(crt)
-	if err != nil {
-		log := logf.WithRelatedResource(m.log, crt)
-		log.Error(err, "failed to get key from certificate object")
-		return
-	}
-
-	m.updateCertificateStatus(key, crt)
-	m.updateCertificateExpiry(ctx, key, crt)
-}
-
-// updateCertificateExpiry updates the expiry time of a certificate
-func (m *Metrics) updateCertificateExpiry(ctx context.Context, key string, crt *cmapi.Certificate) {
-	expiryTime := 0.0
-
-	if crt.Status.NotAfter != nil {
-		expiryTime = float64(crt.Status.NotAfter.Unix())
-	}
-
-	m.certificateExpiryTimeSeconds.With(prometheus.Labels{
-		"name":      crt.Name,
-		"namespace": crt.Namespace}).Set(expiryTime)
-}
-
-// updateCertificateStatus will update the metric for that Certificate
-func (m *Metrics) updateCertificateStatus(key string, crt *cmapi.Certificate) {
-	for _, c := range crt.Status.Conditions {
-		if c.Type == cmapi.CertificateConditionReady {
-			m.updateCertificateReadyStatus(crt, c.Status)
-			return
-		}
-	}
-
-	// If no status condition set yet, set to Unknown
-	m.updateCertificateReadyStatus(crt, cmmeta.ConditionUnknown)
-}
-
-func (m *Metrics) updateCertificateReadyStatus(crt *cmapi.Certificate, current cmmeta.ConditionStatus) {
-	for _, condition := range readyConditionStatuses {
-		value := 0.0
-
-		if current == condition {
-			value = 1.0
-		}
-
-		m.certificateReadyStatus.With(prometheus.Labels{
-			"name":      crt.Name,
-			"namespace": crt.Namespace,
-			"condition": string(condition),
-		}).Set(value)
-	}
-}
-
-// RemoveCertificate will delete the Certificate metrics from continuing to be
-// exposed.
-func (m *Metrics) RemoveCertificate(key string) {
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
-	if err != nil {
-		m.log.Error(err, "failed to get namespace and name from key")
-		return
-	}
-
-	m.certificateExpiryTimeSeconds.DeleteLabelValues(name, namespace)
-	for _, condition := range readyConditionStatuses {
-		m.certificateReadyStatus.DeleteLabelValues(name, namespace, string(condition))
-	}
-}
diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go
index 1f52b0240..a39ec5227 100644
--- a/pkg/metrics/metrics.go
+++ b/pkg/metrics/metrics.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2020 The Jetstack cert-manager contributors.
+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.
@@ -18,171 +18,370 @@ limitations under the License.
 // cert-manager exposes the following metrics:
 // certificate_expiration_timestamp_seconds{name, namespace}
 // certificate_ready_status{name, namespace, condition}
-// acme_client_request_count{"scheme", "host", "path", "method", "status"}
-// acme_client_request_duration_seconds{"scheme", "host", "path", "method", "status"}
-// controller_sync_call_count{"controller"}
 package metrics
 
 import (
 	"context"
-	"net"
+	"crypto/x509"
 	"net/http"
+	"sync"
 	"time"
 
-	"github.com/go-logr/logr"
 	"github.com/gorilla/mux"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/util/wait"
+	corelisters "k8s.io/client-go/listers/core/v1"
+	"k8s.io/client-go/tools/cache"
 
+	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
 	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
+	cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha2"
+	logf "github.com/jetstack/cert-manager/pkg/logs"
+	"github.com/jetstack/cert-manager/pkg/util/errors"
+	"github.com/jetstack/cert-manager/pkg/util/kube"
 )
 
 const (
 	// Namespace is the namespace for cert-manager metric names
 	namespace                              = "certmanager"
+	prometheusMetricsServerAddress         = "0.0.0.0:9402"
 	prometheusMetricsServerShutdownTimeout = 5 * time.Second
 	prometheusMetricsServerReadTimeout     = 8 * time.Second
 	prometheusMetricsServerWriteTimeout    = 8 * time.Second
 	prometheusMetricsServerMaxHeaderBytes  = 1 << 20 // 1 MiB
 )
 
-// Metrics is designed to be a shared object for updating the metrics exposed
-// by cert-manager
+var readyConditionStatuses = [...]string{string(cmmeta.ConditionTrue), string(cmmeta.ConditionFalse), string(cmmeta.ConditionUnknown)}
+
+// Default set of metrics
+var Default = New(logf.NewContext(context.Background(), logf.Log.WithName("metrics")))
+
+var CertificateExpiryTimeSeconds = prometheus.NewGaugeVec(
+	prometheus.GaugeOpts{
+		Namespace: namespace,
+		Name:      "certificate_expiration_timestamp_seconds",
+		Help:      "The date after which the certificate expires. Expressed as a Unix Epoch Time.",
+	},
+	[]string{"name", "namespace"},
+)
+
+var CertificateReadyStatus = prometheus.NewGaugeVec(
+	prometheus.GaugeOpts{
+		Namespace: namespace,
+		Name:      "certificate_ready_status",
+		Help:      "The ready status of the certificate.",
+	},
+	[]string{"name", "namespace", "condition"},
+)
+
+// ACMEClientRequestCount is a Prometheus summary to collect the number of
+// requests made to each endpoint with the ACME client.
+var ACMEClientRequestCount = prometheus.NewCounterVec(
+	prometheus.CounterOpts{
+		Namespace: namespace,
+		Name:      "acme_client_request_count",
+		Help:      "The number of requests made by the ACME client.",
+		Subsystem: "http",
+	},
+	[]string{"scheme", "host", "path", "method", "status"},
+)
+
+// ACMEClientRequestDurationSeconds is a Prometheus summary to collect request
+// times for the ACME client.
+var ACMEClientRequestDurationSeconds = prometheus.NewSummaryVec(
+	prometheus.SummaryOpts{
+		Namespace:  namespace,
+		Name:       "acme_client_request_duration_seconds",
+		Help:       "The HTTP request latencies in seconds for the ACME client.",
+		Subsystem:  "http",
+		Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
+	},
+	[]string{"scheme", "host", "path", "method", "status"},
+)
+
+var ControllerSyncCallCount = prometheus.NewCounterVec(
+	prometheus.CounterOpts{
+		Namespace: namespace,
+		Name:      "controller_sync_call_count",
+		Help:      "The number of sync() calls made by a controller.",
+	},
+	[]string{"controller"},
+)
+
+// registeredCertificates holds the set of all certificates which are currently
+// registered by Prometheus
+var registeredCertificates = &struct {
+	certificates map[string]struct{}
+	mtx          sync.Mutex
+}{
+	certificates: make(map[string]struct{}),
+}
+
+// cleanUpFunctions are functions called to clean up metrics which refer to
+// deleted certificates, inputs are name and namespace of the certificate
+var cleanUpFunctions = []func(string, string){
+	metricCleanUpCertificate(CertificateExpiryTimeSeconds),
+	metricCleanUpCertificateWith(CertificateReadyStatus, readyConditionStatuses[:]),
+}
+
+type cleanableMetric interface {
+	DeleteLabelValues(...string) bool
+}
+
 type Metrics struct {
-	log      logr.Logger
-	registry *prometheus.Registry
-
-	certificateExpiryTimeSeconds     *prometheus.GaugeVec
-	certificateReadyStatus           *prometheus.GaugeVec
-	acmeClientRequestDurationSeconds *prometheus.SummaryVec
-	acmeClientRequestCount           *prometheus.CounterVec
-	controllerSyncCallCount          *prometheus.CounterVec
-}
-
-var readyConditionStatuses = [...]cmmeta.ConditionStatus{cmmeta.ConditionTrue, cmmeta.ConditionFalse, cmmeta.ConditionUnknown}
-
-func New(log logr.Logger) *Metrics {
-	var (
-		certificateExpiryTimeSeconds = prometheus.NewGaugeVec(
-			prometheus.GaugeOpts{
-				Namespace: namespace,
-				Name:      "certificate_expiration_timestamp_seconds",
-				Help:      "The date after which the certificate expires. Expressed as a Unix Epoch Time.",
-			},
-			[]string{"name", "namespace"},
-		)
-
-		certificateReadyStatus = prometheus.NewGaugeVec(
-			prometheus.GaugeOpts{
-				Namespace: namespace,
-				Name:      "certificate_ready_status",
-				Help:      "The ready status of the certificate.",
-			},
-			[]string{"name", "namespace", "condition"},
-		)
-
-		// acmeClientRequestCount is a Prometheus summary to collect the number of
-		// requests made to each endpoint with the ACME client.
-		acmeClientRequestCount = prometheus.NewCounterVec(
-			prometheus.CounterOpts{
-				Namespace: namespace,
-				Name:      "acme_client_request_count",
-				Help:      "The number of requests made by the ACME client.",
-				Subsystem: "http",
-			},
-			[]string{"scheme", "host", "path", "method", "status"},
-		)
-
-		// acmeClientRequestDurationSeconds is a Prometheus summary to collect request
-		// times for the ACME client.
-		acmeClientRequestDurationSeconds = prometheus.NewSummaryVec(
-			prometheus.SummaryOpts{
-				Namespace:  namespace,
-				Name:       "acme_client_request_duration_seconds",
-				Help:       "The HTTP request latencies in seconds for the ACME client.",
-				Subsystem:  "http",
-				Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
-			},
-			[]string{"scheme", "host", "path", "method", "status"},
-		)
-
-		controllerSyncCallCount = prometheus.NewCounterVec(
-			prometheus.CounterOpts{
-				Namespace: namespace,
-				Name:      "controller_sync_call_count",
-				Help:      "The number of sync() calls made by a controller.",
-			},
-			[]string{"controller"},
-		)
-	)
-
-	// Create server and register Prometheus metrics handler
-	m := &Metrics{
-		log:      log.WithName("metrics"),
-		registry: prometheus.NewRegistry(),
-
-		certificateExpiryTimeSeconds:     certificateExpiryTimeSeconds,
-		certificateReadyStatus:           certificateReadyStatus,
-		acmeClientRequestCount:           acmeClientRequestCount,
-		acmeClientRequestDurationSeconds: acmeClientRequestDurationSeconds,
-		controllerSyncCallCount:          controllerSyncCallCount,
-	}
-
-	return m
-}
-
-// Start will register the Prometheu metrics, and start the Prometheus server
-func (m *Metrics) Start(listenAddress string) (*http.Server, error) {
-	m.registry.MustRegister(m.certificateExpiryTimeSeconds)
-	m.registry.MustRegister(m.certificateReadyStatus)
-	m.registry.MustRegister(m.acmeClientRequestDurationSeconds)
-	m.registry.MustRegister(m.acmeClientRequestCount)
-	m.registry.MustRegister(m.controllerSyncCallCount)
+	ctx context.Context
+	http.Server
+	activeCertificates cmlisters.CertificateLister
+
+	// TODO (@dippynark): switch this to use an interface to make it testable
+	registry                         *prometheus.Registry
+	CertificateExpiryTimeSeconds     *prometheus.GaugeVec
+	CertificateReadyStatus           *prometheus.GaugeVec
+	ACMEClientRequestDurationSeconds *prometheus.SummaryVec
+	ACMEClientRequestCount           *prometheus.CounterVec
+	ControllerSyncCallCount          *prometheus.CounterVec
+}
 
+func New(ctx context.Context) *Metrics {
 	router := mux.NewRouter()
-	router.Handle("/metrics", promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{}))
 
-	ln, err := net.Listen("tcp", listenAddress)
-	if err != nil {
-		return nil, err
+	// Create server and register prometheus metrics handler
+	s := &Metrics{
+		ctx: ctx,
+		Server: http.Server{
+			Addr:           prometheusMetricsServerAddress,
+			ReadTimeout:    prometheusMetricsServerReadTimeout,
+			WriteTimeout:   prometheusMetricsServerWriteTimeout,
+			MaxHeaderBytes: prometheusMetricsServerMaxHeaderBytes,
+			Handler:        router,
+		},
+		activeCertificates:               nil,
+		registry:                         prometheus.NewRegistry(),
+		CertificateExpiryTimeSeconds:     CertificateExpiryTimeSeconds,
+		CertificateReadyStatus:           CertificateReadyStatus,
+		ACMEClientRequestDurationSeconds: ACMEClientRequestDurationSeconds,
+		ACMEClientRequestCount:           ACMEClientRequestCount,
+		ControllerSyncCallCount:          ControllerSyncCallCount,
 	}
 
-	server := &http.Server{
-		Addr:           ln.Addr().String(),
-		ReadTimeout:    prometheusMetricsServerReadTimeout,
-		WriteTimeout:   prometheusMetricsServerWriteTimeout,
-		MaxHeaderBytes: prometheusMetricsServerMaxHeaderBytes,
-		Handler:        router,
+	router.Handle("/metrics", promhttp.HandlerFor(s.registry, promhttp.HandlerOpts{}))
+
+	return s
+}
+
+func (m *Metrics) waitShutdown(stopCh <-chan struct{}) {
+	log := logf.FromContext(m.ctx)
+	<-stopCh
+	log.Info("stopping Prometheus metrics server...")
+
+	ctx, cancel := context.WithTimeout(context.Background(), prometheusMetricsServerShutdownTimeout)
+	defer cancel()
+
+	if err := m.Shutdown(ctx); err != nil {
+		log.Error(err, "prometheus metrics server shutdown failed", err)
+		return
 	}
 
+	log.Info("prometheus metrics server gracefully stopped")
+}
+
+func (m *Metrics) Start(stopCh <-chan struct{}) {
+	log := logf.FromContext(m.ctx)
+
+	m.registry.MustRegister(m.CertificateExpiryTimeSeconds)
+	m.registry.MustRegister(m.CertificateReadyStatus)
+	m.registry.MustRegister(m.ACMEClientRequestDurationSeconds)
+	m.registry.MustRegister(m.ACMEClientRequestCount)
+	m.registry.MustRegister(m.ControllerSyncCallCount)
+
 	go func() {
-		log := m.log.WithValues("address", ln.Addr())
+		log := log.WithValues("address", m.Addr)
 		log.Info("listening for connections on")
-
-		if err := server.Serve(ln); err != nil {
+		if err := m.ListenAndServe(); err != nil {
 			log.Error(err, "error running prometheus metrics server")
 			return
 		}
+
+		log.Info("prometheus metrics server exited")
+
 	}()
 
-	return server, nil
+	// clean up metrics referring to deleted resources every minute
+	go wait.Until(func() { m.cleanUp() }, time.Minute, stopCh)
+
+	m.waitShutdown(stopCh)
 }
 
-// IncrementSyncCallCount will increase the sync counter for that controller.
-func (m *Metrics) IncrementSyncCallCount(controllerName string) {
-	m.controllerSyncCallCount.WithLabelValues(controllerName).Inc()
+// UpdateCertificateExpiry updates the expiry time of a certificate
+func (m *Metrics) UpdateCertificateExpiry(crt *v1alpha2.Certificate, secretLister corelisters.SecretLister) {
+	log := logf.FromContext(m.ctx)
+	log = logf.WithResource(log, crt)
+	log = logf.WithRelatedResourceName(log, crt.Spec.SecretName, crt.Namespace, "Secret")
+
+	log.V(logf.DebugLevel).Info("attempting to retrieve secret for certificate")
+	// grab existing certificate
+	cert, err := kube.SecretTLSCert(m.ctx, secretLister, crt.Namespace, crt.Spec.SecretName)
+	if err != nil {
+		if !apierrors.IsNotFound(err) && !errors.IsInvalidData(err) {
+			log.Error(err, "error reading secret for certificate")
+		}
+		return
+	}
+
+	updateX509Expiry(crt, cert)
 }
 
-func (m *Metrics) Shutdown(server *http.Server) {
-	m.log.Info("stopping Prometheus metrics server...")
+func updateX509Expiry(crt *v1alpha2.Certificate, cert *x509.Certificate) {
+	expiryTime := cert.NotAfter
+	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(crt)
+	if err != nil {
+		return
+	}
 
-	ctx, cancel := context.WithTimeout(context.Background(), prometheusMetricsServerShutdownTimeout)
-	defer cancel()
+	registeredCertificates.mtx.Lock()
+	defer registeredCertificates.mtx.Unlock()
+	// set certificate expiry time
+	CertificateExpiryTimeSeconds.With(prometheus.Labels{
+		"name":      crt.Name,
+		"namespace": crt.Namespace}).Set(float64(expiryTime.Unix()))
+	registerCertificateKey(key)
+}
 
-	if err := server.Shutdown(ctx); err != nil {
-		m.log.Error(err, "prometheus metrics server shutdown failed", err)
+func (m *Metrics) UpdateCertificateStatus(crt *v1alpha2.Certificate) {
+	log := logf.FromContext(m.ctx)
+	log = logf.WithResource(log, crt)
+
+	log.V(logf.DebugLevel).Info("attempting to retrieve ready status for certificate")
+	for _, c := range crt.Status.Conditions {
+		switch c.Type {
+		case v1alpha2.CertificateConditionReady:
+			updateCertificateReadyStatus(crt, c.Status)
+		}
+	}
+}
+
+func updateCertificateReadyStatus(crt *v1alpha2.Certificate, current cmmeta.ConditionStatus) {
+	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(crt)
+	if err != nil {
+		return
+	}
+
+	registeredCertificates.mtx.Lock()
+	defer registeredCertificates.mtx.Unlock()
+	for _, condition := range readyConditionStatuses {
+		value := 0.0
+		if string(current) == condition {
+			value = 1.0
+		}
+		CertificateReadyStatus.With(prometheus.Labels{
+			"name":      crt.Name,
+			"namespace": crt.Namespace,
+			"condition": string(condition),
+		}).Set(value)
+	}
+	registerCertificateKey(key)
+}
+
+// registerCertificateKey adds an entry in registeredCertificates to track
+// which certificates have metrics stored in prometheus, allowing for easier
+// clean-up.
+// You MUST lock the mutex before calling this function, this ensures no other
+// function is cleaning up while we are registering a certificate
+func registerCertificateKey(key string) {
+	registeredCertificates.certificates[key] = struct{}{}
+}
+
+func (m *Metrics) SetActiveCertificates(cl cmlisters.CertificateLister) {
+	m.activeCertificates = cl
+}
+
+// cleanUp removes any metrics which reference resources which no longer exist
+func (m *Metrics) cleanUp() {
+	log := logf.FromContext(m.ctx)
+	log.V(logf.DebugLevel).Info("attempting to clean up metrics for recently deleted certificates")
+
+	if m.activeCertificates == nil {
+		log.V(logf.DebugLevel).Info("active certificates is still uninitialized")
+		return
+	}
+
+	activeCrts, err := m.activeCertificates.List(labels.Everything())
+	if err != nil {
+		log.Error(err, "error retrieving active certificates")
+		return
+	}
+
+	cleanUpCertificates(activeCrts)
+}
+
+// cleanUpCertificates removes metrics for recently deleted certificates
+func cleanUpCertificates(activeCrts []*v1alpha2.Certificate) {
+	activeMap := make(map[string]struct{}, len(activeCrts))
+	for _, crt := range activeCrts {
+		key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(crt)
+		if err != nil {
+			continue
+		}
+
+		activeMap[key] = struct{}{}
+	}
+
+	registeredCertificates.mtx.Lock()
+	defer registeredCertificates.mtx.Unlock()
+	var toCleanUp []string
+	for key := range registeredCertificates.certificates {
+		if _, found := activeMap[key]; !found {
+			toCleanUp = append(toCleanUp, key)
+		}
+	}
+
+	for _, key := range toCleanUp {
+		cleanUpCertificateByKey(key)
+	}
+}
+
+// metricCleanUpCertificate creates a clean up function which deletes the entry
+// (if any) for a certificate in the given metric
+func metricCleanUpCertificate(c cleanableMetric) func(string, string) {
+	return func(name, namespace string) {
+		c.DeleteLabelValues(name, namespace)
+	}
+}
+
+// metricCleanUpCertificateWith creates a clean up function which deletes the
+// entries (if any) for a certificate in the given metric, iterating over the
+// additional labels.
+// This is used if the metric keys on data in addition to the name and
+// namespace.
+func metricCleanUpCertificateWith(c cleanableMetric, additionalLabels []string) func(string, string) {
+	return func(name, namespace string) {
+		for _, label := range additionalLabels {
+			c.DeleteLabelValues(name, namespace, label)
+		}
+	}
+}
+
+// cleanUpCertificateByKey removes metrics which refer to a certificate,
+// given the key of the certificate.
+func cleanUpCertificateByKey(key string) {
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
 		return
 	}
 
-	m.log.Info("prometheus metrics server gracefully stopped")
+	// apply all the clean up functions
+	for _, f := range cleanUpFunctions {
+		f(name, namespace)
+	}
+
+	delete(registeredCertificates.certificates, key)
+}
+
+func (m *Metrics) IncrementSyncCallCount(controllerName string) {
+	log := logf.FromContext(m.ctx)
+	log.V(logf.DebugLevel).Info("incrementing controller sync call count", "controllerName", controllerName)
+	ControllerSyncCallCount.WithLabelValues(controllerName).Inc()
 }
diff --git a/pkg/util/pki/generate.go b/pkg/util/pki/generate.go
index 1d5732a2e..00a74d615 100644
--- a/pkg/util/pki/generate.go
+++ b/pkg/util/pki/generate.go
@@ -25,6 +25,7 @@ import (
 	"crypto/x509"
 	"encoding/pem"
 	"fmt"
+
 	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
 )
 


Test patch:
diff --git a/pkg/acme/accounts/registry_test.go b/pkg/acme/accounts/registry_test.go
index 51db9af4f..fb4da369b 100644
--- a/pkg/acme/accounts/registry_test.go
+++ b/pkg/acme/accounts/registry_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package accounts
 
 import (
-	"net/http"
 	"testing"
 
 	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1alpha2"
@@ -32,7 +31,7 @@ func TestRegistry_AddClient(t *testing.T) {
 	}
 
 	// Register a new client
-	r.AddClient(http.DefaultClient, "abc", cmacme.ACMEIssuer{}, pk)
+	r.AddClient("abc", cmacme.ACMEIssuer{}, pk)
 
 	c, err := r.GetClient("abc")
 	if err != nil {
@@ -51,7 +50,7 @@ func TestRegistry_RemoveClient(t *testing.T) {
 	}
 
 	// Register a new client
-	r.AddClient(http.DefaultClient, "abc", cmacme.ACMEIssuer{}, pk)
+	r.AddClient("abc", cmacme.ACMEIssuer{}, pk)
 
 	c, err := r.GetClient("abc")
 	if err != nil {
@@ -91,14 +90,14 @@ func TestRegistry_ListClients(t *testing.T) {
 	}
 
 	// Register a new client
-	r.AddClient(http.DefaultClient, "abc", cmacme.ACMEIssuer{}, pk)
+	r.AddClient("abc", cmacme.ACMEIssuer{}, pk)
 	l := r.ListClients()
 	if len(l) != 1 {
 		t.Errorf("expected ListClients to have 1 item but it has %d", len(l))
 	}
 
 	// Register a second client
-	r.AddClient(http.DefaultClient, "abc2", cmacme.ACMEIssuer{}, pk)
+	r.AddClient("abc2", cmacme.ACMEIssuer{}, pk)
 	l = r.ListClients()
 	if len(l) != 2 {
 		t.Errorf("expected ListClients to have 2 items but it has %d", len(l))
@@ -106,14 +105,14 @@ func TestRegistry_ListClients(t *testing.T) {
 
 	// Register a third client with the same options as the second, meaning
 	// it should be de-duplicated
-	r.AddClient(http.DefaultClient, "abc2", cmacme.ACMEIssuer{}, pk)
+	r.AddClient("abc2", cmacme.ACMEIssuer{}, pk)
 	l = r.ListClients()
 	if len(l) != 2 {
 		t.Errorf("expected ListClients to have 2 items but it has %d", len(l))
 	}
 
 	// Update the second client with a new server URL
-	r.AddClient(http.DefaultClient, "abc2", cmacme.ACMEIssuer{Server: "abc.com"}, pk)
+	r.AddClient("abc2", cmacme.ACMEIssuer{Server: "abc.com"}, pk)
 	l = r.ListClients()
 	if len(l) != 2 {
 		t.Errorf("expected ListClients to have 2 items but it has %d", len(l))
@@ -132,14 +131,14 @@ func TestRegistry_AddClient_UpdatesExistingWhenPrivateKeyChanges(t *testing.T) {
 	}
 
 	// Register a new client
-	r.AddClient(http.DefaultClient, "abc", cmacme.ACMEIssuer{}, pk)
+	r.AddClient("abc", cmacme.ACMEIssuer{}, pk)
 	l := r.ListClients()
 	if len(l) != 1 {
 		t.Errorf("expected ListClients to have 1 item but it has %d", len(l))
 	}
 
 	// Update the client with a new private key
-	r.AddClient(http.DefaultClient, "abc", cmacme.ACMEIssuer{}, pk2)
+	r.AddClient("abc", cmacme.ACMEIssuer{}, pk2)
 	l = r.ListClients()
 	if len(l) != 1 {
 		t.Errorf("expected ListClients to have 1 item but it has %d", len(l))
diff --git a/pkg/controller/test/BUILD.bazel b/pkg/controller/test/BUILD.bazel
index 680a537a6..eab8a3590 100644
--- a/pkg/controller/test/BUILD.bazel
+++ b/pkg/controller/test/BUILD.bazel
@@ -18,7 +18,6 @@ go_library(
         "//pkg/client/informers/externalversions:go_default_library",
         "//pkg/controller:go_default_library",
         "//pkg/logs:go_default_library",
-        "//pkg/metrics:go_default_library",
         "//pkg/util:go_default_library",
         "@com_github_kr_pretty//:go_default_library",
         "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
diff --git a/pkg/controller/test/context_builder.go b/pkg/controller/test/context_builder.go
index 2511fcb12..26d60cee5 100644
--- a/pkg/controller/test/context_builder.go
+++ b/pkg/controller/test/context_builder.go
@@ -39,7 +39,6 @@ import (
 	informers "github.com/jetstack/cert-manager/pkg/client/informers/externalversions"
 	"github.com/jetstack/cert-manager/pkg/controller"
 	"github.com/jetstack/cert-manager/pkg/logs"
-	"github.com/jetstack/cert-manager/pkg/metrics"
 	"github.com/jetstack/cert-manager/pkg/util"
 )
 
@@ -120,7 +119,6 @@ func (b *Builder) Init() {
 	b.KubeSharedInformerFactory = kubeinformers.NewSharedInformerFactory(b.Client, informerResyncPeriod)
 	b.SharedInformerFactory = informers.NewSharedInformerFactory(b.CMClient, informerResyncPeriod)
 	b.stopCh = make(chan struct{})
-	b.Metrics = metrics.New(logs.Log)
 
 	// set the Clock on the context
 	if b.Clock == nil {
diff --git a/pkg/metrics/certificates_test.go b/pkg/metrics/certificates_test.go
deleted file mode 100644
index 42964b847..000000000
--- a/pkg/metrics/certificates_test.go
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
-Copyright 2020 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 metrics
-
-import (
-	"context"
-	"strings"
-	"testing"
-	"time"
-
-	"github.com/prometheus/client_golang/prometheus/testutil"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
-	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
-	logtesting "github.com/jetstack/cert-manager/pkg/logs/testing"
-	"github.com/jetstack/cert-manager/test/unit/gen"
-)
-
-const expiryMetadata = `
-	# HELP certmanager_certificate_expiration_timestamp_seconds The date after which the certificate expires. Expressed as a Unix Epoch Time.
-	# TYPE certmanager_certificate_expiration_timestamp_seconds gauge
-`
-
-const readyMetadata = `
-  # HELP certmanager_certificate_ready_status The ready status of the certificate.
-  # TYPE certmanager_certificate_ready_status gauge
-`
-
-func TestCertificateMetrics(t *testing.T) {
-	type testT struct {
-		crt                           *cmapi.Certificate
-		expectedExpiry, expectedReady string
-	}
-	tests := map[string]testT{
-		"certificate with expiry and ready status": {
-			crt: gen.Certificate("test-certificate",
-				gen.SetCertificateNamespace("test-ns"),
-				gen.SetCertificateNotAfter(metav1.Time{
-					Time: time.Unix(2208988804, 0),
-				}),
-				gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
-					Type:   cmapi.CertificateConditionReady,
-					Status: cmmeta.ConditionTrue,
-				}),
-			),
-			expectedExpiry: `
-	certmanager_certificate_expiration_timestamp_seconds{name="test-certificate",namespace="test-ns"} 2.208988804e+09
-`,
-			expectedReady: `
-        certmanager_certificate_ready_status{condition="False",name="test-certificate",namespace="test-ns"} 0
-        certmanager_certificate_ready_status{condition="True",name="test-certificate",namespace="test-ns"} 1
-        certmanager_certificate_ready_status{condition="Unknown",name="test-certificate",namespace="test-ns"} 0
-`,
-		},
-
-		"certificate with no expiry and no status should give an expiry of 0 and Unknown status": {
-			crt: gen.Certificate("test-certificate",
-				gen.SetCertificateNamespace("test-ns"),
-			),
-			expectedExpiry: `
-	certmanager_certificate_expiration_timestamp_seconds{name="test-certificate",namespace="test-ns"} 0
-`,
-			expectedReady: `
-        certmanager_certificate_ready_status{condition="False",name="test-certificate",namespace="test-ns"} 0
-        certmanager_certificate_ready_status{condition="True",name="test-certificate",namespace="test-ns"} 0
-        certmanager_certificate_ready_status{condition="Unknown",name="test-certificate",namespace="test-ns"} 1
-`,
-		},
-
-		"certificate with expiry and status False should give an expiry and False status": {
-			crt: gen.Certificate("test-certificate",
-				gen.SetCertificateNamespace("test-ns"),
-				gen.SetCertificateNotAfter(metav1.Time{
-					Time: time.Unix(100, 0),
-				}),
-				gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
-					Type:   cmapi.CertificateConditionReady,
-					Status: cmmeta.ConditionFalse,
-				}),
-			),
-			expectedExpiry: `
-	certmanager_certificate_expiration_timestamp_seconds{name="test-certificate",namespace="test-ns"} 100
-`,
-			expectedReady: `
-        certmanager_certificate_ready_status{condition="False",name="test-certificate",namespace="test-ns"} 1
-        certmanager_certificate_ready_status{condition="True",name="test-certificate",namespace="test-ns"} 0
-        certmanager_certificate_ready_status{condition="Unknown",name="test-certificate",namespace="test-ns"} 0
-`,
-		},
-		"certificate with expiry and status Unknown should give an expiry and Unknown status": {
-			crt: gen.Certificate("test-certificate",
-				gen.SetCertificateNamespace("test-ns"),
-				gen.SetCertificateNotAfter(metav1.Time{
-					Time: time.Unix(99999, 0),
-				}),
-				gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
-					Type:   cmapi.CertificateConditionReady,
-					Status: cmmeta.ConditionUnknown,
-				}),
-			),
-			expectedExpiry: `
-	certmanager_certificate_expiration_timestamp_seconds{name="test-certificate",namespace="test-ns"} 99999
-`,
-			expectedReady: `
-        certmanager_certificate_ready_status{condition="False",name="test-certificate",namespace="test-ns"} 0
-        certmanager_certificate_ready_status{condition="True",name="test-certificate",namespace="test-ns"} 0
-        certmanager_certificate_ready_status{condition="Unknown",name="test-certificate",namespace="test-ns"} 1
-`,
-		},
-	}
-	for n, test := range tests {
-		t.Run(n, func(t *testing.T) {
-			m := New(logtesting.TestLogger{T: t})
-			m.UpdateCertificate(context.TODO(), test.crt)
-
-			if err := testutil.CollectAndCompare(m.certificateExpiryTimeSeconds,
-				strings.NewReader(expiryMetadata+test.expectedExpiry),
-				"certmanager_certificate_expiration_timestamp_seconds",
-			); err != nil {
-				t.Errorf("unexpected collecting result:\n%s", err)
-			}
-
-			if err := testutil.CollectAndCompare(m.certificateReadyStatus,
-				strings.NewReader(readyMetadata+test.expectedReady),
-				"certmanager_certificate_ready_status",
-			); err != nil {
-				t.Errorf("unexpected collecting result:\n%s", err)
-			}
-		})
-	}
-}
-
-func TestCertificateCache(t *testing.T) {
-	m := New(logtesting.TestLogger{T: t})
-
-	crt1 := gen.Certificate("crt1",
-		gen.SetCertificateUID("uid-1"),
-		gen.SetCertificateNotAfter(metav1.Time{
-			Time: time.Unix(100, 0),
-		}),
-		gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
-			Type:   cmapi.CertificateConditionReady,
-			Status: cmmeta.ConditionUnknown,
-		}),
-	)
-	crt2 := gen.Certificate("crt2",
-		gen.SetCertificateUID("uid-2"),
-		gen.SetCertificateNotAfter(metav1.Time{
-			Time: time.Unix(200, 0),
-		}),
-		gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
-			Type:   cmapi.CertificateConditionReady,
-			Status: cmmeta.ConditionTrue,
-		}),
-	)
-	crt3 := gen.Certificate("crt3",
-		gen.SetCertificateUID("uid-3"),
-		gen.SetCertificateNotAfter(metav1.Time{
-			Time: time.Unix(300, 0),
-		}),
-		gen.SetCertificateStatusCondition(cmapi.CertificateCondition{
-			Type:   cmapi.CertificateConditionReady,
-			Status: cmmeta.ConditionFalse,
-		}),
-	)
-
-	// Observe all three Certificate metrics
-	m.UpdateCertificate(context.TODO(), crt1)
-	m.UpdateCertificate(context.TODO(), crt2)
-	m.UpdateCertificate(context.TODO(), crt3)
-
-	// Check all three metrics exist
-	if err := testutil.CollectAndCompare(m.certificateReadyStatus,
-		strings.NewReader(readyMetadata+`
-        certmanager_certificate_ready_status{condition="False",name="crt1",namespace="default-unit-test-ns"} 0
-        certmanager_certificate_ready_status{condition="False",name="crt2",namespace="default-unit-test-ns"} 0
-        certmanager_certificate_ready_status{condition="False",name="crt3",namespace="default-unit-test-ns"} 1
-        certmanager_certificate_ready_status{condition="True",name="crt1",namespace="default-unit-test-ns"} 0
-        certmanager_certificate_ready_status{condition="True",name="crt2",namespace="default-unit-test-ns"} 1
-        certmanager_certificate_ready_status{condition="True",name="crt3",namespace="default-unit-test-ns"} 0
-        certmanager_certificate_ready_status{condition="Unknown",name="crt1",namespace="default-unit-test-ns"} 1
-        certmanager_certificate_ready_status{condition="Unknown",name="crt2",namespace="default-unit-test-ns"} 0
-        certmanager_certificate_ready_status{condition="Unknown",name="crt3",namespace="default-unit-test-ns"} 0
-`),
-		"certmanager_certificate_ready_status",
-	); err != nil {
-		t.Errorf("unexpected collecting result:\n%s", err)
-	}
-	if err := testutil.CollectAndCompare(m.certificateExpiryTimeSeconds,
-		strings.NewReader(expiryMetadata+`
-        certmanager_certificate_expiration_timestamp_seconds{name="crt1",namespace="default-unit-test-ns"} 100
-        certmanager_certificate_expiration_timestamp_seconds{name="crt2",namespace="default-unit-test-ns"} 200
-        certmanager_certificate_expiration_timestamp_seconds{name="crt3",namespace="default-unit-test-ns"} 300
-`),
-		"certmanager_certificate_expiration_timestamp_seconds",
-	); err != nil {
-		t.Errorf("unexpected collecting result:\n%s", err)
-	}
-
-	// Remove second certificate and check not exists
-	m.RemoveCertificate("default-unit-test-ns/crt2")
-	if err := testutil.CollectAndCompare(m.certificateReadyStatus,
-		strings.NewReader(readyMetadata+`
-        certmanager_certificate_ready_status{condition="False",name="crt1",namespace="default-unit-test-ns"} 0
-        certmanager_certificate_ready_status{condition="False",name="crt3",namespace="default-unit-test-ns"} 1
-        certmanager_certificate_ready_status{condition="True",name="crt1",namespace="default-unit-test-ns"} 0
-        certmanager_certificate_ready_status{condition="True",name="crt3",namespace="default-unit-test-ns"} 0
-        certmanager_certificate_ready_status{condition="Unknown",name="crt1",namespace="default-unit-test-ns"} 1
-        certmanager_certificate_ready_status{condition="Unknown",name="crt3",namespace="default-unit-test-ns"} 0
-`),
-		"certmanager_certificate_ready_status",
-	); err != nil {
-		t.Errorf("unexpected collecting result:\n%s", err)
-	}
-	if err := testutil.CollectAndCompare(m.certificateExpiryTimeSeconds,
-		strings.NewReader(expiryMetadata+`
-        certmanager_certificate_expiration_timestamp_seconds{name="crt1",namespace="default-unit-test-ns"} 100
-        certmanager_certificate_expiration_timestamp_seconds{name="crt3",namespace="default-unit-test-ns"} 300
-`),
-		"certmanager_certificate_expiration_timestamp_seconds",
-	); err != nil {
-		t.Errorf("unexpected collecting result:\n%s", err)
-	}
-
-	// Remove all Certificates (even is already removed) and observe no Certificates
-	m.RemoveCertificate("default-unit-test-ns/crt1")
-	m.RemoveCertificate("default-unit-test-ns/crt2")
-	m.RemoveCertificate("default-unit-test-ns/crt3")
-	if err := testutil.CollectAndCompare(m.certificateReadyStatus,
-		strings.NewReader(readyMetadata),
-		"certmanager_certificate_ready_status",
-	); err != nil {
-		t.Errorf("unexpected collecting result:\n%s", err)
-	}
-	if err := testutil.CollectAndCompare(m.certificateExpiryTimeSeconds,
-		strings.NewReader(expiryMetadata),
-		"certmanager_certificate_expiration_timestamp_seconds",
-	); err != nil {
-		t.Errorf("unexpected collecting result:\n%s", err)
-	}
-}
diff --git a/pkg/metrics/metrics_test.go b/pkg/metrics/metrics_test.go
new file mode 100644
index 000000000..5dea94350
--- /dev/null
+++ b/pkg/metrics/metrics_test.go
@@ -0,0 +1,268 @@
+/*
+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 metrics
+
+import (
+	"crypto/x509"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/prometheus/client_golang/prometheus/testutil"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
+	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
+)
+
+func getReadyConditionStatus(crt *v1alpha2.Certificate) cmmeta.ConditionStatus {
+	for _, c := range crt.Status.Conditions {
+		switch c.Type {
+		case v1alpha2.CertificateConditionReady:
+			return c.Status
+		}
+	}
+	return cmmeta.ConditionUnknown
+}
+
+func buildCertificate(name, namespace string, condition cmmeta.ConditionStatus) *v1alpha2.Certificate {
+	return &v1alpha2.Certificate{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Status: v1alpha2.CertificateStatus{
+			Conditions: []v1alpha2.CertificateCondition{
+				{
+					Type:   v1alpha2.CertificateConditionReady,
+					Status: condition,
+				},
+			},
+		},
+	}
+}
+
+func TestUpdateCertificateExpiry(t *testing.T) {
+	const metadata = `
+	# HELP certmanager_certificate_expiration_timestamp_seconds The date after which the certificate expires. Expressed as a Unix Epoch Time.
+	# TYPE certmanager_certificate_expiration_timestamp_seconds gauge
+`
+	type testT struct {
+		crt      *v1alpha2.Certificate
+		cert     *x509.Certificate
+		expected string
+	}
+	tests := map[string]testT{
+		"first": {
+			crt: &v1alpha2.Certificate{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "something",
+					Namespace: "default",
+				},
+			},
+			cert: &x509.Certificate{
+				// fixed expiry time for testing
+				NotAfter: time.Unix(2208988804, 0),
+			},
+			expected: `
+	certmanager_certificate_expiration_timestamp_seconds{name="something",namespace="default"} 2.208988804e+09
+`,
+		},
+	}
+	for n, test := range tests {
+		t.Run(n, func(t *testing.T) {
+			defer cleanUpCertificates(nil)
+
+			updateX509Expiry(test.crt, test.cert)
+
+			if err := testutil.CollectAndCompare(
+				CertificateExpiryTimeSeconds,
+				strings.NewReader(metadata+test.expected),
+				"certmanager_certificate_expiration_timestamp_seconds",
+			); err != nil {
+				t.Errorf("unexpected collecting result:\n%s", err)
+			}
+		})
+	}
+}
+
+func TestUpdateCertificateReadyStatus(t *testing.T) {
+	const metadata = `
+	# HELP certmanager_certificate_ready_status The ready status of the certificate.
+	# TYPE certmanager_certificate_ready_status gauge
+`
+
+	type testT struct {
+		crt      *v1alpha2.Certificate
+		expected string
+	}
+	tests := map[string]testT{
+		"ready status true is updated correctly": {
+			crt: buildCertificate("something", "default", cmmeta.ConditionTrue),
+			expected: `
+	certmanager_certificate_ready_status{condition="False",name="something",namespace="default"} 0
+	certmanager_certificate_ready_status{condition="True",name="something",namespace="default"} 1
+	certmanager_certificate_ready_status{condition="Unknown",name="something",namespace="default"} 0
+`,
+		},
+		"ready status false is updated correctly": {
+			crt: buildCertificate("something", "default", cmmeta.ConditionFalse),
+			expected: `
+	certmanager_certificate_ready_status{condition="False",name="something",namespace="default"} 1
+	certmanager_certificate_ready_status{condition="True",name="something",namespace="default"} 0
+	certmanager_certificate_ready_status{condition="Unknown",name="something",namespace="default"} 0
+`,
+		},
+		"ready status unknown is updated correctly": {
+			crt: buildCertificate("something", "default", cmmeta.ConditionUnknown),
+			expected: `
+	certmanager_certificate_ready_status{condition="False",name="something",namespace="default"} 0
+	certmanager_certificate_ready_status{condition="True",name="something",namespace="default"} 0
+	certmanager_certificate_ready_status{condition="Unknown",name="something",namespace="default"} 1
+`,
+		},
+	}
+	for n, test := range tests {
+		t.Run(n, func(t *testing.T) {
+			updateCertificateReadyStatus(test.crt, getReadyConditionStatus(test.crt))
+
+			if err := testutil.CollectAndCompare(
+				CertificateReadyStatus,
+				strings.NewReader(metadata+test.expected),
+				"certmanager_certificate_ready_status",
+			); err != nil {
+				t.Errorf("unexpected collecting result:\n%s", err)
+			}
+		})
+	}
+}
+
+func TestCleanUp(t *testing.T) {
+	const metadataExpiry = `
+	# HELP certmanager_certificate_expiration_timestamp_seconds The date after which the certificate expires. Expressed as a Unix Epoch Time.
+	# TYPE certmanager_certificate_expiration_timestamp_seconds gauge
+`
+
+	const metadataReady = `
+	# HELP certmanager_certificate_ready_status The ready status of the certificate.
+	# TYPE certmanager_certificate_ready_status gauge
+`
+	type testT struct {
+		active         map[*v1alpha2.Certificate]*x509.Certificate
+		inactive       map[*v1alpha2.Certificate]*x509.Certificate
+		expectedExpiry string
+		expectedReady  string
+	}
+	tests := map[string]testT{
+		"inactive certificate metrics cleaned up while active certificate metrics kept": {
+			active: map[*v1alpha2.Certificate]*x509.Certificate{
+				buildCertificate("active", "default", cmmeta.ConditionTrue): {
+					// fixed expiry time for testing
+					NotAfter: time.Unix(2208988804, 0),
+				},
+			},
+			inactive: map[*v1alpha2.Certificate]*x509.Certificate{
+				buildCertificate("inactive", "default", cmmeta.ConditionTrue): {
+					// fixed expiry time for testing
+					NotAfter: time.Unix(2208988804, 0),
+				},
+			},
+			expectedExpiry: `
+	certmanager_certificate_expiration_timestamp_seconds{name="active",namespace="default"} 2.208988804e+09
+`,
+			expectedReady: `
+	certmanager_certificate_ready_status{condition="False",name="active",namespace="default"} 0
+	certmanager_certificate_ready_status{condition="True",name="active",namespace="default"} 1
+	certmanager_certificate_ready_status{condition="Unknown",name="active",namespace="default"} 0
+`,
+		},
+		"no metrics cleaned up when only active certificate metrics": {
+			active: map[*v1alpha2.Certificate]*x509.Certificate{
+				buildCertificate("active", "default", cmmeta.ConditionTrue): {
+					// fixed expiry time for testing
+					NotAfter: time.Unix(2208988804, 0),
+				},
+				buildCertificate("also-active", "default", cmmeta.ConditionTrue): {
+					// fixed expiry time for testing
+					NotAfter: time.Unix(2208988804, 0),
+				},
+			},
+			inactive: map[*v1alpha2.Certificate]*x509.Certificate{},
+			expectedExpiry: `
+	certmanager_certificate_expiration_timestamp_seconds{name="active",namespace="default"} 2.208988804e+09
+	certmanager_certificate_expiration_timestamp_seconds{name="also-active",namespace="default"} 2.208988804e+09
+`,
+			expectedReady: `
+	certmanager_certificate_ready_status{condition="False",name="active",namespace="default"} 0
+	certmanager_certificate_ready_status{condition="False",name="also-active",namespace="default"} 0
+	certmanager_certificate_ready_status{condition="True",name="active",namespace="default"} 1
+	certmanager_certificate_ready_status{condition="True",name="also-active",namespace="default"} 1
+	certmanager_certificate_ready_status{condition="Unknown",name="active",namespace="default"} 0
+	certmanager_certificate_ready_status{condition="Unknown",name="also-active",namespace="default"} 0
+`,
+		},
+		"all metrics cleaned up when only inactive certificate metrics": {
+			active: map[*v1alpha2.Certificate]*x509.Certificate{},
+			inactive: map[*v1alpha2.Certificate]*x509.Certificate{
+				buildCertificate("inactive", "default", cmmeta.ConditionTrue): {
+					// fixed expiry time for testing
+					NotAfter: time.Unix(2208988804, 0),
+				},
+				buildCertificate("also-inactive", "default", cmmeta.ConditionTrue): {
+					// fixed expiry time for testing
+					NotAfter: time.Unix(2208988804, 0),
+				},
+			},
+			expectedExpiry: "",
+			expectedReady:  "",
+		},
+	}
+	for n, test := range tests {
+		t.Run(n, func(t *testing.T) {
+			defer cleanUpCertificates(nil)
+
+			var activeCrts []*v1alpha2.Certificate
+			for crt, cert := range test.active {
+				updateX509Expiry(crt, cert)
+				updateCertificateReadyStatus(crt, getReadyConditionStatus(crt))
+				activeCrts = append(activeCrts, crt)
+			}
+			for crt, cert := range test.inactive {
+				updateCertificateReadyStatus(crt, getReadyConditionStatus(crt))
+				updateX509Expiry(crt, cert)
+			}
+
+			cleanUpCertificates(activeCrts)
+
+			if err := testutil.CollectAndCompare(
+				CertificateExpiryTimeSeconds,
+				strings.NewReader(metadataExpiry+test.expectedExpiry),
+				"certmanager_certificate_expiration_timestamp_seconds",
+			); err != nil {
+				t.Errorf("unexpected collecting result:\n%s", err)
+			}
+
+			if err := testutil.CollectAndCompare(
+				CertificateReadyStatus,
+				strings.NewReader(metadataReady+test.expectedReady),
+				"certmanager_certificate_ready_status",
+			); err != nil {
+				t.Errorf("unexpected collecting result:\n%s", err)
+			}
+		})
+	}
+}
diff --git a/test/acme/dns/server/server.go b/test/acme/dns/server/server.go
index d67f7b989..a41d0c81c 100644
--- a/test/acme/dns/server/server.go
+++ b/test/acme/dns/server/server.go
@@ -80,7 +80,7 @@ func (b *BasicServer) RunWithAddress(ctx context.Context, listenAddr string) err
 	// update the ctx with the new logger
 	ctx = logf.NewContext(ctx, log)
 
-	b.server = &dns.Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour, MsgAcceptFunc: msgAcceptFunc}
+	b.server = &dns.Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
 	if b.EnableTSIG {
 		log.Info("enabling TSIG support")
 		b.server.TsigSecret = map[string]string{b.TSIGKeyName: b.TSIGKeySecret}
@@ -119,11 +119,3 @@ func (b *BasicServer) ListenAddr() string {
 func (b *BasicServer) Shutdown() error {
 	return b.server.Shutdown()
 }
-
-func msgAcceptFunc(dh dns.Header) dns.MsgAcceptAction {
-	// the miekg/dns message accept function disallows rfc2136 headers
-	// this function replaces that behaviour to always accept the request
-	// since this is always running in the controlled environment of tests
-	// it should not be an issue
-	return dns.MsgAccept
-}
diff --git a/test/e2e/framework/addon/vault/proxy.go b/test/e2e/framework/addon/vault/proxy.go
index c4c18328e..0966ecbea 100644
--- a/test/e2e/framework/addon/vault/proxy.go
+++ b/test/e2e/framework/addon/vault/proxy.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2020 The Jetstack cert-manager contributors.
+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.
@@ -55,7 +55,7 @@ func newProxy(ns, podName, kubectl string, vaultCA []byte) *proxy {
 }
 
 func (p *proxy) init() (*vault.Client, error) {
-	listenPort, err := freePort()
+	listenPort, err := p.freePort()
 	if err != nil {
 		return nil, err
 	}
@@ -196,7 +196,7 @@ func (p *proxy) runProxy() error {
 	return nil
 }
 
-func freePort() (int, error) {
+func (p *proxy) freePort() (int, error) {
 	l, err := net.ListenTCP("tcp", &net.TCPAddr{
 		IP:   net.ParseIP("127.0.0.1"),
 		Port: 0,
diff --git a/test/e2e/images.bzl b/test/e2e/images.bzl
index f64e91efb..a8847f3b8 100644
--- a/test/e2e/images.bzl
+++ b/test/e2e/images.bzl
@@ -39,7 +39,7 @@ def install():
         name = "io_kubernetes_ingress-nginx",
         registry = "quay.io",
         repository = "kubernetes-ingress-controller/nginx-ingress-controller",
-        tag = "0.33.0",
+        tag = "0.26.1",
         # For some reason, the suggested sha256 returns an error when fetched from
         # quay.io by digest.
         # digest = "sha256:f7f08fdbbeddaf3179829c662da360a3feac1ecf8c4b1305949fffd8c8f59879",
diff --git a/test/integration/certificates/BUILD.bazel b/test/integration/certificates/BUILD.bazel
index c6f8d4c83..c912b7cad 100644
--- a/test/integration/certificates/BUILD.bazel
+++ b/test/integration/certificates/BUILD.bazel
@@ -4,7 +4,6 @@ go_test(
     name = "go_default_test",
     srcs = [
         "issuing_controller_test.go",
-        "metrics_controller_test.go",
         "trigger_controller_test.go",
     ],
     deps = [
@@ -12,12 +11,10 @@ go_test(
         "//pkg/apis/certmanager/v1alpha2:go_default_library",
         "//pkg/apis/meta/v1:go_default_library",
         "//pkg/controller:go_default_library",
-        "//pkg/controller/certificates/metrics:go_default_library",
         "//pkg/controller/expcertificates/issuing:go_default_library",
         "//pkg/controller/expcertificates/trigger:go_default_library",
         "//pkg/controller/expcertificates/trigger/policies:go_default_library",
         "//pkg/logs:go_default_library",
-        "//pkg/metrics:go_default_library",
         "//pkg/util/pki:go_default_library",
         "//test/integration/framework:go_default_library",
         "//test/unit/gen:go_default_library",
diff --git a/test/integration/certificates/issuing_controller_test.go b/test/integration/certificates/issuing_controller_test.go
index 607c4ae43..80b013f6c 100644
--- a/test/integration/certificates/issuing_controller_test.go
+++ b/test/integration/certificates/issuing_controller_test.go
@@ -35,7 +35,6 @@ import (
 	controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
 	"github.com/jetstack/cert-manager/pkg/controller/expcertificates/issuing"
 	logf "github.com/jetstack/cert-manager/pkg/logs"
-	"github.com/jetstack/cert-manager/pkg/metrics"
 	utilpki "github.com/jetstack/cert-manager/pkg/util/pki"
 	"github.com/jetstack/cert-manager/test/integration/framework"
 	"github.com/jetstack/cert-manager/test/unit/gen"
@@ -59,8 +58,6 @@ func TestIssuingController(t *testing.T) {
 	ctrl, queue, mustSync := issuing.NewController(logf.Log, kubeClient, cmCl, factory, cmFactory, framework.NewEventRecorder(t), clock.RealClock{}, controllerOptions)
 	c := controllerpkg.NewController(
 		context.Background(),
-		"issuing_test",
-		metrics.New(logf.Log),
 		ctrl.ProcessItem,
 		mustSync,
 		nil,
diff --git a/test/integration/certificates/metrics_controller_test.go b/test/integration/certificates/metrics_controller_test.go
deleted file mode 100644
index e6e8d61b9..000000000
--- a/test/integration/certificates/metrics_controller_test.go
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
-Copyright 2020 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 certificates
-
-import (
-	"context"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"strings"
-	"testing"
-	"time"
-
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/wait"
-
-	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
-	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
-	controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
-	controllermetrics "github.com/jetstack/cert-manager/pkg/controller/certificates/metrics"
-	logf "github.com/jetstack/cert-manager/pkg/logs"
-	"github.com/jetstack/cert-manager/pkg/metrics"
-	"github.com/jetstack/cert-manager/test/integration/framework"
-	"github.com/jetstack/cert-manager/test/unit/gen"
-)
-
-// TestMetricscontoller performs a basic test to ensure that Certificates
-// metrics are exposed when a Certificate is created, updated, and removed when
-// it is deleted.
-func TestMetricsController(t *testing.T) {
-	config, stopFn := framework.RunControlPlane(t)
-	defer stopFn()
-
-	// Build, instantiate and run the issuing controller.
-	_, factory, cmClient, cmFactory := framework.NewClients(t, config)
-
-	metricsHandler := metrics.New(logf.Log)
-	server, err := metricsHandler.Start("127.0.0.1:0")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer metricsHandler.Shutdown(server)
-
-	ctrl, queue, mustSync := controllermetrics.NewController(factory, cmFactory, metricsHandler)
-	c := controllerpkg.NewController(
-		context.Background(),
-		"metrics_test",
-		metricsHandler,
-		ctrl.ProcessItem,
-		mustSync,
-		nil,
-		queue,
-	)
-	stopController := framework.StartInformersAndController(t, factory, cmFactory, c)
-	defer stopController()
-
-	ctx, cancel := context.WithTimeout(context.TODO(), time.Second*20)
-	defer cancel()
-
-	var (
-		crtName         = "testcrt"
-		namespace       = "testns"
-		metricsEndpoint = fmt.Sprintf("http://%s/metrics", server.Addr)
-
-		lastErr error
-	)
-
-	testMetrics := func(expectedOutput string) error {
-		resp, err := http.DefaultClient.Get(metricsEndpoint)
-		if err != nil {
-			return err
-		}
-
-		output, err := ioutil.ReadAll(resp.Body)
-		if err != nil {
-			return err
-		}
-
-		if strings.TrimSpace(string(output)) != strings.TrimSpace(expectedOutput) {
-			return fmt.Errorf("got unexpected metrics output\nexp:\n%s\ngot:\n%s\n",
-				expectedOutput, output)
-		}
-
-		return nil
-	}
-
-	waitForMetrics := func(expectedOutput string) {
-		err := wait.Poll(time.Millisecond*100, time.Second*5, func() (done bool, err error) {
-			if err := testMetrics(expectedOutput); err != nil {
-				lastErr = err
-				return false, nil
-			}
-
-			return true, nil
-		})
-		if err != nil {
-			t.Fatalf("%s: failed to wait for expected metrics to be exposed: %s", err, lastErr)
-		}
-	}
-
-	// Should expose no metrics
-	waitForMetrics("")
-
-	// Create Certificate
-	crt := gen.Certificate(crtName,
-		gen.SetCertificateNamespace(namespace),
-		gen.SetCertificateUID("uid-1"),
-	)
-
-	crt, err = cmClient.CertmanagerV1alpha2().Certificates(namespace).Create(ctx, crt, metav1.CreateOptions{})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Should expose that Certificate as unkown with no expiry
-	waitForMetrics(`# HELP certmanager_certificate_expiration_timestamp_seconds The date after which the certificate expires. Expressed as a Unix Epoch Time.
-# TYPE certmanager_certificate_expiration_timestamp_seconds gauge
-certmanager_certificate_expiration_timestamp_seconds{name="testcrt",namespace="testns"} 0
-# HELP certmanager_certificate_ready_status The ready status of the certificate.
-# TYPE certmanager_certificate_ready_status gauge
-certmanager_certificate_ready_status{condition="False",name="testcrt",namespace="testns"} 0
-certmanager_certificate_ready_status{condition="True",name="testcrt",namespace="testns"} 0
-certmanager_certificate_ready_status{condition="Unknown",name="testcrt",namespace="testns"} 1
-# HELP certmanager_controller_sync_call_count The number of sync() calls made by a controller.
-# TYPE certmanager_controller_sync_call_count counter
-certmanager_controller_sync_call_count{controller="metrics_test"} 1
-`)
-
-	// Set Certificate Expiry and Ready status True
-	crt.Status.NotAfter = &metav1.Time{
-		Time: time.Unix(100, 0),
-	}
-	crt.Status.Conditions = []cmapi.CertificateCondition{
-		{
-			Type:   cmapi.CertificateConditionReady,
-			Status: cmmeta.ConditionTrue,
-		},
-	}
-	_, err = cmClient.CertmanagerV1alpha2().Certificates(namespace).UpdateStatus(ctx, crt, metav1.UpdateOptions{})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Should expose that Certificate as ready with expiry
-	waitForMetrics(`# HELP certmanager_certificate_expiration_timestamp_seconds The date after which the certificate expires. Expressed as a Unix Epoch Time.
-# TYPE certmanager_certificate_expiration_timestamp_seconds gauge
-certmanager_certificate_expiration_timestamp_seconds{name="testcrt",namespace="testns"} 100
-# HELP certmanager_certificate_ready_status The ready status of the certificate.
-# TYPE certmanager_certificate_ready_status gauge
-certmanager_certificate_ready_status{condition="False",name="testcrt",namespace="testns"} 0
-certmanager_certificate_ready_status{condition="True",name="testcrt",namespace="testns"} 1
-certmanager_certificate_ready_status{condition="Unknown",name="testcrt",namespace="testns"} 0
-# HELP certmanager_controller_sync_call_count The number of sync() calls made by a controller.
-# TYPE certmanager_controller_sync_call_count counter
-certmanager_controller_sync_call_count{controller="metrics_test"} 2
-`)
-
-	err = cmClient.CertmanagerV1alpha2().Certificates(namespace).Delete(ctx, crt.Name, metav1.DeleteOptions{})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Should expose no Certificates and only metrics sync count increase
-	waitForMetrics(`# HELP certmanager_controller_sync_call_count The number of sync() calls made by a controller.
-# TYPE certmanager_controller_sync_call_count counter
-certmanager_controller_sync_call_count{controller="metrics_test"} 3
-`)
-}
diff --git a/test/integration/certificates/trigger_controller_test.go b/test/integration/certificates/trigger_controller_test.go
index 891778bbd..e10879131 100644
--- a/test/integration/certificates/trigger_controller_test.go
+++ b/test/integration/certificates/trigger_controller_test.go
@@ -32,7 +32,6 @@ import (
 	"github.com/jetstack/cert-manager/pkg/controller/expcertificates/trigger"
 	"github.com/jetstack/cert-manager/pkg/controller/expcertificates/trigger/policies"
 	logf "github.com/jetstack/cert-manager/pkg/logs"
-	"github.com/jetstack/cert-manager/pkg/metrics"
 	"github.com/jetstack/cert-manager/test/integration/framework"
 )
 
@@ -45,16 +44,11 @@ func TestTriggerController(t *testing.T) {
 	config, stopFn := framework.RunControlPlane(t)
 	defer stopFn()
 
-	ctx, cancel := context.WithTimeout(context.TODO(), time.Second*20)
-	defer cancel()
-
 	// Build, instantiate and run the trigger controller.
 	_, factory, cmCl, cmFactory := framework.NewClients(t, config)
 	ctrl, queue, mustSync := trigger.NewController(logf.Log, cmCl, factory, cmFactory, framework.NewEventRecorder(t), clock.RealClock{}, policies.TriggerPolicyChain)
 	c := controllerpkg.NewController(
 		context.Background(),
-		"trigger_test",
-		metrics.New(logf.Log),
 		ctrl.ProcessItem,
 		mustSync,
 		nil,
@@ -63,6 +57,7 @@ func TestTriggerController(t *testing.T) {
 	stopController := framework.StartInformersAndController(t, factory, cmFactory, c)
 	defer stopController()
 
+	ctx := context.TODO()
 	// Create a Certificate resource and wait for it to have the 'Issuing' condition.
 	cert, err := cmCl.CertmanagerV1alpha2().Certificates("testns").Create(ctx, &cmapi.Certificate{
 		ObjectMeta: metav1.ObjectMeta{Name: "testcrt", Namespace: "testns"},
@@ -76,7 +71,7 @@ func TestTriggerController(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	err = wait.Poll(time.Millisecond*100, time.Second*5, func() (done bool, err error) {
+	wait.Poll(time.Millisecond*100, time.Second*5, func() (done bool, err error) {
 		c, err := cmCl.CertmanagerV1alpha2().Certificates(cert.Namespace).Get(ctx, cert.Name, metav1.GetOptions{})
 		if err != nil {
 			t.Logf("Failed to fetch Certificate resource, retrying: %v", err)
@@ -91,7 +86,4 @@ func TestTriggerController(t *testing.T) {
 		}
 		return true, nil
 	})
-	if err != nil {
-		t.Fatal(err)
-	}
 }
diff --git a/test/integration/ctl/ctl_convert_test.go b/test/integration/ctl/ctl_convert_test.go
index a91d667d9..f135135b7 100644
--- a/test/integration/ctl/ctl_convert_test.go
+++ b/test/integration/ctl/ctl_convert_test.go
@@ -26,9 +26,10 @@ import (
 )
 
 const (
-	testdataResource1 = "./testdata/convert_resource1.yaml"
-	testdataResource2 = "./testdata/convert_resource2.yaml"
-	testdataResource3 = "./testdata/convert_resource3.yaml"
+	testdataResource1                        = "./testdata/convert_resource1.yaml"
+	testdataResource2                        = "./testdata/convert_resource2.yaml"
+	testdataResource3                        = "./testdata/convert_resource3.yaml"
+	testdataResourceWithOrganizationV1alpha2 = "./testdata/convert_resource_with_organization_v1alpha2.yaml"
 
 	targetv1alpha2 = "cert-manager.io/v1alpha2"
 	targetv1alpha3 = "cert-manager.io/v1alpha3"
@@ -82,6 +83,29 @@ func TestCtlConvert(t *testing.T) {
 			targetVersion: targetv1alpha3,
 			expErr:        true,
 		},
+		"an object in v1alpha2 that uses a field that has been renamed in v1alpha3 should be converted properly": {
+			input:         testdataResourceWithOrganizationV1alpha2,
+			targetVersion: targetv1alpha3,
+			expOutput: `
+apiVersion: cert-manager.io/v1alpha3
+kind: Certificate
+metadata:
+  creationTimestamp: null
+  name: ca-issuer
+  namespace: sandbox
+spec:
+  commonName: my-csi-app
+  isCA: true
+  issuerRef:
+    group: cert-manager.io
+    kind: Issuer
+    name: selfsigned-issuer
+  secretName: ca-key-pair
+  subject:
+    organizations:
+    - hello world
+status: {}`,
+		},
 	}
 
 	for name, test := range tests {
@@ -104,7 +128,7 @@ func TestCtlConvert(t *testing.T) {
 			}
 
 			if strings.TrimSpace(test.expOutput) != strings.TrimSpace(outBuf.String()) {
-				t.Errorf("got unexpected output, exp=%s got=%s",
+				t.Errorf("got unexpected output, exp=%s\n got=%s",
 					strings.TrimSpace(test.expOutput), strings.TrimSpace(outBuf.String()))
 			}
 		})
diff --git a/test/integration/ctl/testdata/convert_resource_with_organization_v1alpha2.yaml b/test/integration/ctl/testdata/convert_resource_with_organization_v1alpha2.yaml
new file mode 100644
index 000000000..cf23a6d1b
--- /dev/null
+++ b/test/integration/ctl/testdata/convert_resource_with_organization_v1alpha2.yaml
@@ -0,0 +1,15 @@
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: ca-issuer
+  namespace: sandbox
+spec:
+  isCA: true
+  secretName: ca-key-pair
+  organization:
+  - "hello world"
+  commonName: my-csi-app
+  issuerRef:
+    name: selfsigned-issuer
+    kind: Issuer
+    group: cert-manager.io
diff --git a/test/unit/gen/BUILD.bazel b/test/unit/gen/BUILD.bazel
index d2491cfcc..54592013d 100644
--- a/test/unit/gen/BUILD.bazel
+++ b/test/unit/gen/BUILD.bazel
@@ -22,7 +22,6 @@ go_library(
         "//pkg/util/pki:go_default_library",
         "@io_k8s_api//core/v1:go_default_library",
         "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
-        "@io_k8s_apimachinery//pkg/types:go_default_library",
     ],
 )
 
diff --git a/test/unit/gen/certificate.go b/test/unit/gen/certificate.go
index d86d827c9..df7e63e35 100644
--- a/test/unit/gen/certificate.go
+++ b/test/unit/gen/certificate.go
@@ -20,7 +20,6 @@ import (
 	"time"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
 
 	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
 	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
@@ -177,12 +176,6 @@ func SetCertificateRevision(revision int) CertificateModifier {
 	}
 }
 
-func SetCertificateUID(uid types.UID) CertificateModifier {
-	return func(crt *v1alpha2.Certificate) {
-		crt.UID = uid
-	}
-}
-
 func AddCertificateAnnotations(annotations map[string]string) CertificateModifier {
 	return func(crt *v1alpha2.Certificate) {
 		if crt.Annotations == nil {