Remove cluster check-expiration subcommand
This command is outdated and no longer used, according to the design call it should be removed. Change-Id: I5953f0c66e9cee40c070afe148aa98c9d07113f7 Signed-off-by: Ruslan Aliev <raliev@mirantis.com> Relates-To: #588
This commit is contained in:
parent
df55f50cb6
commit
7a89572149
@ -1,86 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 checkexpiration
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/checkexpiration"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/client"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
)
|
||||
|
||||
const (
|
||||
checkLong = `
|
||||
Displays a list of certificate along with expirations from both the management and workload clusters, or in a
|
||||
self-managed cluster. Checks for TLS Secrets, kubeconf secrets (which gets created while creating the
|
||||
workload cluster) and also the node certificates present inside /etc/kubernetes/pki directory for each node.
|
||||
`
|
||||
|
||||
checkExample = `
|
||||
To display all the expiring certificates in the cluster
|
||||
# airshipctl cluster check-certificate-expiration --kubeconfig testconfig
|
||||
|
||||
To display the certificates whose expiration is within threshold of 30 days
|
||||
# airshipctl cluster check-certificate-expiration -t 30 --kubeconfig testconfig
|
||||
|
||||
To output the contents in json format (default operation)
|
||||
# airshipctl cluster check-certificate-expiration -o json --kubeconfig testconfig
|
||||
or
|
||||
# airshipctl cluster check-certificate-expiration --kubeconfig testconfig
|
||||
|
||||
To output the contents in yaml format
|
||||
# airshipctl cluster check-certificate-expiration -o yaml --kubeconfig testconfig
|
||||
|
||||
To output the contents whose expiration is within 30 days in yaml format
|
||||
# airshipctl cluster check-certificate-expiration -t 30 -o yaml --kubeconfig testconfig
|
||||
`
|
||||
|
||||
kubeconfigFlag = "kubeconfig"
|
||||
)
|
||||
|
||||
// NewCheckCommand creates a new command for generating secret information
|
||||
func NewCheckCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
c := &checkexpiration.CheckCommand{
|
||||
Options: checkexpiration.CheckFlags{},
|
||||
CfgFactory: cfgFactory,
|
||||
ClientFactory: client.DefaultClient,
|
||||
}
|
||||
|
||||
checkCmd := &cobra.Command{
|
||||
Use: "check-certificate-expiration",
|
||||
Short: "Airshipctl command to check expiring TLS certificates, " +
|
||||
"secrets and kubeconfigs in the kubernetes cluster",
|
||||
Long: checkLong[1:],
|
||||
Example: checkExample,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return c.RunE(cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
|
||||
checkCmd.Flags().StringVarP(&c.Options.FormatType, "output", "o", "json", "convert output to yaml or json")
|
||||
checkCmd.Flags().StringVar(&c.Options.KubeContext, "kubecontext", "", "kubeconfig context to be used")
|
||||
checkCmd.Flags().StringVar(&c.Options.Kubeconfig, kubeconfigFlag, "",
|
||||
"path to kubeconfig associated with cluster being managed")
|
||||
checkCmd.Flags().IntVarP(&c.Options.Threshold, "threshold", "t", -1,
|
||||
"the max expiration threshold in days before a certificate is expiring. Displays all the certificates by default")
|
||||
|
||||
err := checkCmd.MarkFlagRequired(kubeconfigFlag)
|
||||
if err != nil {
|
||||
log.Fatalf("marking kubeconfig flag required failed: %v", err)
|
||||
}
|
||||
return checkCmd
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 checkexpiration_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/cluster/checkexpiration"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
func TestCheckExpiration(t *testing.T) {
|
||||
cmdTests := []*testutil.CmdTest{
|
||||
{
|
||||
Name: "check-expiration-with-help",
|
||||
CmdLine: "--help",
|
||||
Cmd: checkexpiration.NewCheckCommand(nil),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cmdTests {
|
||||
testutil.RunTest(t, tt)
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
Displays a list of certificate along with expirations from both the management and workload clusters, or in a
|
||||
self-managed cluster. Checks for TLS Secrets, kubeconf secrets (which gets created while creating the
|
||||
workload cluster) and also the node certificates present inside /etc/kubernetes/pki directory for each node.
|
||||
|
||||
Usage:
|
||||
check-certificate-expiration [flags]
|
||||
|
||||
Examples:
|
||||
|
||||
To display all the expiring certificates in the cluster
|
||||
# airshipctl cluster check-certificate-expiration --kubeconfig testconfig
|
||||
|
||||
To display the certificates whose expiration is within threshold of 30 days
|
||||
# airshipctl cluster check-certificate-expiration -t 30 --kubeconfig testconfig
|
||||
|
||||
To output the contents in json format (default operation)
|
||||
# airshipctl cluster check-certificate-expiration -o json --kubeconfig testconfig
|
||||
or
|
||||
# airshipctl cluster check-certificate-expiration --kubeconfig testconfig
|
||||
|
||||
To output the contents in yaml format
|
||||
# airshipctl cluster check-certificate-expiration -o yaml --kubeconfig testconfig
|
||||
|
||||
To output the contents whose expiration is within 30 days in yaml format
|
||||
# airshipctl cluster check-certificate-expiration -t 30 -o yaml --kubeconfig testconfig
|
||||
|
||||
|
||||
Flags:
|
||||
-h, --help help for check-certificate-expiration
|
||||
--kubeconfig string path to kubeconfig associated with cluster being managed
|
||||
--kubecontext string kubeconfig context to be used
|
||||
-o, --output string convert output to yaml or json (default "json")
|
||||
-t, --threshold int the max expiration threshold in days before a certificate is expiring. Displays all the certificates by default (default -1)
|
@ -17,7 +17,6 @@ package cluster
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/cluster/checkexpiration"
|
||||
"opendev.org/airship/airshipctl/cmd/cluster/resetsatoken"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
)
|
||||
@ -40,7 +39,6 @@ func NewClusterCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
|
||||
clusterRootCmd.AddCommand(NewStatusCommand(cfgFactory))
|
||||
clusterRootCmd.AddCommand(resetsatoken.NewResetCommand(cfgFactory))
|
||||
clusterRootCmd.AddCommand(checkexpiration.NewCheckCommand(cfgFactory))
|
||||
clusterRootCmd.AddCommand(NewGetKubeconfigCommand(cfgFactory))
|
||||
clusterRootCmd.AddCommand(NewListCommand(cfgFactory))
|
||||
|
||||
|
@ -5,12 +5,11 @@ Usage:
|
||||
cluster [command]
|
||||
|
||||
Available Commands:
|
||||
check-certificate-expiration Airshipctl command to check expiring TLS certificates, secrets and kubeconfigs in the kubernetes cluster
|
||||
get-kubeconfig Airshipctl command to retrieve kubeconfig for a desired cluster
|
||||
help Help about any command
|
||||
list Airshipctl command to get and list defined clusters
|
||||
rotate-sa-token Airshipctl command to rotate tokens of Service Account(s)
|
||||
status Retrieve statuses of deployed cluster components
|
||||
get-kubeconfig Airshipctl command to retrieve kubeconfig for a desired cluster
|
||||
help Help about any command
|
||||
list Airshipctl command to get and list defined clusters
|
||||
rotate-sa-token Airshipctl command to rotate tokens of Service Account(s)
|
||||
status Retrieve statuses of deployed cluster components
|
||||
|
||||
Flags:
|
||||
-h, --help help for cluster
|
||||
|
@ -32,7 +32,6 @@ SEE ALSO
|
||||
~~~~~~~~
|
||||
|
||||
* :ref:`airshipctl <airshipctl>` - A unified command line tool for management of end-to-end kubernetes cluster deployment on cloud infrastructure environments.
|
||||
* :ref:`airshipctl cluster check-certificate-expiration <airshipctl_cluster_check-certificate-expiration>` - Airshipctl command to check expiring TLS certificates, secrets and kubeconfigs in the kubernetes cluster
|
||||
* :ref:`airshipctl cluster get-kubeconfig <airshipctl_cluster_get-kubeconfig>` - Airshipctl command to retrieve kubeconfig for a desired cluster
|
||||
* :ref:`airshipctl cluster list <airshipctl_cluster_list>` - Airshipctl command to get and list defined clusters
|
||||
* :ref:`airshipctl cluster rotate-sa-token <airshipctl_cluster_rotate-sa-token>` - Airshipctl command to rotate tokens of Service Account(s)
|
||||
|
@ -6,7 +6,6 @@ cluster
|
||||
:maxdepth: 2
|
||||
|
||||
airshipctl_cluster
|
||||
airshipctl_cluster_check-certificate-expiration
|
||||
airshipctl_cluster_get-kubeconfig
|
||||
airshipctl_cluster_list
|
||||
airshipctl_cluster_rotate-sa-token
|
||||
|
@ -1,350 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 checkexpiration
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/client"
|
||||
)
|
||||
|
||||
const (
|
||||
kubeconfigIdentifierSuffix = "-kubeconfig"
|
||||
timeFormat = "Jan 02, 2006 15:04 MST"
|
||||
nodeCertExpirationAnnotation = "cert-expiration"
|
||||
)
|
||||
|
||||
// CertificateExpirationStore is the customized client store
|
||||
type CertificateExpirationStore struct {
|
||||
Kclient client.Interface
|
||||
Settings config.Factory
|
||||
ExpirationThreshold int
|
||||
}
|
||||
|
||||
// NewStore returns an instance of a CertificateExpirationStore
|
||||
func NewStore(cfgFactory config.Factory, clientFactory client.Factory,
|
||||
kubeconfig, _ string, expirationThreshold int) (CertificateExpirationStore, error) {
|
||||
airshipconfig, err := cfgFactory()
|
||||
if err != nil {
|
||||
return CertificateExpirationStore{}, err
|
||||
}
|
||||
|
||||
// TODO (guhan) Allow kube context to be passed to client Factory
|
||||
// 4th argument in NewStore takes kube context and is ignored for now.
|
||||
// To be modified post #388. Refer to
|
||||
// https://review.opendev.org/#/c/760501/7/pkg/cluster/checkexpiration/command.go@31
|
||||
kclient, err := clientFactory(airshipconfig.LoadedConfigPath(), kubeconfig)
|
||||
if err != nil {
|
||||
return CertificateExpirationStore{}, err
|
||||
}
|
||||
|
||||
return CertificateExpirationStore{
|
||||
Kclient: kclient,
|
||||
Settings: cfgFactory,
|
||||
ExpirationThreshold: expirationThreshold,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetExpiringTLSCertificates returns the list of TLS certificates whose expiration date
|
||||
// falls within the given expirationThreshold
|
||||
func (store CertificateExpirationStore) GetExpiringTLSCertificates() ([]TLSSecret, error) {
|
||||
secrets, err := store.getAllTLSCertificates()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsData := make([]TLSSecret, 0)
|
||||
for _, secret := range secrets.Items {
|
||||
expiringCertificates := store.getExpiringCertificates(secret)
|
||||
if len(expiringCertificates) > 0 {
|
||||
tlsData = append(tlsData, TLSSecret{
|
||||
Name: secret.Name,
|
||||
Namespace: secret.Namespace,
|
||||
ExpiringCertificates: expiringCertificates,
|
||||
})
|
||||
}
|
||||
}
|
||||
return tlsData, nil
|
||||
}
|
||||
|
||||
// getAllTLSCertificates juist returns all the k8s secrets with tyoe as TLS
|
||||
func (store CertificateExpirationStore) getAllTLSCertificates() (*corev1.SecretList, error) {
|
||||
secretTypeFieldSelector := fmt.Sprintf("type=%s", corev1.SecretTypeTLS)
|
||||
listOptions := metav1.ListOptions{FieldSelector: secretTypeFieldSelector}
|
||||
return store.getSecrets(listOptions)
|
||||
}
|
||||
|
||||
// getSecrets returns the secret list based on the listOptions
|
||||
func (store CertificateExpirationStore) getSecrets(listOptions metav1.ListOptions) (*corev1.SecretList, error) {
|
||||
return store.Kclient.ClientSet().CoreV1().Secrets("").List(listOptions)
|
||||
}
|
||||
|
||||
// getExpiringCertificates skims through all the TLS certificates and returns the ones
|
||||
// lesser than threshold
|
||||
func (store CertificateExpirationStore) getExpiringCertificates(secret corev1.Secret) map[string]string {
|
||||
expiringCertificates := map[string]string{}
|
||||
for _, certName := range []string{corev1.TLSCertKey, corev1.ServiceAccountRootCAKey} {
|
||||
if cert, found := secret.Data[certName]; found {
|
||||
expirationDate, err := extractExpirationDateFromCertificate(cert)
|
||||
if err != nil {
|
||||
log.Printf("Unable to parse certificate for %s in secret %s in namespace %s: %v",
|
||||
certName, secret.Name, secret.Namespace, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if isWithinDuration(expirationDate, store.ExpirationThreshold) {
|
||||
expiringCertificates[certName] = expirationDate.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
return expiringCertificates
|
||||
}
|
||||
|
||||
// isWithinDuration checks if the certificate expirationDate is within the duration (input)
|
||||
func isWithinDuration(expirationDate time.Time, duration int) bool {
|
||||
if duration < 0 {
|
||||
return true
|
||||
}
|
||||
daysUntilExpiration := int(time.Until(expirationDate).Hours() / 24)
|
||||
return 0 <= daysUntilExpiration && daysUntilExpiration < duration
|
||||
}
|
||||
|
||||
// extractExpirationDateFromCertificate parses the certificate and returns the expiration date
|
||||
func extractExpirationDateFromCertificate(certData []byte) (time.Time, error) {
|
||||
block, _ := pem.Decode(certData)
|
||||
if block == nil {
|
||||
return time.Time{}, ErrPEMFail{Context: "decode", Err: "no PEM data could be found"}
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return time.Time{}, ErrPEMFail{Context: "parse", Err: err.Error()}
|
||||
}
|
||||
return cert.NotAfter, nil
|
||||
}
|
||||
|
||||
// GetExpiringKubeConfigs - fetches all the '-kubeconfig' secrets and identifies expiration
|
||||
func (store CertificateExpirationStore) GetExpiringKubeConfigs() ([]Kubeconfig, error) {
|
||||
kubeconfigs, err := store.getKubeconfSecrets()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kSecretData := make([]Kubeconfig, 0)
|
||||
|
||||
for _, kubeconfig := range kubeconfigs {
|
||||
kubecontent, err := clientcmd.Load(kubeconfig.Data["value"])
|
||||
if err != nil {
|
||||
log.Printf("Failed to read kubeconfig from %s in %s-"+
|
||||
"it maybe malformed : %v", kubeconfig.Name, kubeconfig.Namespace, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
expiringClusters := store.getExpiringClusterCertificates(kubecontent)
|
||||
|
||||
expiringUsers := store.getExpiringUserCertificates(kubecontent)
|
||||
|
||||
if len(expiringClusters) > 0 || len(expiringUsers) > 0 {
|
||||
kSecretData = append(kSecretData, Kubeconfig{
|
||||
SecretName: kubeconfig.Name,
|
||||
SecretNamespace: kubeconfig.Namespace,
|
||||
Cluster: expiringClusters,
|
||||
User: expiringUsers,
|
||||
})
|
||||
}
|
||||
}
|
||||
return kSecretData, nil
|
||||
}
|
||||
|
||||
// filterKubeConfigs identifies the kubeconfig secrets based on the kubeconfigIdentifierSuffix
|
||||
func filterKubeConfigs(secrets []corev1.Secret) []corev1.Secret {
|
||||
filteredSecrets := []corev1.Secret{}
|
||||
for _, secret := range secrets {
|
||||
if strings.HasSuffix(secret.Name, kubeconfigIdentifierSuffix) {
|
||||
filteredSecrets = append(filteredSecrets, secret)
|
||||
}
|
||||
}
|
||||
return filteredSecrets
|
||||
}
|
||||
|
||||
// filterOwners allows only the secrets with Ownerreferences matching ownerKind
|
||||
func filterOwners(secrets []corev1.Secret, ownerKind string) []corev1.Secret {
|
||||
filteredSecrets := []corev1.Secret{}
|
||||
for _, secret := range secrets {
|
||||
for _, ownerRef := range secret.OwnerReferences {
|
||||
if ownerRef.Kind == ownerKind {
|
||||
filteredSecrets = append(filteredSecrets, secret)
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredSecrets
|
||||
}
|
||||
|
||||
func (store CertificateExpirationStore) getExpiringClusterCertificates(
|
||||
kubeconfig *clientcmdapi.Config) []kubeconfData {
|
||||
expiringClusterCertificates := make([]kubeconfData, 0)
|
||||
|
||||
// Iterate through each Cluster and identify expiration
|
||||
for clusterName, clusterData := range kubeconfig.Clusters {
|
||||
expirationDate, err := extractExpirationDateFromCertificate(clusterData.CertificateAuthorityData)
|
||||
if err != nil {
|
||||
log.Printf("Unable to parse certificate for %s : %v", clusterName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if isWithinDuration(expirationDate, store.ExpirationThreshold) {
|
||||
expiringClusterCertificates = append(expiringClusterCertificates, kubeconfData{
|
||||
Name: clusterName,
|
||||
CertificateName: "CertificateAuthorityData",
|
||||
ExpirationDate: expirationDate.String(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return expiringClusterCertificates
|
||||
}
|
||||
|
||||
func (store CertificateExpirationStore) getExpiringUserCertificates(
|
||||
kubeconfig *clientcmdapi.Config) []kubeconfData {
|
||||
expiringUserCertificates := make([]kubeconfData, 0)
|
||||
|
||||
// Iterate through each User and identify expiration
|
||||
for userName, userData := range kubeconfig.AuthInfos {
|
||||
expirationDate, err := extractExpirationDateFromCertificate(userData.ClientCertificateData)
|
||||
if err != nil {
|
||||
log.Printf("Unable to parse certificate for %s : %v", userName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if isWithinDuration(expirationDate, store.ExpirationThreshold) {
|
||||
expiringUserCertificates = append(expiringUserCertificates, kubeconfData{
|
||||
Name: userName,
|
||||
CertificateName: "ClientCertificateData",
|
||||
ExpirationDate: expirationDate.String(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return expiringUserCertificates
|
||||
}
|
||||
|
||||
// getKubeconfSecrets filters the kubeconf secrets
|
||||
func (store CertificateExpirationStore) getKubeconfSecrets() ([]corev1.Secret, error) {
|
||||
secrets, err := store.getSecrets(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kubeconfigs := filterKubeConfigs(secrets.Items)
|
||||
kubeconfigs = filterOwners(kubeconfigs, "KubeadmControlPlane")
|
||||
return kubeconfigs, nil
|
||||
}
|
||||
|
||||
// GetExpiringNodeCertificates runs through all the nodes and identifies expiration
|
||||
func (store CertificateExpirationStore) GetExpiringNodeCertificates() ([]NodeCert, error) {
|
||||
// Node will be updated with an annotation with the expiry content (Activity
|
||||
// of HostConfig Operator - 'check-expiry' CR Object) every day (Cron like
|
||||
// activity is performed by reconcile tag in the Operator) Below code is
|
||||
// implemented to just read the annotation, parse it, identify expirable
|
||||
// content and report back
|
||||
|
||||
// Expected Annotation Format:
|
||||
// "cert-expiration": "{ admin.conf: Aug 06, 2021 12:36 UTC },
|
||||
// { apiserver: Aug 06, 2021 12:36 UTC },
|
||||
// { apiserver-etcd-client: Aug 06, 2021 12:36 UTC },
|
||||
// { apiserver-kubelet-client: Aug 06, 2021 12:36 UTC },
|
||||
// { controller-manager.conf: Aug 06, 2021 12:36 UTC },
|
||||
// { etcd-healthcheck-client: Aug 06, 2021 12:36 UTC },
|
||||
// { etcd-peer: Aug 06, 2021 12:36 UTC },
|
||||
// { etcd-server: Aug 06, 2021 12:36 UTC },
|
||||
// { front-proxy-client: Aug 06, 2021 12:36 UTC },
|
||||
// { scheduler.conf: Aug 06, 2021 12:36 UTC },
|
||||
// { ca: Aug 04, 2030 12:36 UTC },
|
||||
// { etcd-ca: Aug 04, 2030 12:36 UTC },
|
||||
// { front-proxy-ca: Aug 04, 2030 12:36 UTC }"
|
||||
|
||||
nodes, err := store.getNodes(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeData := make([]NodeCert, 0)
|
||||
for _, node := range nodes.Items {
|
||||
expiringNodeCertificates := store.getExpiringNodeCertificates(node)
|
||||
if len(expiringNodeCertificates) > 0 {
|
||||
nodeData = append(nodeData, NodeCert{
|
||||
Name: node.Name,
|
||||
Namespace: node.Namespace,
|
||||
ExpiringCertificates: expiringNodeCertificates,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nodeData, nil
|
||||
}
|
||||
|
||||
// getSecrets returns the Nodes list based on the listOptions
|
||||
func (store CertificateExpirationStore) getNodes(listOptions metav1.ListOptions) (*corev1.NodeList, error) {
|
||||
return store.Kclient.ClientSet().CoreV1().Nodes().List(listOptions)
|
||||
}
|
||||
|
||||
// getExpiringNodeCertificates skims through all the node certificates and returns
|
||||
// the ones lesser than threshold
|
||||
func (store CertificateExpirationStore) getExpiringNodeCertificates(node corev1.Node) map[string]string {
|
||||
if cert, found := node.ObjectMeta.Annotations[nodeCertExpirationAnnotation]; found {
|
||||
certificateList := splitAsList(cert)
|
||||
|
||||
expiringCertificates := map[string]string{}
|
||||
for _, certificate := range certificateList {
|
||||
certificateName, expirationDate := identifyCertificateNameAndExpirationDate(certificate)
|
||||
if certificateName != "" && isWithinDuration(expirationDate, store.ExpirationThreshold) {
|
||||
expiringCertificates[certificateName] = expirationDate.String()
|
||||
}
|
||||
}
|
||||
return expiringCertificates
|
||||
}
|
||||
log.Printf("%s annotation missing for node %s in %s", nodeCertExpirationAnnotation,
|
||||
node.Name, node.Namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
// splitAsList performes the required string manipulations and returns list of items
|
||||
func splitAsList(value string) []string {
|
||||
return strings.Split(strings.ReplaceAll(value, "{", ""), "},")
|
||||
}
|
||||
|
||||
// identifyCertificateNameAndExpirationDate performs string manipulations and returns
|
||||
// certificate name and its expiration date
|
||||
func identifyCertificateNameAndExpirationDate(certificate string) (string, time.Time) {
|
||||
certificateName := strings.TrimSpace(strings.Split(certificate, ":")[0])
|
||||
expirationDate := strings.TrimSpace(strings.Split(certificate, ":")[1]) +
|
||||
":" +
|
||||
strings.TrimSpace(strings.ReplaceAll(strings.Split(certificate, ":")[2], "}", ""))
|
||||
|
||||
formattedExpirationDate, err := time.Parse(timeFormat, expirationDate)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
return "", time.Time{}
|
||||
}
|
||||
return certificateName, formattedExpirationDate
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 checkexpiration_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/checkexpiration"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/client"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/client/fake"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
expiryThreshold int
|
||||
nodeTestFile string
|
||||
kubeconfTestFile string
|
||||
tlsSecretTestFile string
|
||||
nodeExpirationYear string
|
||||
expectedExpiringNodeCount int
|
||||
expectedExpiringKubeConfigCount int
|
||||
expectedExpiringTLSSecretCount int
|
||||
}
|
||||
|
||||
var (
|
||||
testCases = []*testCase{
|
||||
{
|
||||
name: "empty-expect-error",
|
||||
expectedExpiringNodeCount: 0,
|
||||
expectedExpiringKubeConfigCount: 0,
|
||||
expectedExpiringTLSSecretCount: 0,
|
||||
},
|
||||
{
|
||||
name: "node-cert-expiring",
|
||||
nodeTestFile: nodeFile,
|
||||
nodeExpirationYear: "2021",
|
||||
expiryThreshold: testThreshold, // 20 years
|
||||
expectedExpiringNodeCount: 1,
|
||||
},
|
||||
{
|
||||
name: "node-cert-not-expiring",
|
||||
nodeExpirationYear: "2025",
|
||||
nodeTestFile: nodeFile,
|
||||
expiryThreshold: 10,
|
||||
expectedExpiringNodeCount: 0,
|
||||
},
|
||||
{
|
||||
name: "all-certs-not-expiring",
|
||||
nodeExpirationYear: "2025",
|
||||
nodeTestFile: nodeFile,
|
||||
tlsSecretTestFile: tlsSecretFile,
|
||||
kubeconfTestFile: kubeconfFile,
|
||||
expiryThreshold: 1,
|
||||
expectedExpiringNodeCount: 0,
|
||||
expectedExpiringKubeConfigCount: 0,
|
||||
expectedExpiringTLSSecretCount: 0,
|
||||
},
|
||||
{
|
||||
name: "all-certs-expiring",
|
||||
nodeExpirationYear: "2021",
|
||||
nodeTestFile: nodeFile,
|
||||
tlsSecretTestFile: tlsSecretFile,
|
||||
kubeconfTestFile: kubeconfFile,
|
||||
expiryThreshold: testThreshold,
|
||||
expectedExpiringNodeCount: 1,
|
||||
expectedExpiringKubeConfigCount: 1,
|
||||
expectedExpiringTLSSecretCount: 1,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestCheckExpiration(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
cfg, _ := testutil.InitConfig(t)
|
||||
settings := func() (*config.Config, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
var objects []runtime.Object
|
||||
|
||||
if testCase.nodeExpirationYear != "" && testCase.nodeTestFile != "" {
|
||||
objects = append(objects, getNodeObject(t, testCase.nodeTestFile, testCase.nodeExpirationYear))
|
||||
}
|
||||
|
||||
if testCase.tlsSecretTestFile != "" {
|
||||
objects = append(objects, getSecretObject(t, testCase.tlsSecretTestFile))
|
||||
}
|
||||
|
||||
if testCase.kubeconfTestFile != "" {
|
||||
objects = append(objects, getSecretObject(t, testCase.kubeconfTestFile))
|
||||
}
|
||||
|
||||
ra := fake.WithTypedObjects(objects...)
|
||||
|
||||
clientFactory := func(_ string, _ string) (client.Interface, error) {
|
||||
return fake.NewClient(ra), nil
|
||||
}
|
||||
|
||||
store, err := checkexpiration.NewStore(settings, clientFactory, "", "", testCase.expiryThreshold)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expirationInfo := store.GetExpiringCertificates()
|
||||
|
||||
assert.Len(t, expirationInfo.Kubeconfs, testCase.expectedExpiringKubeConfigCount)
|
||||
|
||||
assert.Len(t, expirationInfo.TLSSecrets, testCase.expectedExpiringTLSSecretCount)
|
||||
|
||||
assert.Len(t, expirationInfo.NodeCerts, testCase.expectedExpiringNodeCount)
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 checkexpiration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/client"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
"opendev.org/airship/airshipctl/pkg/util/yaml"
|
||||
)
|
||||
|
||||
// CheckFlags flags given for checking the expiration
|
||||
type CheckFlags struct {
|
||||
Threshold int
|
||||
FormatType string
|
||||
Kubeconfig string
|
||||
KubeContext string
|
||||
}
|
||||
|
||||
// CheckCommand check expiration command
|
||||
type CheckCommand struct {
|
||||
Options CheckFlags
|
||||
CfgFactory config.Factory
|
||||
ClientFactory client.Factory
|
||||
}
|
||||
|
||||
// ExpirationStore captures expiration information of all expirable entities in the cluster
|
||||
type ExpirationStore struct {
|
||||
TLSSecrets []TLSSecret `json:"tlsSecrets,omitempty" yaml:"tlsSecrets,omitempty"`
|
||||
Kubeconfs []Kubeconfig `json:"kubeconfs,omitempty" yaml:"kubeconfs,omitempty"`
|
||||
NodeCerts []NodeCert `json:"nodeCerts,omitempty" yaml:"nodeCerts,omitempty"`
|
||||
}
|
||||
|
||||
// TLSSecret captures expiration information of certificates embedded in TLS secrets
|
||||
type TLSSecret struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
ExpiringCertificates map[string]string `json:"certificate,omitempty" yaml:"certificate,omitempty"`
|
||||
}
|
||||
|
||||
// Kubeconfig captures expiration information of all kubeconfigs
|
||||
type Kubeconfig struct {
|
||||
SecretName string `json:"secretName,omitempty" yaml:"secretName,omitempty"`
|
||||
SecretNamespace string `json:"secretNamespace,omitempty" yaml:"secretNamespace,omitempty"`
|
||||
Cluster []kubeconfData `json:"cluster,omitempty" yaml:"cluster,omitempty"`
|
||||
User []kubeconfData `json:"user,omitempty" yaml:"user,omitempty"`
|
||||
}
|
||||
|
||||
// kubeconfData captures cluster ca certificate expiration information and kubeconfig's user's certificate
|
||||
type kubeconfData struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
CertificateName string `json:"certificateName,omitempty" yaml:"certificateName,omitempty"`
|
||||
ExpirationDate string `json:"expirationDate,omitempty" yaml:"expirationDate,omitempty"`
|
||||
}
|
||||
|
||||
// NodeCert captures certificate expiry information for certificates on each node
|
||||
type NodeCert struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
ExpiringCertificates map[string]string `json:"certificate,omitempty" yaml:"certificate,omitempty"`
|
||||
}
|
||||
|
||||
// RunE is the implementation of check command
|
||||
func (c *CheckCommand) RunE(w io.Writer) error {
|
||||
if !strings.EqualFold(c.Options.FormatType, "json") && !strings.EqualFold(c.Options.FormatType, "yaml") {
|
||||
return ErrInvalidFormat{RequestedFormat: c.Options.FormatType}
|
||||
}
|
||||
|
||||
secretStore, err := NewStore(c.CfgFactory, c.ClientFactory, c.Options.Kubeconfig,
|
||||
c.Options.KubeContext, c.Options.Threshold)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expirationInfo := secretStore.GetExpiringCertificates()
|
||||
|
||||
if c.Options.FormatType == "yaml" {
|
||||
err = yaml.WriteOut(w, expirationInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
buffer, err := json.MarshalIndent(expirationInfo, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExpiringCertificates encapsulates all the different expirable entities in the cluster
|
||||
func (store CertificateExpirationStore) GetExpiringCertificates() ExpirationStore {
|
||||
expiringTLSCertificates, err := store.GetExpiringTLSCertificates()
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
|
||||
expiringKubeConfCertificates, err := store.GetExpiringKubeConfigs()
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
|
||||
expiringNodeCertificates, err := store.GetExpiringNodeCertificates()
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
|
||||
return ExpirationStore{
|
||||
TLSSecrets: expiringTLSCertificates,
|
||||
Kubeconfs: expiringKubeConfCertificates,
|
||||
NodeCerts: expiringNodeCertificates,
|
||||
}
|
||||
}
|
@ -1,257 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 checkexpiration_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/checkexpiration"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/client"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/client/fake"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
const (
|
||||
testThreshold = 7200
|
||||
|
||||
nodeFile = "testdata/node.yaml"
|
||||
kubeconfFile = "testdata/kubeconfig.yaml"
|
||||
tlsSecretFile = "testdata/tls-secret.yaml" //nolint:gosec
|
||||
|
||||
expectedJSONOutput = ` {
|
||||
"tlsSecrets": [
|
||||
{
|
||||
"name": "test-cluster-etcd",
|
||||
"namespace": "target-infra",
|
||||
"certificate": {
|
||||
"ca.crt": "2030-08-31 10:12:49 +0000 UTC",
|
||||
"tls.crt": "2030-08-31 10:12:49 +0000 UTC"
|
||||
}
|
||||
}
|
||||
],
|
||||
"kubeconfs": [
|
||||
{
|
||||
"secretName": "test-cluster-kubeconfig",
|
||||
"secretNamespace": "target-infra",
|
||||
"cluster": [
|
||||
{
|
||||
"name": "workload-cluster",
|
||||
"certificateName": "CertificateAuthorityData",
|
||||
"expirationDate": "2030-08-31 10:12:48 +0000 UTC"
|
||||
}
|
||||
],
|
||||
"user": [
|
||||
{
|
||||
"name": "workload-cluster-admin",
|
||||
"certificateName": "ClientCertificateData",
|
||||
"expirationDate": "2021-09-02 10:12:50 +0000 UTC"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodeCerts": [
|
||||
{
|
||||
"name": "test-node",
|
||||
"certificate": {
|
||||
"admin.conf": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"apiserver": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"apiserver-etcd-client": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"apiserver-kubelet-client": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"ca": "2021-08-04 12:36:00 +0000 UTC",
|
||||
"controller-manager.conf": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"etcd-ca": "2021-08-04 12:36:00 +0000 UTC",
|
||||
"etcd-healthcheck-client": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"etcd-peer": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"etcd-server": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"front-proxy-ca": "2021-08-04 12:36:00 +0000 UTC",
|
||||
"front-proxy-client": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"scheduler.conf": "2021-08-06 12:36:00 +0000 UTC"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
expectedYAMLOutput = `
|
||||
---
|
||||
kubeconfs:
|
||||
- cluster:
|
||||
- certificateName: CertificateAuthorityData
|
||||
expirationDate: 2030-08-31 10:12:48 +0000 UTC
|
||||
name: workload-cluster
|
||||
secretName: test-cluster-kubeconfig
|
||||
secretNamespace: target-infra
|
||||
user:
|
||||
- certificateName: ClientCertificateData
|
||||
expirationDate: 2021-09-02 10:12:50 +0000 UTC
|
||||
name: workload-cluster-admin
|
||||
tlsSecrets:
|
||||
- certificate:
|
||||
ca.crt: 2030-08-31 10:12:49 +0000 UTC
|
||||
tls.crt: 2030-08-31 10:12:49 +0000 UTC
|
||||
name: test-cluster-etcd
|
||||
namespace: target-infra
|
||||
nodeCerts:
|
||||
- name: test-node
|
||||
certificate:
|
||||
admin.conf: 2021-08-06 12:36:00 +0000 UTC
|
||||
apiserver: 2021-08-06 12:36:00 +0000 UTC
|
||||
apiserver-etcd-client: 2021-08-06 12:36:00 +0000 UTC
|
||||
apiserver-kubelet-client: 2021-08-06 12:36:00 +0000 UTC
|
||||
ca: 2021-08-04 12:36:00 +0000 UTC
|
||||
controller-manager.conf: 2021-08-06 12:36:00 +0000 UTC
|
||||
etcd-ca: 2021-08-04 12:36:00 +0000 UTC
|
||||
etcd-healthcheck-client: 2021-08-06 12:36:00 +0000 UTC
|
||||
etcd-peer: 2021-08-06 12:36:00 +0000 UTC
|
||||
etcd-server: 2021-08-06 12:36:00 +0000 UTC
|
||||
front-proxy-ca: 2021-08-04 12:36:00 +0000 UTC
|
||||
front-proxy-client: 2021-08-06 12:36:00 +0000 UTC
|
||||
scheduler.conf: 2021-08-06 12:36:00 +0000 UTC
|
||||
...
|
||||
`
|
||||
)
|
||||
|
||||
func TestRunE(t *testing.T) {
|
||||
tests := []struct {
|
||||
testCaseName string
|
||||
testErr string
|
||||
checkFlags checkexpiration.CheckFlags
|
||||
cfgFactory config.Factory
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
testCaseName: "invalid-input-format",
|
||||
cfgFactory: func() (*config.Config, error) {
|
||||
return nil, nil
|
||||
},
|
||||
checkFlags: checkexpiration.CheckFlags{
|
||||
Threshold: 0,
|
||||
FormatType: "test-yaml",
|
||||
},
|
||||
testErr: checkexpiration.ErrInvalidFormat{RequestedFormat: "test-yaml"}.Error(),
|
||||
},
|
||||
{
|
||||
testCaseName: "valid-input-format-json",
|
||||
cfgFactory: func() (*config.Config, error) {
|
||||
cfg, _ := testutil.InitConfig(t)
|
||||
return cfg, nil
|
||||
},
|
||||
checkFlags: checkexpiration.CheckFlags{
|
||||
Threshold: testThreshold,
|
||||
FormatType: "json",
|
||||
Kubeconfig: "",
|
||||
},
|
||||
testErr: "",
|
||||
expectedOutput: expectedJSONOutput,
|
||||
},
|
||||
{
|
||||
testCaseName: "valid-input-format-yaml",
|
||||
cfgFactory: func() (*config.Config, error) {
|
||||
cfg, _ := testutil.InitConfig(t)
|
||||
return cfg, nil
|
||||
},
|
||||
checkFlags: checkexpiration.CheckFlags{
|
||||
Threshold: testThreshold,
|
||||
FormatType: "yaml",
|
||||
},
|
||||
testErr: "",
|
||||
expectedOutput: expectedYAMLOutput,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.testCaseName, func(t *testing.T) {
|
||||
objects := []runtime.Object{
|
||||
getSecretObject(t, tlsSecretFile),
|
||||
getSecretObject(t, kubeconfFile),
|
||||
getNodeObject(t, nodeFile, "2021"),
|
||||
}
|
||||
ra := fake.WithTypedObjects(objects...)
|
||||
|
||||
command := checkexpiration.CheckCommand{
|
||||
Options: tt.checkFlags,
|
||||
CfgFactory: tt.cfgFactory,
|
||||
ClientFactory: func(_ string, _ string) (client.Interface, error) {
|
||||
return fake.NewClient(ra), nil
|
||||
},
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
err := command.RunE(&buffer)
|
||||
|
||||
if tt.testErr != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.testErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
t.Log(buffer.String())
|
||||
switch tt.checkFlags.FormatType {
|
||||
case "json":
|
||||
assert.JSONEq(t, tt.expectedOutput, buffer.String())
|
||||
case "yaml":
|
||||
assert.YAMLEq(t, tt.expectedOutput, buffer.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getSecretObject(t *testing.T, fileName string) *v1.Secret {
|
||||
t.Helper()
|
||||
|
||||
object := readObjectFromFile(t, fileName)
|
||||
secret, ok := object.(*v1.Secret)
|
||||
require.True(t, ok)
|
||||
|
||||
return secret
|
||||
}
|
||||
|
||||
func getNodeObject(t *testing.T, fileName string, expirationYear string) *v1.Node {
|
||||
t.Helper()
|
||||
|
||||
object := readObjectFromFile(t, fileName)
|
||||
node, ok := object.(*v1.Node)
|
||||
require.True(t, ok)
|
||||
|
||||
node.Annotations["cert-expiration"] = strings.ReplaceAll(node.Annotations["cert-expiration"],
|
||||
"{{year}}", expirationYear)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func readObjectFromFile(t *testing.T, fileName string) runtime.Object {
|
||||
t.Helper()
|
||||
|
||||
contents, err := ioutil.ReadFile(fileName)
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonContents, err := yaml.ToJSON(contents)
|
||||
require.NoError(t, err)
|
||||
|
||||
object, err := runtime.Decode(scheme.Codecs.UniversalDeserializer(), jsonContents)
|
||||
require.NoError(t, err)
|
||||
|
||||
return object
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 checkexpiration
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ErrInvalidFormat is called when the user provides format other than yaml/json
|
||||
type ErrInvalidFormat struct {
|
||||
RequestedFormat string
|
||||
}
|
||||
|
||||
func (e ErrInvalidFormat) Error() string {
|
||||
return fmt.Sprintf("invalid output format specified %s. Allowed values are json|yaml", e.RequestedFormat)
|
||||
}
|
||||
|
||||
// ErrPEMFail is called where there is a PEM related failure while parsing the certificate block
|
||||
type ErrPEMFail struct {
|
||||
Context string
|
||||
Err string
|
||||
}
|
||||
|
||||
func (e ErrPEMFail) Error() string {
|
||||
return fmt.Sprintf("failed to %s certificate PEM: %s", e.Context, e.Err)
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,6 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Node
|
||||
metadata:
|
||||
annotations:
|
||||
cert-expiration: "{ admin.conf: Aug 06, {{year}} 12:36 UTC },{ apiserver: Aug 06, {{year}} 12:36 UTC },{ apiserver-etcd-client: Aug 06, {{year}} 12:36 UTC },{ apiserver-kubelet-client: Aug 06, {{year}} 12:36 UTC },{ controller-manager.conf: Aug 06, {{year}} 12:36 UTC },{ etcd-healthcheck-client: Aug 06, {{year}} 12:36 UTC },{ etcd-peer: Aug 06, {{year}} 12:36 UTC },{ etcd-server: Aug 06, {{year}} 12:36 UTC },{ front-proxy-client: Aug 06, {{year}} 12:36 UTC },{ scheduler.conf: Aug 06, {{year}} 12:36 UTC },{ ca: Aug 04, {{year}} 12:36 UTC },{ etcd-ca: Aug 04, {{year}} 12:36 UTC },{ front-proxy-ca: Aug 04, {{year}} 12:36 UTC }"
|
||||
name: test-node
|
@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
data:
|
||||
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5ekNDQWJPZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01Ea3dNakV3TURjME9Wb1hEVE13TURnek1URXdNVEkwT1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTHdVCkErWWRNcWF0clI4WWVHcEVYTFlIeVZHRi9Na0g1L3FqWEdpcmxITCtiNEhXYmg2enkvSWY3N1MzNWhnTzFPSkcKcG5wdmk5ck5ISEMyV3hNblVUOFVxSE1lYjNyb0phMlFpME1sNGlNTFFncTd0TGhLQ04zTnFwYmk5OEJ4d1VxSAo3eGkzWmU3WEZ2NVJyRlpFa2hicW9ycVJRZzg0cHdRTTNvMkh1NmJSWElETjc5bnVMV3piZ0pYUzhwMytnZHFuCkxIa3owOGN4VkxxZmJPOFMxOFEwdWJUbnY3RjVBblBPZkhJY2xyR2h3MFVUZXRWZGxuVmNISnRpZ29xYjBBdysKOUY5WkNaN0ZZUzE2eEJ0L0N0OHpKZEQ3MEFGZ2NMRm4vSTJlSG05bTFSK0FISWxJU3ZLbzl0OVBtekJxWS9tbgpHOUFoektYSlBTSHRPbEJqMXBzQ0F3RUFBYU1tTUNRd0RnWURWUjBQQVFIL0JBUURBZ0trTUJJR0ExVWRFd0VCCi93UUlNQVlCQWY4Q0FRQXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQVAzLzdmYzhBNlFjWU5CUnQxdDRWQ0QKRnYwMDB0Q09mVjVxdnNOU2RQYTRZY2NaTDBmT2dUVmtNbGczbTVRMmVKUkpVTHR5V0NHQ3BNcHRFR2duMGI3eQpIWkhtWkpkZjZBN0twbFRqQWxyemRDOUpPTVBrQUtjaXhNcWhPcDFxdTI0dHl5eEZQZWdMeTE2SU5ZMGl5ZzI2ClJhYkowREdQcTNZUitwZHphRkZ2YUN6bk1yTGtDckJpV0xvUmdrK2xaT3NoUU1EaHl4Y1crQW5mcHRINHlxM2YKZjhhdFZPVGprMGVIYVlQUlNKSlFlM0RLOXFEY0V4bVFib1orM1NLYXRtK0RNMWVieE5CNlVKaGdYWEluZ3dQQwp4czV0azJkUGlJaXZldXZYbGVqTkZ1c0RzekV1NU9ZN08xVzZNSUdmNlBPQ3JmSzk0S3JhVWVhWng4bUlRcE09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5ekNDQWJPZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01Ea3dNakV3TURjME9Wb1hEVE13TURnek1URXdNVEkwT1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTHdVCkErWWRNcWF0clI4WWVHcEVYTFlIeVZHRi9Na0g1L3FqWEdpcmxITCtiNEhXYmg2enkvSWY3N1MzNWhnTzFPSkcKcG5wdmk5ck5ISEMyV3hNblVUOFVxSE1lYjNyb0phMlFpME1sNGlNTFFncTd0TGhLQ04zTnFwYmk5OEJ4d1VxSAo3eGkzWmU3WEZ2NVJyRlpFa2hicW9ycVJRZzg0cHdRTTNvMkh1NmJSWElETjc5bnVMV3piZ0pYUzhwMytnZHFuCkxIa3owOGN4VkxxZmJPOFMxOFEwdWJUbnY3RjVBblBPZkhJY2xyR2h3MFVUZXRWZGxuVmNISnRpZ29xYjBBdysKOUY5WkNaN0ZZUzE2eEJ0L0N0OHpKZEQ3MEFGZ2NMRm4vSTJlSG05bTFSK0FISWxJU3ZLbzl0OVBtekJxWS9tbgpHOUFoektYSlBTSHRPbEJqMXBzQ0F3RUFBYU1tTUNRd0RnWURWUjBQQVFIL0JBUURBZ0trTUJJR0ExVWRFd0VCCi93UUlNQVlCQWY4Q0FRQXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQVAzLzdmYzhBNlFjWU5CUnQxdDRWQ0QKRnYwMDB0Q09mVjVxdnNOU2RQYTRZY2NaTDBmT2dUVmtNbGczbTVRMmVKUkpVTHR5V0NHQ3BNcHRFR2duMGI3eQpIWkhtWkpkZjZBN0twbFRqQWxyemRDOUpPTVBrQUtjaXhNcWhPcDFxdTI0dHl5eEZQZWdMeTE2SU5ZMGl5ZzI2ClJhYkowREdQcTNZUitwZHphRkZ2YUN6bk1yTGtDckJpV0xvUmdrK2xaT3NoUU1EaHl4Y1crQW5mcHRINHlxM2YKZjhhdFZPVGprMGVIYVlQUlNKSlFlM0RLOXFEY0V4bVFib1orM1NLYXRtK0RNMWVieE5CNlVKaGdYWEluZ3dQQwp4czV0azJkUGlJaXZldXZYbGVqTkZ1c0RzekV1NU9ZN08xVzZNSUdmNlBPQ3JmSzk0S3JhVWVhWng4bUlRcE09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBdkJRRDVoMHlwcTJ0SHhoNGFrUmN0Z2ZKVVlYOHlRZm4rcU5jYUt1VWN2NXZnZFp1CkhyUEw4aC92dExmbUdBN1U0a2FtZW0rTDJzMGNjTFpiRXlkUlB4U29jeDV2ZXVnbHJaQ0xReVhpSXd0Q0NydTAKdUVvSTNjMnFsdUwzd0hIQlNvZnZHTGRsN3RjVy9sR3NWa1NTRnVxaXVwRkNEemluQkF6ZWpZZTdwdEZjZ00zdgoyZTR0Yk51QWxkTHluZjZCMnFjc2VUUFR4ekZVdXA5czd4TFh4RFM1dE9lL3NYa0NjODU4Y2h5V3NhSERSUk42CjFWMldkVndjbTJLQ2lwdlFERDcwWDFrSm5zVmhMWHJFRzM4SzN6TWwwUHZRQVdCd3NXZjhqWjRlYjJiVkg0QWMKaVVoSzhxajIzMCtiTUdwaithY2IwQ0hNcGNrOUllMDZVR1BXbXdJREFRQUJBb0lCQUJESkZJUUFEUm8xRytOUAprc2VoTEVrT3J0ZjR4bFBHd2R4cm9mNnhlWUU5MWdQWGVHS0RGMnVYa0JRbjZZQXlLcXU3TkhadTZDTng5TnpXCldaQi9ETkE5YnI4L2N5R2NBR2phSXFPdWlOMHB6dzRZTEl2YUI2cU1CWEtMOVNLV3hISjdhVXBpYTlXQ0dzbzkKemN5eE4veVZta3BlVm0vM1ZXaVdJWEt1TDRBMnZmaHJjbHdVc3NwNFA4Q1dZUFZVbExOT0JsYlQrNy9qQ1RubwpyRDVYcjNDVEFiUmNBNEdvcGx1ZFphaUtqY2FpZXpYeHdTaGFmWkFURWJJM0xwejZSTnRBMDRLbjEyR0JLRDQwCk1zV21xUU1zRE4zUkgveFdKbnp5TFVSTGVlcDdRUTdXQytZYmRmdFpTZ3FKUkRNYmw1aHdwUXRLeFBoUmhHakcKSGEwWUVIa0NnWUVBNkdjSCtzUzczd3FwUmloZXJ3aFVEY21Na3VEZDBiMkEyWlhpcjBRUStKUXZrVHVSd2NZVgpHdDU3Zm40UnBybFUwNzByMTZRQzcrdTU0OVdQVlFpbExvMURvVUVHMC9QVEhSV2ZQVjBtOWh2cGF2akdtMG1zCmpmd20rMXZ1WGFiS0RMblg0dXcxQ0JKSU9vdmJ6RnhrTGJWZk91YVhrN1BhckhiOGdHRHR2ZjhDZ1lFQXp5elcKZ3VtT25tMmVNYy80ZHJmMDI2Q1doZk1ZMXF0VnFEb2s4anVlMVlEaVdhZEMwOGo3VEVoQ1dTQUZPOC9ZOVBDRApKQ3cvcGpUZThERlRLZEw3d2t5ZVUrS2JJRFRLNHJ5clMzenZmV3Q4Zjg2ZmU5SVd3L3FDaEN2cm5tSTdNeS8rCkowL1BCZFFGL0xkeS9PWlVGcW9wblJNS1VnVTJXMWhxRTJOcUgyVUNnWUExV0NqeHU2U3YvcDk2Tmh2OXF6aTMKN1hKeDZHR2lHaEJ3WVVJbUhzYVNlRmt1eWZDYi9ONnRTekluaDhKL2RYenVHVGJ1Q1h5UEc1bVFuVjJJRkRMdQpLNGpCZzg2UWFpQWtSZWxHU1pKKzNVdEh2WkRBNWpsUVlmZUVyTVpiQXNUUUJQeHozdW9SVHpqN0QwMUZiRk9tClZrSmtuN2RkTk9SVnYvNFhiYWhFZXdLQmdENURTM1NzbktBZ2NacW0xaFZYMDg3dHhFOGRjQ21UOUhwS2Z6QU4KbXY2dmJWZGtYVUVvOWQxSEdpbU81Z1BEdzRCWmlCQW0vRG9IU2JrR0dlaEg4RUhFcFJDdzJjNGtENVYwL2tZQgpsamdyUlk5am1hcXN5UXE5RHR5S0ZwWFREOWVpWk0rTHZMd1RySGoyNlNmNFVPMCsxcUxPUmh2QVZVVytuS0tYCkRoM0JBb0dBRjVEU0R2U2ltVjhtaHFWbjc2M1dwOUJXN0RwbTAxTE5qMHRZbVFhZVpyK3VoZ1BKdW5SYzhBUE4KWXRudjlsY04wSkx1MzhWaWI3eVFzVmgvVUsvYVdwS2w1YUZzTXdRMVNJTUJXUTVEeVR0VEE0VGZXQzFUSUYxUQpuempLd0NSTHBaZ1F6ZkNCaTBIT3doZ0pUaDZBNng4SG1hYXU5ZmZLdE90SUpkTUZ5d1E9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
cluster.x-k8s.io/cluster-name: test-cluster
|
||||
name: test-cluster-etcd
|
||||
namespace: target-infra
|
||||
type: Opaque
|
Loading…
Reference in New Issue
Block a user