Merge pull request #6030 from inteon/remove_deprecated_2

Replace deprecated wait.PollImmediate
This commit is contained in:
jetstack-bot 2023-05-09 15:31:55 +01:00 committed by GitHub
commit 3fee31c0c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 182 additions and 212 deletions

View File

@ -254,7 +254,7 @@ func (o InjectorControllerOptions) RunInjectorController(ctx context.Context) er
if err != nil {
return fmt.Errorf("failed to create client: %w", err)
}
err = wait.PollImmediate(time.Second, time.Minute*5, func() (bool, error) {
err = wait.PollUntilContextTimeout(ctx, time.Second, time.Minute*5, true, func(ctx context.Context) (bool, error) {
certsCRDName := types.NamespacedName{Name: "certificates.cert-manager.io"}
certsCRD := apiext.CustomResourceDefinition{}
err := directClient.Get(ctx, certsCRDName, &certsCRD)

View File

@ -208,7 +208,7 @@ func (v *VaultInitializer) Init() error {
return fmt.Errorf("error parsing proxy URL: %s", err.Error())
}
var lastError error
err = wait.PollImmediate(time.Second, 20*time.Second, func() (bool, error) {
err = wait.PollUntilContextTimeout(context.TODO(), time.Second, 20*time.Second, true, func(ctx context.Context) (bool, error) {
conn, err := net.DialTimeout("tcp", proxyUrl.Host, time.Second)
if err != nil {
lastError = err
@ -226,7 +226,7 @@ func (v *VaultInitializer) Init() error {
// Wait for Vault to be ready
{
var lastError error
err = wait.PollImmediate(time.Second, 20*time.Second, func() (bool, error) {
err = wait.PollUntilContextTimeout(context.TODO(), time.Second, 20*time.Second, true, func(ctx context.Context) (bool, error) {
_, err := v.client.Sys().Health()
if err != nil {
lastError = err

View File

@ -302,7 +302,7 @@ func (v *Vault) Provision() error {
}
var lastError error
err = wait.PollImmediate(5*time.Second, 5*time.Minute, func() (bool, error) {
err = wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) {
pod, err := kubeClient.CoreV1().Pods(v.proxy.podNamespace).Get(context.TODO(), v.proxy.podName, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return false, err

View File

@ -43,25 +43,23 @@ func (h *Helper) WaitForCertificateRequestReady(ns, name string, timeout time.Du
var cr *cmapi.CertificateRequest
logf, done := log.LogBackoff()
defer done()
err := wait.PollImmediate(time.Second, timeout,
func() (bool, error) {
var err error
logf("Waiting for CertificateRequest %s to be ready", name)
cr, err = h.CMClient.CertmanagerV1().CertificateRequests(ns).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error getting CertificateRequest %s: %v", name, err)
}
isReady := apiutil.CertificateRequestHasCondition(cr, cmapi.CertificateRequestCondition{
Type: cmapi.CertificateRequestConditionReady,
Status: cmmeta.ConditionTrue,
})
if !isReady {
logf("Expected CertificateRequest to have Ready condition 'true' but it has: %v", cr.Status.Conditions)
return false, nil
}
return true, nil
},
)
err := wait.PollUntilContextTimeout(context.TODO(), time.Second, timeout, true, func(ctx context.Context) (bool, error) {
var err error
logf("Waiting for CertificateRequest %s to be ready", name)
cr, err = h.CMClient.CertmanagerV1().CertificateRequests(ns).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error getting CertificateRequest %s: %v", name, err)
}
isReady := apiutil.CertificateRequestHasCondition(cr, cmapi.CertificateRequestCondition{
Type: cmapi.CertificateRequestConditionReady,
Status: cmmeta.ConditionTrue,
})
if !isReady {
logf("Expected CertificateRequest to have Ready condition 'true' but it has: %v", cr.Status.Conditions)
return false, nil
}
return true, nil
})
if err != nil {
return nil, err

View File

@ -41,7 +41,8 @@ func (h *Helper) WaitForCertificateToExist(namespace string, name string, timeou
var certificate *v1.Certificate
logf, done := log.LogBackoff()
defer done()
pollErr := wait.PollImmediate(500*time.Millisecond, timeout, func() (bool, error) {
pollErr := wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, timeout, true, func(ctx context.Context) (bool, error) {
logf("Waiting for Certificate %v to exist", name)
var err error
certificate, err = client.Get(context.TODO(), name, metav1.GetOptions{})
@ -59,7 +60,7 @@ func (h *Helper) WaitForCertificateToExist(namespace string, name string, timeou
func (h *Helper) waitForCertificateCondition(client clientset.CertificateInterface, name string, check func(*v1.Certificate) bool, timeout time.Duration) (*cmapi.Certificate, error) {
var certificate *v1.Certificate
pollErr := wait.PollImmediate(500*time.Millisecond, timeout, func() (bool, error) {
pollErr := wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, timeout, true, func(ctx context.Context) (bool, error) {
var err error
certificate, err = client.Get(context.TODO(), name, metav1.GetOptions{})
if nil != err {
@ -174,7 +175,7 @@ func (h *Helper) WaitForCertificateNotReadyAndDoneIssuing(cert *cmapi.Certificat
func (h *Helper) waitForIssuerCondition(client clientset.IssuerInterface, name string, check func(issuer *v1.Issuer) bool, timeout time.Duration) (*cmapi.Issuer, error) {
var issuer *v1.Issuer
pollErr := wait.PollImmediate(500*time.Millisecond, timeout, func() (bool, error) {
pollErr := wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, timeout, true, func(ctx context.Context) (bool, error) {
var err error
issuer, err = client.Get(context.TODO(), name, metav1.GetOptions{})
if nil != err {
@ -221,7 +222,7 @@ func (h *Helper) WaitIssuerReady(issuer *cmapi.Issuer, timeout time.Duration) (*
func (h *Helper) waitForClusterIssuerCondition(client clientset.ClusterIssuerInterface, name string, check func(issuer *v1.ClusterIssuer) bool, timeout time.Duration) (*cmapi.ClusterIssuer, error) {
var issuer *v1.ClusterIssuer
pollErr := wait.PollImmediate(500*time.Millisecond, timeout, func() (bool, error) {
pollErr := wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, timeout, true, func(ctx context.Context) (bool, error) {
var err error
issuer, err = client.Get(context.TODO(), name, metav1.GetOptions{})
if nil != err {

View File

@ -35,25 +35,23 @@ func (h *Helper) WaitForCertificateSigningRequestSigned(name string, timeout tim
var csr *certificatesv1.CertificateSigningRequest
logf, done := log.LogBackoff()
defer done()
err := wait.PollImmediate(time.Second, timeout,
func() (bool, error) {
var err error
logf("Waiting for CertificateSigningRequest %s to be ready", name)
csr, err = h.KubeClient.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error getting CertificateSigningRequest %s: %v", name, err)
}
err := wait.PollUntilContextTimeout(context.TODO(), time.Second, timeout, true, func(ctx context.Context) (bool, error) {
var err error
logf("Waiting for CertificateSigningRequest %s to be ready", name)
csr, err = h.KubeClient.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error getting CertificateSigningRequest %s: %v", name, err)
}
if util.CertificateSigningRequestIsFailed(csr) {
return false, fmt.Errorf("CertificateSigningRequest has failed: %v", csr.Status)
}
if util.CertificateSigningRequestIsFailed(csr) {
return false, fmt.Errorf("CertificateSigningRequest has failed: %v", csr.Status)
}
if len(csr.Status.Certificate) == 0 {
return false, nil
}
return true, nil
},
)
if len(csr.Status.Certificate) == 0 {
return false, nil
}
return true, nil
})
if err != nil {
return nil, err

View File

@ -47,7 +47,7 @@ func (h *Helper) WaitForAllPodsRunningInNamespaceTimeout(ns string, timeout time
ginkgo.By("Waiting " + timeout.String() + " for all pods in namespace '" + ns + "' to be Ready")
logf, done := log.LogBackoff()
defer done()
return wait.PollImmediate(Poll, timeout, func() (bool, error) {
return wait.PollUntilContextTimeout(context.TODO(), Poll, timeout, true, func(ctx context.Context) (bool, error) {
pods, err := h.KubeClient.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return false, err

View File

@ -34,24 +34,22 @@ func (h *Helper) WaitForSecretCertificateData(ns, name string, timeout time.Dura
var secret *corev1.Secret
logf, done := log.LogBackoff()
defer done()
err := wait.PollImmediate(time.Second, timeout,
func() (bool, error) {
var err error
logf("Waiting for Secret %s:%s to contain a certificate", ns, name)
secret, err = h.KubeClient.CoreV1().Secrets(ns).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error getting secret %s: %s", name, err)
}
err := wait.PollUntilContextTimeout(context.TODO(), time.Second, timeout, true, func(ctx context.Context) (bool, error) {
var err error
logf("Waiting for Secret %s:%s to contain a certificate", ns, name)
secret, err = h.KubeClient.CoreV1().Secrets(ns).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error getting secret %s: %s", name, err)
}
if len(secret.Data[corev1.TLSCertKey]) > 0 {
return true, nil
}
if len(secret.Data[corev1.TLSCertKey]) > 0 {
return true, nil
}
logf("Secret still does not contain certificate data %s/%s",
secret.Namespace, secret.Name)
return false, nil
},
)
logf("Secret still does not contain certificate data %s/%s",
secret.Namespace, secret.Name)
return false, nil
})
if err != nil {
return nil, err

View File

@ -26,7 +26,6 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
)
// Defines methods that help provision test environments
@ -77,12 +76,8 @@ func (f *Framework) DeleteKubeNamespace(namespace string) error {
// WaitForKubeNamespaceNotExist will wait for the namespace with the given name
// to not exist for up to 2 minutes.
func (f *Framework) WaitForKubeNamespaceNotExist(namespace string) error {
return wait.PollImmediate(Poll, time.Minute*2, namespaceNotExist(f.KubeClientSet, namespace))
}
func namespaceNotExist(c kubernetes.Interface, namespace string) wait.ConditionFunc {
return func() (bool, error) {
_, err := c.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
return wait.PollUntilContextTimeout(context.TODO(), Poll, time.Minute*2, true, func(ctx context.Context) (bool, error) {
_, err := f.KubeClientSet.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return true, nil
}
@ -90,5 +85,5 @@ func namespaceNotExist(c kubernetes.Interface, namespace string) wait.ConditionF
return false, err
}
return false, nil
}
})
}

View File

@ -174,23 +174,21 @@ var _ = framework.CertManagerDescribe("Approval CertificateRequests", func() {
token []byte
ok bool
)
err = wait.PollImmediate(time.Second, time.Second*10,
func() (bool, error) {
secret, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), secret.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
err = wait.PollUntilContextTimeout(context.TODO(), time.Second, time.Second*10, true, func(ctx context.Context) (bool, error) {
secret, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(ctx, secret.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
if len(secret.Data) == 0 {
return false, nil
}
if token, ok = secret.Data["token"]; !ok {
return false, nil
}
if len(secret.Data) == 0 {
return false, nil
}
if token, ok = secret.Data["token"]; !ok {
return false, nil
}
return true, nil
},
)
return true, nil
})
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error: %s", err))
By("Building ServiceAccount kubernetes clientset")

View File

@ -142,23 +142,21 @@ var _ = framework.CertManagerDescribe("UserInfo CertificateRequests", func() {
ok bool
)
By("Waiting for service account secret to be created")
err = wait.PollImmediate(time.Second, time.Second*10,
func() (bool, error) {
secret, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), secret.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
err = wait.PollUntilContextTimeout(context.TODO(), time.Second, time.Second*10, true, func(ctx context.Context) (bool, error) {
secret, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(ctx, secret.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
if len(secret.Data) == 0 {
return false, nil
}
if token, ok = secret.Data["token"]; !ok {
return false, nil
}
if len(secret.Data) == 0 {
return false, nil
}
if token, ok = secret.Data["token"]; !ok {
return false, nil
}
return true, nil
},
)
return true, nil
})
Expect(err).NotTo(HaveOccurred())
By("Building ServiceAccount kubernetes clientset")

View File

@ -153,7 +153,7 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
order := &cmacme.Order{}
logf, done := log.LogBackoff()
defer done()
err = wait.PollImmediate(1*time.Second, 1*time.Minute, func() (done bool, err error) {
err = wait.PollUntilContextTimeout(context.TODO(), 1*time.Second, 1*time.Minute, true, func(ctx context.Context) (done bool, err error) {
orders, err := listOwnedOrders(f.CertManagerClientSet, cert)
Expect(err).NotTo(HaveOccurred())
@ -440,26 +440,24 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
var pod corev1.Pod
logf, done := log.LogBackoff()
defer done()
err = wait.PollImmediate(1*time.Second, time.Minute*3,
func() (bool, error) {
logf("Waiting for solver pod to exist")
podlist, err := podClient.List(context.TODO(), metav1.ListOptions{})
if err != nil {
return false, err
}
err = wait.PollUntilContextTimeout(context.TODO(), 1*time.Second, time.Minute*3, true, func(ctx context.Context) (bool, error) {
logf("Waiting for solver pod to exist")
podlist, err := podClient.List(ctx, metav1.ListOptions{})
if err != nil {
return false, err
}
for _, p := range podlist.Items {
logf("solver pod %s", p.Name)
// TODO(dmo): make this cleaner instead of just going by name
if strings.Contains(p.Name, "http-solver") {
pod = p
return true, nil
}
for _, p := range podlist.Items {
logf("solver pod %s", p.Name)
// TODO(dmo): make this cleaner instead of just going by name
if strings.Contains(p.Name, "http-solver") {
pod = p
return true, nil
}
return false, nil
}
return false, nil
},
)
})
Expect(err).NotTo(HaveOccurred())
err = podClient.Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})

View File

@ -128,50 +128,46 @@ var _ = framework.CertManagerDescribe("ACME webhook DNS provider", func() {
var order *cmacme.Order
logf, done := log.LogBackoff()
defer done()
pollErr := wait.PollImmediate(2*time.Second, time.Minute*1,
func() (bool, error) {
orders, err := listOwnedOrders(f.CertManagerClientSet, cert)
Expect(err).NotTo(HaveOccurred())
pollErr := wait.PollUntilContextTimeout(context.TODO(), 2*time.Second, time.Minute*1, true, func(ctx context.Context) (bool, error) {
orders, err := listOwnedOrders(f.CertManagerClientSet, cert)
Expect(err).NotTo(HaveOccurred())
logf("Found %d orders for certificate", len(orders))
if len(orders) == 1 {
order = orders[0]
logf("Found order named %q", order.Name)
return true, nil
}
logf("Found %d orders for certificate", len(orders))
if len(orders) == 1 {
order = orders[0]
logf("Found order named %q", order.Name)
return true, nil
}
logf("Waiting as one Order should exist, but we found %d", len(orders))
return false, nil
},
)
logf("Waiting as one Order should exist, but we found %d", len(orders))
return false, nil
})
Expect(pollErr).NotTo(HaveOccurred())
logf, done = log.LogBackoff()
defer done()
pollErr = wait.PollImmediate(2*time.Second, time.Minute*3,
func() (bool, error) {
l, err := listOwnedChallenges(f.CertManagerClientSet, order)
Expect(err).NotTo(HaveOccurred())
pollErr = wait.PollUntilContextTimeout(context.TODO(), 2*time.Second, time.Minute*3, true, func(ctx context.Context) (bool, error) {
l, err := listOwnedChallenges(f.CertManagerClientSet, order)
Expect(err).NotTo(HaveOccurred())
logf("Found %d challenges", len(l))
if len(l) == 0 {
logf("Waiting for at least one challenge to exist")
return false, nil
logf("Found %d challenges", len(l))
if len(l) == 0 {
logf("Waiting for at least one challenge to exist")
return false, nil
}
allPresented := true
for _, ch := range l {
logf("Found challenge named %q", ch.Name)
if ch.Status.Presented == false {
logf("Challenge %q has not been 'Presented'", ch.Name)
allPresented = false
}
}
allPresented := true
for _, ch := range l {
logf("Found challenge named %q", ch.Name)
if ch.Status.Presented == false {
logf("Challenge %q has not been 'Presented'", ch.Name)
allPresented = false
}
}
return allPresented, nil
},
)
return allPresented, nil
})
Expect(pollErr).NotTo(HaveOccurred())
})
})

View File

@ -220,26 +220,23 @@ var _ = framework.CertManagerDescribe("ACME CertificateRequest (HTTP01)", func()
var pod corev1.Pod
logf, done := log.LogBackoff()
defer done()
err = wait.PollImmediate(1*time.Second, time.Minute*3,
func() (bool, error) {
logf("Waiting for solver pod to exist")
podlist, err := podClient.List(context.TODO(), metav1.ListOptions{})
if err != nil {
return false, err
}
err = wait.PollUntilContextTimeout(context.TODO(), 1*time.Second, time.Minute*3, true, func(ctx context.Context) (bool, error) {
logf("Waiting for solver pod to exist")
podlist, err := podClient.List(ctx, metav1.ListOptions{})
if err != nil {
return false, err
}
for _, p := range podlist.Items {
logf("solver pod %s", p.Name)
// TODO(dmo): make this cleaner instead of just going by name
if strings.Contains(p.Name, "http-solver") {
pod = p
return true, nil
}
for _, p := range podlist.Items {
logf("solver pod %s", p.Name)
// TODO(dmo): make this cleaner instead of just going by name
if strings.Contains(p.Name, "http-solver") {
pod = p
return true, nil
}
return false, nil
},
)
}
return false, nil
})
Expect(err).NotTo(HaveOccurred())
err = podClient.Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})

View File

@ -57,14 +57,13 @@ func CertificateOnlyValidForDomains(cert *x509.Certificate, commonName string, d
}
func WaitForIssuerStatusFunc(client clientset.IssuerInterface, name string, fn func(*v1.Issuer) (bool, error)) error {
return wait.PollImmediate(500*time.Millisecond, time.Minute,
func() (bool, error) {
issuer, err := client.Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error getting Issuer %q: %v", name, err)
}
return fn(issuer)
})
return wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, time.Minute, true, func(ctx context.Context) (bool, error) {
issuer, err := client.Get(ctx, name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error getting Issuer %q: %v", name, err)
}
return fn(issuer)
})
}
// WaitForIssuerCondition waits for the status of the named issuer to contain
@ -72,17 +71,15 @@ func WaitForIssuerStatusFunc(client clientset.IssuerInterface, name string, fn f
func WaitForIssuerCondition(client clientset.IssuerInterface, name string, condition v1.IssuerCondition) error {
logf, done := log.LogBackoff()
defer done()
pollErr := wait.PollImmediate(500*time.Millisecond, time.Minute,
func() (bool, error) {
logf("Waiting for issuer %v condition %#v", name, condition)
issuer, err := client.Get(context.TODO(), name, metav1.GetOptions{})
if nil != err {
return false, fmt.Errorf("error getting Issuer %q: %v", name, err)
}
pollErr := wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, time.Minute, true, func(ctx context.Context) (bool, error) {
logf("Waiting for issuer %v condition %#v", name, condition)
issuer, err := client.Get(ctx, name, metav1.GetOptions{})
if nil != err {
return false, fmt.Errorf("error getting Issuer %q: %v", name, err)
}
return apiutil.IssuerHasCondition(issuer, condition), nil
},
)
return apiutil.IssuerHasCondition(issuer, condition), nil
})
return wrapErrorWithIssuerStatusCondition(client, pollErr, name, condition.Type)
}
@ -112,17 +109,15 @@ func wrapErrorWithIssuerStatusCondition(client clientset.IssuerInterface, pollEr
func WaitForClusterIssuerCondition(client clientset.ClusterIssuerInterface, name string, condition v1.IssuerCondition) error {
logf, done := log.LogBackoff()
defer done()
pollErr := wait.PollImmediate(500*time.Millisecond, time.Minute,
func() (bool, error) {
logf("Waiting for clusterissuer %v condition %#v", name, condition)
issuer, err := client.Get(context.TODO(), name, metav1.GetOptions{})
if nil != err {
return false, fmt.Errorf("error getting ClusterIssuer %v: %v", name, err)
}
pollErr := wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, time.Minute, true, func(ctx context.Context) (bool, error) {
logf("Waiting for clusterissuer %v condition %#v", name, condition)
issuer, err := client.Get(ctx, name, metav1.GetOptions{})
if nil != err {
return false, fmt.Errorf("error getting ClusterIssuer %v: %v", name, err)
}
return apiutil.IssuerHasCondition(issuer, condition), nil
},
)
return apiutil.IssuerHasCondition(issuer, condition), nil
})
return wrapErrorWithClusterIssuerStatusCondition(client, pollErr, name, condition.Type)
}
@ -152,21 +147,19 @@ func wrapErrorWithClusterIssuerStatusCondition(client clientset.ClusterIssuerInt
func WaitForCRDToNotExist(client apiextensionsv1.CustomResourceDefinitionInterface, name string) error {
logf, done := log.LogBackoff()
defer done()
return wait.PollImmediate(500*time.Millisecond, time.Minute,
func() (bool, error) {
logf("Waiting for CRD %v to not exist", name)
_, err := client.Get(context.TODO(), name, metav1.GetOptions{})
if nil == err {
return false, nil
}
if errors.IsNotFound(err) {
return true, nil
}
return wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, time.Minute, true, func(ctx context.Context) (bool, error) {
logf("Waiting for CRD %v to not exist", name)
_, err := client.Get(ctx, name, metav1.GetOptions{})
if nil == err {
return false, nil
},
)
}
if errors.IsNotFound(err) {
return true, nil
}
return false, nil
})
}
// Deprecated: use test/unit/gen/Certificate in future