Adds command objects for cluster check-certificate-expiration
Reference:- https://hackmd.io/aGaz7YXSSHybGcyol8vYEw Previous work:- https://review.opendev.org/#/c/755291/ Below is the complete ordered flow of PS for the feature: https://review.opendev.org/#/c/760498/ - Cobra command https://review.opendev.org/#/c/760501/ - Command Objects https://review.opendev.org/#/c/760504/ - TLS check https://review.opendev.org/#/c/760517/ - Kubeconf check https://review.opendev.org/#/c/760532/ - Node check https://review.opendev.org/#/c/760537/ - Combined Unit tests Change-Id: Ie0fac7799724b7fb2255e387b7e90b26159bda5c Relates-To: #391
This commit is contained in:
parent
260d6905a4
commit
dd03db0916
@ -17,8 +17,9 @@ package checkexpiration
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"opendev.org/airship/airshipctl/pkg/cluster/checkexpiration"
|
||||||
"opendev.org/airship/airshipctl/pkg/config"
|
"opendev.org/airship/airshipctl/pkg/config"
|
||||||
"opendev.org/airship/airshipctl/pkg/errors"
|
"opendev.org/airship/airshipctl/pkg/k8s/client"
|
||||||
"opendev.org/airship/airshipctl/pkg/log"
|
"opendev.org/airship/airshipctl/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,25 +55,31 @@ airshipctl cluster check-certificate-expiration -t 30 -o yaml --kubeconfig testc
|
|||||||
|
|
||||||
// NewCheckCommand creates a new command for generating secret information
|
// NewCheckCommand creates a new command for generating secret information
|
||||||
func NewCheckCommand(cfgFactory config.Factory) *cobra.Command {
|
func NewCheckCommand(cfgFactory config.Factory) *cobra.Command {
|
||||||
var threshold int
|
c := &checkexpiration.CheckCommand{
|
||||||
var contentType, kubeconfig string
|
Options: checkexpiration.CheckFlags{},
|
||||||
|
CfgFactory: cfgFactory,
|
||||||
|
ClientFactory: client.DefaultClient,
|
||||||
|
}
|
||||||
|
|
||||||
checkCmd := &cobra.Command{
|
checkCmd := &cobra.Command{
|
||||||
Use: "check-certificate-expiration",
|
Use: "check-certificate-expiration",
|
||||||
Short: "Check for expiring TLS certificates, secrets and kubeconfigs in the kubernetes cluster",
|
Short: "Check for expiring TLS certificates, secrets and kubeconfigs in the kubernetes cluster",
|
||||||
Long: checkLong[1:],
|
Long: checkLong[1:],
|
||||||
Example: checkExample,
|
Example: checkExample,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return errors.ErrNotImplemented{What: "check certificate expiration"}
|
return c.RunE(cmd.OutOrStdout())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
checkCmd.Flags().IntVarP(&threshold, "threshold", "t", -1,
|
checkCmd.Flags().IntVarP(&c.Options.Threshold, "threshold", "t", -1,
|
||||||
"The max expiration threshold in days before a certificate is"+
|
"The max expiration threshold in days before a certificate is"+
|
||||||
" expiring. Displays all the certificates by default")
|
" expiring. Displays all the certificates by default")
|
||||||
checkCmd.Flags().StringVarP(&contentType, "output", "o", "json", "Convert "+
|
checkCmd.Flags().StringVarP(&c.Options.FormatType, "output", "o", "json", "Convert "+
|
||||||
"output to yaml or json")
|
"output to yaml or json")
|
||||||
checkCmd.Flags().StringVar(&kubeconfig, kubeconfigFlag, "",
|
checkCmd.Flags().StringVar(&c.Options.Kubeconfig, kubeconfigFlag, "",
|
||||||
"Path to kubeconfig associated with cluster being managed")
|
"Path to kubeconfig associated with cluster being managed")
|
||||||
|
checkCmd.Flags().StringVar(&c.Options.KubeContext, "kubecontext", "",
|
||||||
|
"Kubeconfig context to be used")
|
||||||
|
|
||||||
err := checkCmd.MarkFlagRequired(kubeconfigFlag)
|
err := checkCmd.MarkFlagRequired(kubeconfigFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -28,7 +28,8 @@ airshipctl cluster check-certificate-expiration -t 30 -o yaml --kubeconfig testc
|
|||||||
|
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for check-certificate-expiration
|
-h, --help help for check-certificate-expiration
|
||||||
--kubeconfig string Path to kubeconfig associated with cluster being managed
|
--kubeconfig string Path to kubeconfig associated with cluster being managed
|
||||||
-o, --output string Convert output to yaml or json (default "json")
|
--kubecontext string Kubeconfig context to be used
|
||||||
-t, --threshold int The max expiration threshold in days before a certificate is expiring. Displays all the certificates by default (default -1)
|
-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)
|
||||||
|
@ -40,10 +40,11 @@ airshipctl cluster check-certificate-expiration -t 30 -o yaml --kubeconfig testc
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help help for check-certificate-expiration
|
-h, --help help for check-certificate-expiration
|
||||||
--kubeconfig string Path to kubeconfig associated with cluster being managed
|
--kubeconfig string Path to kubeconfig associated with cluster being managed
|
||||||
-o, --output string Convert output to yaml or json (default "json")
|
--kubecontext string Kubeconfig context to be used
|
||||||
-t, --threshold int The max expiration threshold in days before a certificate is expiring. Displays all the certificates by default (default -1)
|
-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)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
60
pkg/cluster/checkexpiration/checkexpiration.go
Normal file
60
pkg/cluster/checkexpiration/checkexpiration.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"opendev.org/airship/airshipctl/pkg/config"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/errors"
|
||||||
|
"opendev.org/airship/airshipctl/pkg/k8s/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CertificateExpirationStore is the customized client store
|
||||||
|
type CertificateExpirationStore struct {
|
||||||
|
Kclient client.Interface
|
||||||
|
Settings config.Factory
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expiration captures expiration information of all expirable entities in the cluster
|
||||||
|
type Expiration struct{}
|
||||||
|
|
||||||
|
// NewStore returns an instance of a CertificateExpirationStore
|
||||||
|
func NewStore(cfgFactory config.Factory, clientFactory client.Factory,
|
||||||
|
kubeconfig string, _ string) (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,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExpiringCertificates checks for the expiration data
|
||||||
|
// NOT IMPLEMENTED (guhan)
|
||||||
|
// TODO (guhan) check for TLS certificates, workload kubeconfig and node certificates
|
||||||
|
func (store CertificateExpirationStore) GetExpiringCertificates(expirationThreshold int) (Expiration, error) {
|
||||||
|
return Expiration{}, errors.ErrNotImplemented{What: "check certificate expiration logic"}
|
||||||
|
}
|
73
pkg/cluster/checkexpiration/command.go
Normal file
73
pkg/cluster/checkexpiration/command.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
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/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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
expirationInfo, err := secretStore.GetExpiringCertificates(c.Options.Threshold)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
106
pkg/cluster/checkexpiration/command_test.go
Normal file
106
pkg/cluster/checkexpiration/command_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testNotImplementedErr = "not implemented: check certificate expiration logic"
|
||||||
|
)
|
||||||
|
|
||||||
|
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: 5000,
|
||||||
|
FormatType: "json",
|
||||||
|
Kubeconfig: "",
|
||||||
|
},
|
||||||
|
testErr: testNotImplementedErr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCaseName: "valid-input-format-yaml",
|
||||||
|
cfgFactory: func() (*config.Config, error) {
|
||||||
|
cfg, _ := testutil.InitConfig(t)
|
||||||
|
return cfg, nil
|
||||||
|
},
|
||||||
|
checkFlags: checkexpiration.CheckFlags{
|
||||||
|
Threshold: 5000,
|
||||||
|
FormatType: "yaml",
|
||||||
|
},
|
||||||
|
testErr: testNotImplementedErr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.testCaseName, func(t *testing.T) {
|
||||||
|
var objects []runtime.Object
|
||||||
|
// TODO (guhan) append a dummy object for testing
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// TODO (guhan) add else part to check the actual vs expected o/p
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
26
pkg/cluster/checkexpiration/errors.go
Normal file
26
pkg/cluster/checkexpiration/errors.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user