@@ -6,50 +6,46 @@ import (
66 "regexp"
77 "time"
88
9- "github.com/openshift/library-go/pkg/operator/v1helpers"
109 "github.com/spf13/cobra"
11- "github.com/spf13/pflag"
1210
13- "k8s.io/apimachinery/pkg/util/sets"
1411 "k8s.io/apimachinery/pkg/util/wait"
1512 "k8s.io/apiserver/pkg/server"
1613 k8senvelopekmsv2 "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2"
1714 "k8s.io/client-go/rest"
18- "k8s.io/client-go/tools/clientcmd"
1915 "k8s.io/klog/v2"
16+
17+ applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1"
2018)
2119
22- const Subcommand = "kms-health-reporter"
20+ const (
21+ Subcommand = "kms-health-reporter"
22+ )
2323
2424// kmsSocketPattern matches the socket path each co-located KMSv2 plugin is
2525// mounted at, e.g. unix:///var/run/kmsplugin/kms-1.sock.
2626var kmsSocketPattern = regexp .MustCompile (`^unix:///var/run/kmsplugin/kms-(\d+)\.sock$` )
2727
28- // options' flag-bound fields are exported so the struct can be logged as a
29- // whole via klog.InfoS, which JSON-marshals its values.
30- type options struct {
31- KMSSockets []string
32- Interval time.Duration
33- ReadTimeout time.Duration
34- WriteTimeout time.Duration
35- NodeName string
36- Kubeconfig string
37-
38- newOperatorClient func (* rest.Config ) (v1helpers.OperatorClient , error )
39- }
28+ // NewEncryptionStatusWriterFunc builds the EncryptionStatusWriter for a target
29+ // apiserver operator status CR. fieldManager sets the owner in the
30+ // managedFields when doing SSA.
31+ type NewEncryptionStatusWriterFunc func (restConfig * rest.Config , fieldManager string ) (EncryptionStatusWriter , error )
32+
33+ // EncryptionStatusWriter is capable of applying the
34+ // KMSEncryptionStatusApplyConfiguration at the correct place in the operator's
35+ // status.
36+ type EncryptionStatusWriter func (ctx context.Context , status * applyoperatorv1.KMSEncryptionStatusApplyConfiguration ) error
4037
4138type Config struct {
42- operatorClient v1helpers. OperatorClient
43- prober * prober
39+ writeStatus EncryptionStatusWriter
40+ prober * prober
4441
4542 interval time.Duration
4643 writeTimeout time.Duration
47- nodeName string
4844}
4945
50- func NewCommand (ctx context.Context , newOperatorClient func ( * rest. Config ) (v1helpers. OperatorClient , error ) ) * cobra.Command {
46+ func NewCommand (ctx context.Context , newWriter NewEncryptionStatusWriterFunc ) * cobra.Command {
5147 o := & options {
52- newOperatorClient : newOperatorClient ,
48+ newWriter : newWriter ,
5349 }
5450
5551 cmd := & cobra.Command {
@@ -78,80 +74,17 @@ func NewCommand(ctx context.Context, newOperatorClient func(*rest.Config) (v1hel
7874 return cmd
7975}
8076
81- func (o * options ) addFlags (fs * pflag.FlagSet ) {
82- fs .StringSliceVar (& o .KMSSockets , "kms-sockets" , nil , "KMS plugin endpoints in unix:// URI format (e.g. unix:///var/run/kmsplugin/kms-1.sock)" )
83- fs .DurationVar (& o .Interval , "interval" , 30 * time .Second , "cadence between probe+emit cycles" )
84- fs .DurationVar (& o .ReadTimeout , "read-timeout" , 5 * time .Second , "deadline for each Status RPC" )
85- fs .DurationVar (& o .WriteTimeout , "write-timeout" , 10 * time .Second , "deadline for each condition update" )
86- fs .StringVar (& o .NodeName , "node-name" , "" , "node name recorded in the condition used to help to identify the origin" )
87- fs .StringVar (& o .Kubeconfig , "kubeconfig" , "" , "path to a kubeconfig; empty uses in-cluster config" )
88- }
89-
90- func (o * options ) validate () error {
91- if len (o .KMSSockets ) == 0 {
92- return fmt .Errorf ("--kms-sockets is required, at least one" )
93- }
94- socketSet := sets .New [string ]()
95- for _ , s := range o .KMSSockets {
96- if ! kmsSocketPattern .MatchString (s ) {
97- return fmt .Errorf ("--kms-sockets entry %q must match %s" , s , kmsSocketPattern )
98- }
99- if socketSet .Has (s ) {
100- return fmt .Errorf ("--kms-sockets entry %q is duplicated" , s )
101- }
102- socketSet .Insert (s )
103- }
104-
105- if o .Interval <= 0 {
106- return fmt .Errorf ("--interval must be positive" )
107- }
108- if o .ReadTimeout <= 0 {
109- return fmt .Errorf ("--read-timeout must be positive" )
110- }
111- if o .WriteTimeout <= 0 {
112- return fmt .Errorf ("--write-timeout must be positive" )
113- }
114- if o .NodeName == "" {
115- return fmt .Errorf ("--node-name is required" )
116- }
117-
118- return nil
119- }
120-
121- func (o * options ) Config (ctx context.Context ) (* Config , error ) {
122- // Empty kubeconfig falls back to the in-cluster config (service account
123- // token + KUBERNETES_SERVICE_HOST), which is the deployed path.
124- restCfg , err := clientcmd .BuildConfigFromFlags ("" , o .Kubeconfig )
125- if err != nil {
126- return nil , fmt .Errorf ("build rest config: %w" , err )
127- }
128-
129- operatorClient , err := o .newOperatorClient (restCfg )
130- if err != nil {
131- return nil , fmt .Errorf ("build operator client: %w" , err )
132- }
133-
134- plugins , err := buildPlugins (ctx , o .KMSSockets , o .ReadTimeout )
135- if err != nil {
136- return nil , err
137- }
138-
139- return & Config {
140- operatorClient : operatorClient ,
141- prober : newProber (plugins ),
142- interval : o .Interval ,
143- writeTimeout : o .WriteTimeout ,
144- nodeName : o .NodeName ,
145- }, nil
146- }
147-
14877func (c * Config ) Run (ctx context.Context ) error {
14978 wait .JitterUntilWithContext (ctx , func (ctx context.Context ) {
15079 // Each Status RPC enforces the read timeout internally (set at dial
15180 // time); ctx here only carries shutdown cancellation.
152- conditions := c .prober .probeAll (ctx )
153- // TODO: hand conditions to the writer once it lands; logging is a placeholder.
154- klog .InfoS ("kms plugin health" , "conditions" , conditions )
81+ reports := c .prober .probeAll (ctx )
82+
83+ writeCtx , cancel := context .WithTimeout (ctx , c .writeTimeout )
84+ defer cancel ()
85+ if err := c .writeStatus (writeCtx , reports ); err != nil {
86+ klog .ErrorS (err , "failed to publish kms plugin health" )
87+ }
15588 }, c .interval , 0.1 , false )
15689
15790 return nil
@@ -168,7 +101,7 @@ func buildPlugins(ctx context.Context, sockets []string, timeout time.Duration)
168101
169102 // Unique name per plugin so the gRPC client's KMS operation metrics
170103 // don't merge both plugins into one series.
171- service , err := k8senvelopekmsv2 .NewGRPCService (ctx , socket , Subcommand + "-" + keyID , timeout )
104+ service , err := k8senvelopekmsv2 .NewGRPCService (ctx , socket , Subcommand , timeout )
172105 if err != nil {
173106 // With the current dependency version this should never happen with a validated GRPC endpoint.
174107 return nil , fmt .Errorf ("setting up grpc service failed at %q: %w" , socket , err )
0 commit comments