Add duration into ACME
Signed-off-by: Maartje Eyskens <maartje@eyskens.me>
This commit is contained in:
parent
811b67bb8c
commit
7b6573aa35
@ -75,6 +75,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -1087,6 +1090,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -2101,6 +2107,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -3115,6 +3124,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
|
||||
@ -75,6 +75,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -1101,6 +1104,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -2129,6 +2135,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -3157,6 +3166,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
|
||||
@ -75,6 +75,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -1087,6 +1090,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -2101,6 +2107,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -3115,6 +3124,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
|
||||
@ -75,6 +75,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -1101,6 +1104,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -2129,6 +2135,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
@ -3157,6 +3166,9 @@ spec:
|
||||
email:
|
||||
description: Email is the email address to be associated with the ACME account. This field is optional, but it is strongly recommended to be set. It will be used to contact you in case of issues with your account or certificates, including expiry notification emails. This field may be updated after the account is initially registered.
|
||||
type: string
|
||||
enableNotAfterDate:
|
||||
description: Enables requesting a Not After date on certificates that matches the duration of the certificate. This is not supported by all ACME servers like Let's Encrypt. If set to true when the ACME server does not support it it will create an error on the Order. Defaults to false.
|
||||
type: boolean
|
||||
externalAccountBinding:
|
||||
description: ExternalAccountBinding is a reference to a CA external account of the ACME server. If set, upon registration cert-manager will attempt to associate the given external account credentials with the registered ACME account.
|
||||
type: object
|
||||
|
||||
@ -102,6 +102,10 @@ spec:
|
||||
name:
|
||||
description: Name of the resource being referred to.
|
||||
type: string
|
||||
notAfter:
|
||||
description: NotAfter is the date for the requested certificate's Not Valid After date
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
@ -238,6 +242,10 @@ spec:
|
||||
name:
|
||||
description: Name of the resource being referred to.
|
||||
type: string
|
||||
notAfter:
|
||||
description: NotAfter is the date for the requested certificate's Not Valid After date
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
@ -371,6 +379,10 @@ spec:
|
||||
name:
|
||||
description: Name of the resource being referred to.
|
||||
type: string
|
||||
notAfter:
|
||||
description: NotAfter is the date for the requested certificate's Not Valid After date
|
||||
type: string
|
||||
format: date-time
|
||||
request:
|
||||
description: Certificate signing request bytes in DER encoding. This will be used when finalizing the order. This field must be set on the order.
|
||||
type: string
|
||||
@ -508,6 +520,10 @@ spec:
|
||||
name:
|
||||
description: Name of the resource being referred to.
|
||||
type: string
|
||||
notAfter:
|
||||
description: NotAfter is the date for the requested certificate's Not Valid After date
|
||||
type: string
|
||||
format: date-time
|
||||
request:
|
||||
description: Certificate signing request bytes in DER encoding. This will be used when finalizing the order. This field must be set on the order.
|
||||
type: string
|
||||
|
||||
@ -102,6 +102,10 @@ spec:
|
||||
name:
|
||||
description: Name of the resource being referred to.
|
||||
type: string
|
||||
notAfter:
|
||||
description: NotAfter is the date for the requested certificate's Not Valid After date
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
@ -256,6 +260,10 @@ spec:
|
||||
name:
|
||||
description: Name of the resource being referred to.
|
||||
type: string
|
||||
notAfter:
|
||||
description: NotAfter is the date for the requested certificate's Not Valid After date
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
@ -407,6 +415,10 @@ spec:
|
||||
name:
|
||||
description: Name of the resource being referred to.
|
||||
type: string
|
||||
notAfter:
|
||||
description: NotAfter is the date for the requested certificate's Not Valid After date
|
||||
type: string
|
||||
format: date-time
|
||||
request:
|
||||
description: Certificate signing request bytes in DER encoding. This will be used when finalizing the order. This field must be set on the order.
|
||||
type: string
|
||||
@ -562,6 +574,10 @@ spec:
|
||||
name:
|
||||
description: Name of the resource being referred to.
|
||||
type: string
|
||||
notAfter:
|
||||
description: NotAfter is the date for the requested certificate's Not Valid After date
|
||||
type: string
|
||||
format: date-time
|
||||
request:
|
||||
description: Certificate signing request bytes in DER encoding. This will be used when finalizing the order. This field must be set on the order.
|
||||
type: string
|
||||
|
||||
@ -93,6 +93,14 @@ type ACMEIssuer struct {
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
DisableAccountKeyGeneration bool `json:"disableAccountKeyGeneration,omitempty"`
|
||||
|
||||
// Enables requesting a Not After date on certificates that matches the
|
||||
// duration of the certificate. This is not supported by all ACME servers
|
||||
// like Let's Encrypt. If set to true when the ACME server does not support
|
||||
// it it will create an error on the Order.
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
EnableNotAfterDate bool `json:"enableNotAfterDate,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEExternalAccountBinding is a reference to a CA external account of the ACME
|
||||
|
||||
@ -69,7 +69,7 @@ type OrderSpec struct {
|
||||
// DNSNames is a list of DNS names that should be included as part of the Order
|
||||
// validation process.
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
//+optonal
|
||||
//+optional
|
||||
DNSNames []string `json:"dnsNames,omitempty"`
|
||||
|
||||
// IPAddresses is a list of IP addresses that should be included as part of the Order
|
||||
@ -77,6 +77,10 @@ type OrderSpec struct {
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
// +optional
|
||||
IPAddresses []string `json:"ipAddresses,omitempty"`
|
||||
|
||||
// NotAfter is the date for the requested certificate's Not Valid After date
|
||||
// +optional
|
||||
NotAfter *metav1.Time `json:"notAfter,omitempty"`
|
||||
}
|
||||
|
||||
type OrderStatus struct {
|
||||
|
||||
@ -789,6 +789,10 @@ func (in *OrderSpec) DeepCopyInto(out *OrderSpec) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.NotAfter != nil {
|
||||
in, out := &in.NotAfter, &out.NotAfter
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -93,6 +93,14 @@ type ACMEIssuer struct {
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
DisableAccountKeyGeneration bool `json:"disableAccountKeyGeneration,omitempty"`
|
||||
|
||||
// Enables requesting a Not After date on certificates that matches the
|
||||
// duration of the certificate. This is not supported by all ACME servers
|
||||
// like Let's Encrypt. If set to true when the ACME server does not support
|
||||
// it it will create an error on the Order.
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
EnableNotAfterDate bool `json:"enableNotAfterDate,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEExternalAccountBinding is a reference to a CA external account of the ACME
|
||||
|
||||
@ -67,7 +67,7 @@ type OrderSpec struct {
|
||||
// DNSNames is a list of DNS names that should be included as part of the Order
|
||||
// validation process.
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
//+optonal
|
||||
//+optional
|
||||
DNSNames []string `json:"dnsNames,omitempty"`
|
||||
|
||||
// IPAddresses is a list of IP addresses that should be included as part of the Order
|
||||
@ -75,6 +75,10 @@ type OrderSpec struct {
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
// +optional
|
||||
IPAddresses []string `json:"ipAddresses,omitempty"`
|
||||
|
||||
// NotAfter is the date for the requested certificate's Not Valid After date
|
||||
// +optional
|
||||
NotAfter *metav1.Time `json:"notAfter,omitempty"`
|
||||
}
|
||||
|
||||
type OrderStatus struct {
|
||||
|
||||
@ -789,6 +789,10 @@ func (in *OrderSpec) DeepCopyInto(out *OrderSpec) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.NotAfter != nil {
|
||||
in, out := &in.NotAfter, &out.NotAfter
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -93,6 +93,14 @@ type ACMEIssuer struct {
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
DisableAccountKeyGeneration bool `json:"disableAccountKeyGeneration,omitempty"`
|
||||
|
||||
// Enables requesting a Not After date on certificates that matches the
|
||||
// duration of the certificate. This is not supported by all ACME servers
|
||||
// like Let's Encrypt. If set to true when the ACME server does not support
|
||||
// it it will create an error on the Order.
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
EnableNotAfterDate bool `json:"enableNotAfterDate,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEExternalAccountBinding is a reference to a CA external account of the ACME
|
||||
|
||||
@ -67,7 +67,7 @@ type OrderSpec struct {
|
||||
// DNSNames is a list of DNS names that should be included as part of the Order
|
||||
// validation process.
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
//+optonal
|
||||
//+optional
|
||||
DNSNames []string `json:"dnsNames,omitempty"`
|
||||
|
||||
// IPAddresses is a list of IP addresses that should be included as part of the Order
|
||||
@ -75,6 +75,10 @@ type OrderSpec struct {
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
// +optional
|
||||
IPAddresses []string `json:"ipAddresses,omitempty"`
|
||||
|
||||
// NotAfter is the date for the requested certificate's Not Valid After date
|
||||
// +optional
|
||||
NotAfter *metav1.Time `json:"notAfter,omitempty"`
|
||||
}
|
||||
|
||||
type OrderStatus struct {
|
||||
|
||||
@ -789,6 +789,10 @@ func (in *OrderSpec) DeepCopyInto(out *OrderSpec) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.NotAfter != nil {
|
||||
in, out := &in.NotAfter, &out.NotAfter
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -93,6 +93,14 @@ type ACMEIssuer struct {
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
DisableAccountKeyGeneration bool `json:"disableAccountKeyGeneration,omitempty"`
|
||||
|
||||
// Enables requesting a Not After date on certificates that matches the
|
||||
// duration of the certificate. This is not supported by all ACME servers
|
||||
// like Let's Encrypt. If set to true when the ACME server does not support
|
||||
// it it will create an error on the Order.
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
EnableNotAfterDate bool `json:"enableNotAfterDate,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEExternalAccountBinding is a reference to a CA external account of the ACME
|
||||
|
||||
@ -68,7 +68,7 @@ type OrderSpec struct {
|
||||
// DNSNames is a list of DNS names that should be included as part of the Order
|
||||
// validation process.
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
//+optonal
|
||||
//+optional
|
||||
DNSNames []string `json:"dnsNames,omitempty"`
|
||||
|
||||
// IPAddresses is a list of IP addresses that should be included as part of the Order
|
||||
@ -76,6 +76,10 @@ type OrderSpec struct {
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
// +optional
|
||||
IPAddresses []string `json:"ipAddresses,omitempty"`
|
||||
|
||||
// NotAfter is the date for the requested certificate's Not Valid After date
|
||||
// +optional
|
||||
NotAfter *metav1.Time `json:"notAfter,omitempty"`
|
||||
}
|
||||
|
||||
type OrderStatus struct {
|
||||
|
||||
@ -789,6 +789,10 @@ func (in *OrderSpec) DeepCopyInto(out *OrderSpec) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.NotAfter != nil {
|
||||
in, out := &in.NotAfter, &out.NotAfter
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -200,7 +200,12 @@ func (c *controller) createOrder(ctx context.Context, cl acmecl.Interface, o *cm
|
||||
authzIDs := acmeapi.DomainIDs(dnsIdentifierSet.List()...)
|
||||
authzIDs = append(authzIDs, acmeapi.IPIDs(ipIdentifierSet.List()...)...)
|
||||
// create a new order with the acme server
|
||||
acmeOrder, err := cl.AuthorizeOrder(ctx, authzIDs)
|
||||
|
||||
var options []acmeapi.OrderOption
|
||||
if o.Spec.NotAfter != nil {
|
||||
options = append(options, acmeapi.WithOrderNotAfter(o.Spec.NotAfter.Time))
|
||||
}
|
||||
acmeOrder, err := cl.AuthorizeOrder(ctx, authzIDs, options...)
|
||||
if acmeErr, ok := err.(*acmeapi.Error); ok {
|
||||
if acmeErr.StatusCode >= 400 && acmeErr.StatusCode < 500 {
|
||||
log.Error(err, "failed to create Order resource due to bad request, marking Order as failed")
|
||||
|
||||
@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -105,7 +106,7 @@ func (a *ACME) Sign(ctx context.Context, cr *v1.CertificateRequest, issuer v1.Ge
|
||||
}
|
||||
|
||||
// If we fail to build the order we have to hard fail.
|
||||
expectedOrder, err := buildOrder(cr, csr)
|
||||
expectedOrder, err := buildOrder(cr, csr, issuer)
|
||||
if err != nil {
|
||||
message := "Failed to build order"
|
||||
|
||||
@ -199,7 +200,7 @@ func (a *ACME) Sign(ctx context.Context, cr *v1.CertificateRequest, issuer v1.Ge
|
||||
}
|
||||
|
||||
// Build order. If we error here it is a terminating failure.
|
||||
func buildOrder(cr *v1.CertificateRequest, csr *x509.CertificateRequest) (*cmacme.Order, error) {
|
||||
func buildOrder(cr *v1.CertificateRequest, csr *x509.CertificateRequest, issuer v1.GenericIssuer) (*cmacme.Order, error) {
|
||||
var ipAddresses []string
|
||||
for _, ip := range csr.IPAddresses {
|
||||
ipAddresses = append(ipAddresses, ip.String())
|
||||
@ -218,9 +219,15 @@ func buildOrder(cr *v1.CertificateRequest, csr *x509.CertificateRequest) (*cmacm
|
||||
IPAddresses: ipAddresses,
|
||||
}
|
||||
|
||||
if issuer.GetSpec().ACME.EnableNotAfterDate {
|
||||
notAfterTime := metav1.NewTime(time.Now().Add(cr.Spec.Duration.Duration))
|
||||
spec.NotAfter = ¬AfterTime
|
||||
}
|
||||
|
||||
computeNameSpec := spec.DeepCopy()
|
||||
// create a deep copy of the OrderSpec so we can overwrite the Request field
|
||||
// create a deep copy of the OrderSpec so we can overwrite the Request and NotAfter field
|
||||
computeNameSpec.Request = nil
|
||||
computeNameSpec.NotAfter = nil // NotAfter is time based and will shift, confusing the controller to reconcile
|
||||
name, err := apiutil.ComputeName(cr.Name, computeNameSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -153,7 +153,7 @@ func TestSign(t *testing.T) {
|
||||
t.Fatalf("failed to build order during testing: %s", err)
|
||||
}
|
||||
|
||||
baseOrder, err := buildOrder(baseCR, csr)
|
||||
baseOrder, err := buildOrder(baseCR, csr, baseIssuer)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to build order during testing: %s", err)
|
||||
}
|
||||
|
||||
@ -85,6 +85,13 @@ type ACMEIssuer struct {
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
DisableAccountKeyGeneration bool `json:"disableAccountKeyGeneration,omitempty"`
|
||||
|
||||
// Enables requesting a Not After date on certificates that matches the
|
||||
// duration of the certificate. This is not supported by all ACME servers
|
||||
// like Let's Encrypt. If set to true when the ACME server does not support
|
||||
// it it will create an error on the Order.
|
||||
// Defaults to false.
|
||||
EnableNotAfterDate bool `json:"enableNotAfterDate,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEExternalAccountBinding is a reference to a CA external account of the ACME
|
||||
|
||||
@ -70,6 +70,9 @@ type OrderSpec struct {
|
||||
// validation process.
|
||||
// This field must match the corresponding field on the DER encoded CSR.
|
||||
IPAddresses []string
|
||||
|
||||
// NotAfter is the date for the requested certificate's Not Valid After date
|
||||
NotAfter *metav1.Time `json:"notAfter"`
|
||||
}
|
||||
|
||||
type OrderStatus struct {
|
||||
|
||||
@ -694,6 +694,7 @@ func autoConvert_v1_ACMEIssuer_To_acme_ACMEIssuer(in *v1.ACMEIssuer, out *acme.A
|
||||
}
|
||||
out.Solvers = *(*[]acme.ACMEChallengeSolver)(unsafe.Pointer(&in.Solvers))
|
||||
out.DisableAccountKeyGeneration = in.DisableAccountKeyGeneration
|
||||
out.EnableNotAfterDate = in.EnableNotAfterDate
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -714,6 +715,7 @@ func autoConvert_acme_ACMEIssuer_To_v1_ACMEIssuer(in *acme.ACMEIssuer, out *v1.A
|
||||
}
|
||||
out.Solvers = *(*[]v1.ACMEChallengeSolver)(unsafe.Pointer(&in.Solvers))
|
||||
out.DisableAccountKeyGeneration = in.DisableAccountKeyGeneration
|
||||
out.EnableNotAfterDate = in.EnableNotAfterDate
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1245,6 +1247,7 @@ func autoConvert_v1_OrderSpec_To_acme_OrderSpec(in *v1.OrderSpec, out *acme.Orde
|
||||
out.CommonName = in.CommonName
|
||||
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
|
||||
out.IPAddresses = *(*[]string)(unsafe.Pointer(&in.IPAddresses))
|
||||
out.NotAfter = (*apismetav1.Time)(unsafe.Pointer(in.NotAfter))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1262,6 +1265,7 @@ func autoConvert_acme_OrderSpec_To_v1_OrderSpec(in *acme.OrderSpec, out *v1.Orde
|
||||
out.CommonName = in.CommonName
|
||||
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
|
||||
out.IPAddresses = *(*[]string)(unsafe.Pointer(&in.IPAddresses))
|
||||
out.NotAfter = (*apismetav1.Time)(unsafe.Pointer(in.NotAfter))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -694,6 +694,7 @@ func autoConvert_v1alpha2_ACMEIssuer_To_acme_ACMEIssuer(in *v1alpha2.ACMEIssuer,
|
||||
}
|
||||
out.Solvers = *(*[]acme.ACMEChallengeSolver)(unsafe.Pointer(&in.Solvers))
|
||||
out.DisableAccountKeyGeneration = in.DisableAccountKeyGeneration
|
||||
out.EnableNotAfterDate = in.EnableNotAfterDate
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -714,6 +715,7 @@ func autoConvert_acme_ACMEIssuer_To_v1alpha2_ACMEIssuer(in *acme.ACMEIssuer, out
|
||||
}
|
||||
out.Solvers = *(*[]v1alpha2.ACMEChallengeSolver)(unsafe.Pointer(&in.Solvers))
|
||||
out.DisableAccountKeyGeneration = in.DisableAccountKeyGeneration
|
||||
out.EnableNotAfterDate = in.EnableNotAfterDate
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1255,6 +1257,7 @@ func autoConvert_v1alpha2_OrderSpec_To_acme_OrderSpec(in *v1alpha2.OrderSpec, ou
|
||||
out.CommonName = in.CommonName
|
||||
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
|
||||
out.IPAddresses = *(*[]string)(unsafe.Pointer(&in.IPAddresses))
|
||||
out.NotAfter = (*apismetav1.Time)(unsafe.Pointer(in.NotAfter))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1267,6 +1270,7 @@ func autoConvert_acme_OrderSpec_To_v1alpha2_OrderSpec(in *acme.OrderSpec, out *v
|
||||
out.CommonName = in.CommonName
|
||||
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
|
||||
out.IPAddresses = *(*[]string)(unsafe.Pointer(&in.IPAddresses))
|
||||
out.NotAfter = (*apismetav1.Time)(unsafe.Pointer(in.NotAfter))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -694,6 +694,7 @@ func autoConvert_v1alpha3_ACMEIssuer_To_acme_ACMEIssuer(in *v1alpha3.ACMEIssuer,
|
||||
}
|
||||
out.Solvers = *(*[]acme.ACMEChallengeSolver)(unsafe.Pointer(&in.Solvers))
|
||||
out.DisableAccountKeyGeneration = in.DisableAccountKeyGeneration
|
||||
out.EnableNotAfterDate = in.EnableNotAfterDate
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -714,6 +715,7 @@ func autoConvert_acme_ACMEIssuer_To_v1alpha3_ACMEIssuer(in *acme.ACMEIssuer, out
|
||||
}
|
||||
out.Solvers = *(*[]v1alpha3.ACMEChallengeSolver)(unsafe.Pointer(&in.Solvers))
|
||||
out.DisableAccountKeyGeneration = in.DisableAccountKeyGeneration
|
||||
out.EnableNotAfterDate = in.EnableNotAfterDate
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1255,6 +1257,7 @@ func autoConvert_v1alpha3_OrderSpec_To_acme_OrderSpec(in *v1alpha3.OrderSpec, ou
|
||||
out.CommonName = in.CommonName
|
||||
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
|
||||
out.IPAddresses = *(*[]string)(unsafe.Pointer(&in.IPAddresses))
|
||||
out.NotAfter = (*apismetav1.Time)(unsafe.Pointer(in.NotAfter))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1267,6 +1270,7 @@ func autoConvert_acme_OrderSpec_To_v1alpha3_OrderSpec(in *acme.OrderSpec, out *v
|
||||
out.CommonName = in.CommonName
|
||||
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
|
||||
out.IPAddresses = *(*[]string)(unsafe.Pointer(&in.IPAddresses))
|
||||
out.NotAfter = (*apismetav1.Time)(unsafe.Pointer(in.NotAfter))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -694,6 +694,7 @@ func autoConvert_v1beta1_ACMEIssuer_To_acme_ACMEIssuer(in *v1beta1.ACMEIssuer, o
|
||||
}
|
||||
out.Solvers = *(*[]acme.ACMEChallengeSolver)(unsafe.Pointer(&in.Solvers))
|
||||
out.DisableAccountKeyGeneration = in.DisableAccountKeyGeneration
|
||||
out.EnableNotAfterDate = in.EnableNotAfterDate
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -714,6 +715,7 @@ func autoConvert_acme_ACMEIssuer_To_v1beta1_ACMEIssuer(in *acme.ACMEIssuer, out
|
||||
}
|
||||
out.Solvers = *(*[]v1beta1.ACMEChallengeSolver)(unsafe.Pointer(&in.Solvers))
|
||||
out.DisableAccountKeyGeneration = in.DisableAccountKeyGeneration
|
||||
out.EnableNotAfterDate = in.EnableNotAfterDate
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1245,6 +1247,7 @@ func autoConvert_v1beta1_OrderSpec_To_acme_OrderSpec(in *v1beta1.OrderSpec, out
|
||||
out.CommonName = in.CommonName
|
||||
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
|
||||
out.IPAddresses = *(*[]string)(unsafe.Pointer(&in.IPAddresses))
|
||||
out.NotAfter = (*apismetav1.Time)(unsafe.Pointer(in.NotAfter))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1262,6 +1265,7 @@ func autoConvert_acme_OrderSpec_To_v1beta1_OrderSpec(in *acme.OrderSpec, out *v1
|
||||
out.CommonName = in.CommonName
|
||||
out.DNSNames = *(*[]string)(unsafe.Pointer(&in.DNSNames))
|
||||
out.IPAddresses = *(*[]string)(unsafe.Pointer(&in.IPAddresses))
|
||||
out.NotAfter = (*apismetav1.Time)(unsafe.Pointer(in.NotAfter))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -789,6 +789,10 @@ func (in *OrderSpec) DeepCopyInto(out *OrderSpec) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.NotAfter != nil {
|
||||
in, out := &in.NotAfter, &out.NotAfter
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"dns01.go",
|
||||
"http01.go",
|
||||
"notafter.go",
|
||||
"webhook.go",
|
||||
],
|
||||
importpath = "github.com/jetstack/cert-manager/test/e2e/suite/issuers/acme/certificate",
|
||||
@ -16,6 +17,7 @@ go_library(
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/client/clientset/versioned:go_default_library",
|
||||
"//pkg/util:go_default_library",
|
||||
"//pkg/util/pki:go_default_library",
|
||||
"//test/e2e/framework:go_default_library",
|
||||
"//test/e2e/framework/addon:go_default_library",
|
||||
"//test/e2e/framework/log:go_default_library",
|
||||
|
||||
151
test/e2e/suite/issuers/acme/certificate/notafter.go
Normal file
151
test/e2e/suite/issuers/acme/certificate/notafter.go
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
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 certificate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/util/pki"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
|
||||
v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework"
|
||||
frameworkutil "github.com/jetstack/cert-manager/test/e2e/framework/util"
|
||||
"github.com/jetstack/cert-manager/test/e2e/util"
|
||||
"github.com/jetstack/cert-manager/test/unit/gen"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01 + Not After)", func() {
|
||||
f := framework.NewDefaultFramework("create-acme-certificate-duration")
|
||||
|
||||
var acmeIngressDomain string
|
||||
issuerName := "test-acme-issuer"
|
||||
certificateName := "test-acme-certificate"
|
||||
certificateSecretName := "test-acme-certificate"
|
||||
// fixedIngressName is the name of an ingress resource that is configured
|
||||
// with a challenge solve.
|
||||
// To utilise this solver, add the 'testing.cert-manager.io/fixed-ingress: "true"' label.
|
||||
fixedIngressName := "testingress"
|
||||
|
||||
BeforeEach(func() {
|
||||
acmeIssuer := util.NewCertManagerACMEIssuer(issuerName, f.Config.Addons.ACMEServer.URL, testingACMEEmail, testingACMEPrivateKey)
|
||||
// Enable NotAfter feature
|
||||
acmeIssuer.Spec.ACME.EnableNotAfterDate = true
|
||||
acmeIssuer.Spec.ACME.Solvers = []cmacme.ACMEChallengeSolver{
|
||||
{
|
||||
HTTP01: &cmacme.ACMEChallengeSolverHTTP01{
|
||||
Ingress: &cmacme.ACMEChallengeSolverHTTP01Ingress{
|
||||
Class: &f.Config.Addons.IngressController.IngressClass,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Selector: &cmacme.CertificateDNSNameSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"testing.cert-manager.io/fixed-ingress": "true",
|
||||
},
|
||||
},
|
||||
HTTP01: &cmacme.ACMEChallengeSolverHTTP01{
|
||||
Ingress: &cmacme.ACMEChallengeSolverHTTP01Ingress{
|
||||
Name: fixedIngressName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Creating an Issuer")
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), acmeIssuer, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Waiting for Issuer to become Ready")
|
||||
err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
|
||||
issuerName,
|
||||
v1.IssuerCondition{
|
||||
Type: v1.IssuerConditionReady,
|
||||
Status: cmmeta.ConditionTrue,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the ACME account URI is set")
|
||||
err = util.WaitForIssuerStatusFunc(f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name),
|
||||
issuerName,
|
||||
func(i *v1.Issuer) (bool, error) {
|
||||
if i.GetStatus().ACMEStatus().URI == "" {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying ACME account private key exists")
|
||||
secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), testingACMEPrivateKey, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(secret.Data) != 1 {
|
||||
Fail("Expected 1 key in ACME account private key secret, but there was %d", len(secret.Data))
|
||||
}
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
acmeIngressDomain = frameworkutil.RandomSubdomain(f.Config.Addons.IngressController.Domain)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
By("Cleaning up")
|
||||
f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Delete(context.TODO(), issuerName, metav1.DeleteOptions{})
|
||||
f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(context.TODO(), testingACMEPrivateKey, metav1.DeleteOptions{})
|
||||
})
|
||||
|
||||
It("should obtain a signed certificate with a single CN from the ACME server with 1 hour validity", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert := gen.Certificate(certificateName,
|
||||
gen.SetCertificateDuration(time.Hour),
|
||||
gen.SetCertificateRenewBefore(45*time.Minute),
|
||||
gen.SetCertificateSecretName(certificateSecretName),
|
||||
gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: issuerName}),
|
||||
gen.SetCertificateDNSNames(acmeIngressDomain),
|
||||
)
|
||||
cert.Namespace = f.Namespace.Name
|
||||
|
||||
_, err := certClient.Create(context.TODO(), cert, metav1.CreateOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Certificate to be issued...")
|
||||
err = f.Helper().WaitCertificateIssued(f.Namespace.Name, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validating the issued Certificate...")
|
||||
err = f.Helper().ValidateCertificate(f.Namespace.Name, certificateName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
sec, err := f.Helper().WaitForSecretCertificateData(f.Namespace.Name, certificateSecretName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to wait for secret")
|
||||
|
||||
crtPEM := sec.Data[corev1.TLSCertKey]
|
||||
crt, err := pki.DecodeX509CertificateBytes(crtPEM)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to get decode signed certificate data")
|
||||
|
||||
// checking losely to tot hit too many timing issues as the date is defined in the controller
|
||||
if crt.NotAfter.After(time.Now().Add(time.Hour)) {
|
||||
Fail(fmt.Sprintf("Certificate has a NotAfter time after more than 1 hour (requested duration), got %s, current time %s", crt.NotAfter.String(), time.Now().String()))
|
||||
}
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user