func hashCert(certificate *x509.Certificate) string {
	spkiHash := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
	return "sha256:" + strings.ToLower(hex.EncodeToString(spkiHash[:]))
}
hashCert calculates the sha256 of certificate.
package secret
import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/hex"
	"math/big"
	"path/filepath"
	"strings"
	"time"
	"github.com/pkg/errors"
	bootstrapv1 "github.com/zawachte-msft/cluster-api-k3s/bootstrap/api/v1alpha3"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/util/cert"
	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
	"sigs.k8s.io/cluster-api/util/certs"
	"sigs.k8s.io/controller-runtime/pkg/client"
)
const (
	rootOwnerValue = "root:root"
	DefaultCertificatesDir = "/var/lib/rancher/k3s/server/tls"
)
var (
	ErrMissingCertificate = errors.New("missing certificate")
	ErrMissingCrt = errors.New("missing crt data")
	ErrMissingKey = errors.New("missing key data")
)
type Certificates []*Certificate
func NewCertificatesForInitialControlPlane() Certificates {
	certificatesDir := DefaultCertificatesDir
	certificates := Certificates{
		&Certificate{
			Purpose:  ClusterCA,
			CertFile: filepath.Join(certificatesDir, "server-ca.crt"),
			KeyFile:  filepath.Join(certificatesDir, "server-ca.key"),
		},
		&Certificate{
			Purpose:  ClientClusterCA,
			CertFile: filepath.Join(certificatesDir, "client-ca.crt"),
			KeyFile:  filepath.Join(certificatesDir, "client-ca.key"),
		},
	}
	return certificates
}
func (c Certificates) GetByPurpose(purpose Purpose) *Certificate {
	for _, certificate := range c {
		if certificate.Purpose == purpose {
			return certificate
		}
	}
	return nil
}
func (c Certificates) Lookup(ctx context.Context, ctrlclient client.Client, clusterName client.ObjectKey) error {
	for _, certificate := range c {
		s := &corev1.Secret{}
		key := client.ObjectKey{
			Name:      Name(clusterName.Name, certificate.Purpose),
			Namespace: clusterName.Namespace,
		}
		if err := ctrlclient.Get(ctx, key, s); err != nil {
			if apierrors.IsNotFound(err) {
				if certificate.External {
					return errors.WithMessage(err, "external certificate not found")
				}
				continue
			}
			return errors.WithStack(err)
		}
		kp, err := secretToKeyPair(s)
		if err != nil {
			return err
		}
		certificate.KeyPair = kp
	}
	return nil
}
func (c Certificates) EnsureAllExist() error {
	for _, certificate := range c {
		if certificate.KeyPair == nil {
			return ErrMissingCertificate
		}
		if len(certificate.KeyPair.Cert) == 0 {
			return errors.Wrapf(ErrMissingCrt, "for certificate: %s", certificate.Purpose)
		}
		if !certificate.External {
			if len(certificate.KeyPair.Key) == 0 {
				return errors.Wrapf(ErrMissingKey, "for certificate: %s", certificate.Purpose)
			}
		}
	}
	return nil
}
func (c Certificates) Generate() error {
	for _, certificate := range c {
		if certificate.KeyPair == nil {
			err := certificate.Generate()
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func (c Certificates) SaveGenerated(ctx context.Context, ctrlclient client.Client, clusterName client.ObjectKey, owner metav1.OwnerReference) error {
	for _, certificate := range c {
		if !certificate.Generated {
			continue
		}
		s := certificate.AsSecret(clusterName, owner)
		if err := ctrlclient.Create(ctx, s); err != nil {
			return errors.WithStack(err)
		}
	}
	return nil
}
func (c Certificates) LookupOrGenerate(ctx context.Context, ctrlclient client.Client, clusterName client.ObjectKey, owner metav1.OwnerReference) error {
	if err := c.Lookup(ctx, ctrlclient, clusterName); err != nil {
		return err
	}
	if err := c.Generate(); err != nil {
		return err
	}
	if err := c.SaveGenerated(ctx, ctrlclient, clusterName, owner); err != nil {
		return err
	}
	return nil
}
type Certificate struct {
	Generated         bool
	External          bool
	Purpose           Purpose
	KeyPair           *certs.KeyPair
	CertFile, KeyFile string
}
func (c *Certificate) Hashes() ([]string, error) {
	certificates, err := cert.ParseCertsPEM(c.KeyPair.Cert)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to parse %s certificate", c.Purpose)
	}
	out := make([]string, 0)
	for _, c := range certificates {
		out = append(out, hashCert(c))
	}
	return out, nil
}