func (crStatus *CRStatus) String() string {
	if crStatus.Error != nil {
		return crStatus.Error.Error()
	}
	crFormat := `
  Name: %s
  Namespace: %s
  Conditions:
  %s`
	conditionMsg := ""
	for _, con := range crStatus.Conditions {
		conditionMsg += fmt.Sprintf("  %s: %s, Reason: %s, Message: %s\n", con.Type, con.Status, con.Reason, con.Message)
	}
	if conditionMsg == "" {
		conditionMsg = "  No Conditions set\n"
	}
	infos := fmt.Sprintf(crFormat, crStatus.Name, crStatus.Namespace, conditionMsg)
	infos = fmt.Sprintf("CertificateRequest:%s", infos)
	infos += eventsToString(crStatus.Events, 1)
	return infos
}
String returns the information about the status of a CR as a string to be printed as output
package certificate
import (
	"bytes"
	"crypto/x509"
	"encoding/hex"
	"fmt"
	"math/big"
	"strings"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/kubectl/pkg/describe"
	"github.com/jetstack/cert-manager/cmd/ctl/pkg/status/util"
	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)
type CertificateStatus struct {
	Name string
	Namespace string
	CreationTime metav1.Time
	Conditions []cmapi.CertificateCondition
	DNSNames []string
	Events *v1.EventList
	NotBefore *metav1.Time
	NotAfter *metav1.Time
	RenewalTime *metav1.Time
	IssuerStatus *IssuerStatus
	SecretStatus *SecretStatus
	CRStatus *CRStatus
	OrderStatus *OrderStatus
	ChallengeStatusList *ChallengeStatusList
}
type IssuerStatus struct {
	Error error
	Name string
	Kind string
	Conditions []cmapi.IssuerCondition
	Events *v1.EventList
}
type SecretStatus struct {
	Error error
	Name string
	IssuerCountry []string
	IssuerOrganisation []string
	IssuerCommonName string
	KeyUsage x509.KeyUsage
	ExtKeyUsage []x509.ExtKeyUsage
	PublicKeyAlgorithm x509.PublicKeyAlgorithm
	SignatureAlgorithm x509.SignatureAlgorithm
	SubjectKeyId []byte
	AuthorityKeyId []byte
	SerialNumber *big.Int
	Events *v1.EventList
}
type CRStatus struct {
	Error error
	Name string
	Namespace string
	Conditions []cmapi.CertificateRequestCondition
	Events *v1.EventList
}
type OrderStatus struct {
	Error error
	Name string
	State cmacme.State
	Reason string
	Authorizations []cmacme.ACMEAuthorization
	FailureTime *metav1.Time
}
type ChallengeStatusList struct {
	Error             error
	ChallengeStatuses []*ChallengeStatus
}
type ChallengeStatus struct {
	Name       string
	Type       cmacme.ACMEChallengeType
	Token      string
	Key        string
	State      cmacme.State
	Reason     string
	Processing bool
	Presented  bool
}
func newCertificateStatusFromCert(crt *cmapi.Certificate) *CertificateStatus {
	if crt == nil {
		return nil
	}
	return &CertificateStatus{
		Name: crt.Name, Namespace: crt.Namespace, CreationTime: crt.CreationTimestamp,
		Conditions: crt.Status.Conditions, DNSNames: crt.Spec.DNSNames,
		NotBefore: crt.Status.NotBefore, NotAfter: crt.Status.NotAfter, RenewalTime: crt.Status.RenewalTime}
}
func (status *CertificateStatus) withEvents(events *v1.EventList) *CertificateStatus {
	status.Events = events
	return status
}
func (status *CertificateStatus) withGenericIssuer(genericIssuer cmapi.GenericIssuer, issuerKind string, issuerEvents *v1.EventList, err error) *CertificateStatus {
	if err != nil {
		status.IssuerStatus = &IssuerStatus{Error: err}
		return status
	}
	if genericIssuer == nil {
		return status
	}
	if issuerKind == "ClusterIssuer" {
		status.IssuerStatus = &IssuerStatus{Name: genericIssuer.GetName(), Kind: "ClusterIssuer",
			Conditions: genericIssuer.GetStatus().Conditions, Events: issuerEvents}
		return status
	}
	status.IssuerStatus = &IssuerStatus{Name: genericIssuer.GetName(), Kind: "Issuer",
		Conditions: genericIssuer.GetStatus().Conditions, Events: issuerEvents}
	return status
}
func (status *CertificateStatus) withSecret(secret *v1.Secret, secretEvents *v1.EventList, err error) *CertificateStatus {
	if err != nil {
		status.SecretStatus = &SecretStatus{Error: err}
		return status
	}
	if secret == nil {
		return status
	}
	certData := secret.Data["tls.crt"]
	if len(certData) == 0 {
		status.SecretStatus = &SecretStatus{Error: fmt.Errorf("error: 'tls.crt' of Secret %q is not set\n", secret.Name)}
		return status
	}
	x509Cert, err := pki.DecodeX509CertificateBytes(certData)
	if err != nil {
		status.SecretStatus = &SecretStatus{Error: fmt.Errorf("error when parsing 'tls.crt' of Secret %q: %s\n", secret.Name, err)}
		return status
	}
	status.SecretStatus = &SecretStatus{Error: nil, Name: secret.Name, IssuerCountry: x509Cert.Issuer.Country,
		IssuerOrganisation: x509Cert.Issuer.Organization,
		IssuerCommonName:   x509Cert.Issuer.CommonName, KeyUsage: x509Cert.KeyUsage,
		ExtKeyUsage: x509Cert.ExtKeyUsage, PublicKeyAlgorithm: x509Cert.PublicKeyAlgorithm,
		SignatureAlgorithm: x509Cert.SignatureAlgorithm,
		SubjectKeyId:       x509Cert.SubjectKeyId, AuthorityKeyId: x509Cert.AuthorityKeyId,
		SerialNumber: x509Cert.SerialNumber, Events: secretEvents}
	return status
}
func (status *CertificateStatus) withCR(req *cmapi.CertificateRequest, events *v1.EventList, err error) *CertificateStatus {
	if err != nil {
		status.CRStatus = &CRStatus{Error: err}
		return status
	}
	if req == nil {
		return status
	}
	status.CRStatus = &CRStatus{Name: req.Name, Namespace: req.Namespace, Conditions: req.Status.Conditions, Events: events}
	return status
}
func (status *CertificateStatus) withOrder(order *cmacme.Order, err error) *CertificateStatus {
	if err != nil {
		status.OrderStatus = &OrderStatus{Error: err}
		return status
	}
	if order == nil {
		return status
	}
	status.OrderStatus = &OrderStatus{Name: order.Name, State: order.Status.State,
		Reason: order.Status.Reason, Authorizations: order.Status.Authorizations,
		FailureTime: order.Status.FailureTime}
	return status
}
func (status *CertificateStatus) withChallenges(challenges []*cmacme.Challenge, err error) *CertificateStatus {
	if err != nil {
		status.ChallengeStatusList = &ChallengeStatusList{Error: err}
		return status
	}
	if len(challenges) == 0 {
		return status
	}
	var list []*ChallengeStatus
	for _, challenge := range challenges {
		list = append(list, &ChallengeStatus{
			Name:       challenge.Name,
			Type:       challenge.Spec.Type,
			Token:      challenge.Spec.Token,
			Key:        challenge.Spec.Key,
			State:      challenge.Status.State,
			Reason:     challenge.Status.Reason,
			Processing: challenge.Status.Processing,
			Presented:  challenge.Status.Presented,
		})
	}
	status.ChallengeStatusList = &ChallengeStatusList{ChallengeStatuses: list}
	return status
}
func (status *CertificateStatus) String() string {
	output := ""
	output += fmt.Sprintf("Name: %s\n", status.Name)
	output += fmt.Sprintf("Namespace: %s\n", status.Namespace)
	output += fmt.Sprintf("Created at: %s\n", formatTimeString(&status.CreationTime))
	conditionMsg := ""
	for _, con := range status.Conditions {
		conditionMsg += fmt.Sprintf("  %s: %s, Reason: %s, Message: %s\n", con.Type, con.Status, con.Reason, con.Message)
	}
	if conditionMsg == "" {
		conditionMsg = "  No Conditions set\n"
	}
	output += fmt.Sprintf("Conditions:\n%s", conditionMsg)
	output += fmt.Sprintf("DNS Names:\n%s", formatStringSlice(status.DNSNames))
	output += eventsToString(status.Events, 0)
	output += status.IssuerStatus.String()
	output += status.SecretStatus.String()
	output += fmt.Sprintf("Not Before: %s\n", formatTimeString(status.NotBefore))
	output += fmt.Sprintf("Not After: %s\n", formatTimeString(status.NotAfter))
	output += fmt.Sprintf("Renewal Time: %s\n", formatTimeString(status.RenewalTime))
	output += status.CRStatus.String()
	if status.OrderStatus != nil {
		output += status.OrderStatus.String()
	}
	if status.ChallengeStatusList != nil {
		output += status.ChallengeStatusList.String()
	}
	return output
}
func (issuerStatus *IssuerStatus) String() string {
	if issuerStatus.Error != nil {
		return issuerStatus.Error.Error()
	}
	issuerFormat := `Issuer:
  Name: %s
  Kind: %s
  Conditions:
  %s`
	conditionMsg := ""
	for _, con := range issuerStatus.Conditions {
		conditionMsg += fmt.Sprintf("  %s: %s, Reason: %s, Message: %s\n", con.Type, con.Status, con.Reason, con.Message)
	}
	if conditionMsg == "" {
		conditionMsg = "  No Conditions set\n"
	}
	output := fmt.Sprintf(issuerFormat, issuerStatus.Name, issuerStatus.Kind, conditionMsg)
	output += eventsToString(issuerStatus.Events, 1)
	return output
}
func (secretStatus *SecretStatus) String() string {
	if secretStatus.Error != nil {
		return secretStatus.Error.Error()
	}
	secretFormat := `Secret:
  Name: %s
  Issuer Country: %s
  Issuer Organisation: %s
  Issuer Common Name: %s
  Key Usage: %s
  Extended Key Usages: %s
  Public Key Algorithm: %s
  Signature Algorithm: %s
  Subject Key ID: %s
  Authority Key ID: %s
  Serial Number: %s
`
	extKeyUsageString, err := extKeyUsageToString(secretStatus.ExtKeyUsage)
	if err != nil {
		extKeyUsageString = err.Error()
	}
	output := fmt.Sprintf(secretFormat, secretStatus.Name, strings.Join(secretStatus.IssuerCountry, ", "),
		strings.Join(secretStatus.IssuerOrganisation, ", "),
		secretStatus.IssuerCommonName, keyUsageToString(secretStatus.KeyUsage),
		extKeyUsageString, secretStatus.PublicKeyAlgorithm, secretStatus.SignatureAlgorithm,
		hex.EncodeToString(secretStatus.SubjectKeyId), hex.EncodeToString(secretStatus.AuthorityKeyId),
		hex.EncodeToString(secretStatus.SerialNumber.Bytes()))
	output += eventsToString(secretStatus.Events, 1)
	return output
}
var (
	keyUsageToStringMap = map[int]string{
		1:   "Digital Signature",
		2:   "Content Commitment",
		4:   "Key Encipherment",
		8:   "Data Encipherment",
		16:  "Key Agreement",
		32:  "Cert Sign",
		64:  "CRL Sign",
		128: "Encipher Only",
		256: "Decipher Only",
	}
	keyUsagePossibleValues  = []int{256, 128, 64, 32, 16, 8, 4, 2, 1}
	extKeyUsageStringValues = []string{"Any", "Server Authentication", "Client Authentication", "Code Signing", "Email Protection",
		"IPSEC End System", "IPSEC Tunnel", "IPSEC User", "Time Stamping", "OCSP Signing", "Microsoft Server Gated Crypto",
		"Netscape Server Gated Crypto", "Microsoft Commercial Code Signing", "Microsoft Kernel Code Signing",
	}
)
func keyUsageToString(usage x509.KeyUsage) string {
	usageInt := int(usage)
	var usageStrings []string
	for _, val := range keyUsagePossibleValues {
		if usageInt >= val {
			usageInt -= val
			usageStrings = append(usageStrings, keyUsageToStringMap[val])
		}
		if usageInt == 0 {
			break
		}
	}
	for i := 0; i < len(usageStrings)/2; i++ {
		opp := len(usageStrings) - 1 - i
		usageStrings[i], usageStrings[opp] = usageStrings[opp], usageStrings[i]
	}
	return strings.Join(usageStrings, ", ")
}
func extKeyUsageToString(extUsages []x509.ExtKeyUsage) (string, error) {
	var extUsageStrings []string
	for _, extUsage := range extUsages {
		if extUsage < 0 || int(extUsage) >= len(extKeyUsageStringValues) {
			return "", fmt.Errorf("error when converting Extended Usages to string: encountered unknown Extended Usage with code %d", extUsage)
		}
		extUsageStrings = append(extUsageStrings, extKeyUsageStringValues[extUsage])
	}
	return strings.Join(extUsageStrings, ", "), nil
}