Skip to content

Commit 360c4bc

Browse files
authored
Introduce quick gather command (#371)
* Introduce quick gather command * Adds docs, and some fixes
1 parent a39f0b5 commit 360c4bc

File tree

5 files changed

+291
-95
lines changed

5 files changed

+291
-95
lines changed

Diff for: cmd/insights-operator/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func NewOperatorCommand() *cobra.Command {
5757

5858
cmd.AddCommand(start.NewOperator())
5959
cmd.AddCommand(start.NewReceiver())
60+
cmd.AddCommand(start.NewGather())
6061

6162
return cmd
6263
}

Diff for: pkg/cmd/start/start.go

+122-49
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ import (
99

1010
"github.com/openshift/library-go/pkg/controller/controllercmd"
1111
"github.com/openshift/library-go/pkg/serviceability"
12+
1213
"github.com/spf13/cobra"
1314
"k8s.io/client-go/pkg/version"
15+
"k8s.io/client-go/rest"
16+
"k8s.io/client-go/tools/clientcmd"
1417
"k8s.io/klog/v2"
1518

1619
"github.com/openshift/insights-operator/pkg/config"
@@ -19,8 +22,9 @@ import (
1922

2023
const serviceCACertPath = "/var/run/configmaps/service-ca-bundle/service-ca.crt"
2124

25+
// Create the commad for running the Insights Operator.
2226
func NewOperator() *cobra.Command {
23-
operator := &controller.Support{
27+
operator := &controller.Operator{
2428
Controller: config.Controller{
2529
StoragePath: "/var/lib/insights-operator",
2630
Interval: 10 * time.Minute,
@@ -35,59 +39,128 @@ func NewOperator() *cobra.Command {
3539
cmd := &cobra.Command{
3640
Use: "start",
3741
Short: "Start the operator",
38-
Run: func(cmd *cobra.Command, args []string) {
39-
// boiler plate for the "normal" command
40-
rand.Seed(time.Now().UTC().UnixNano())
41-
defer serviceability.BehaviorOnPanic(os.Getenv("OPENSHIFT_ON_PANIC"), version.Get())()
42-
defer serviceability.Profile(os.Getenv("OPENSHIFT_PROFILE")).Stop()
43-
serviceability.StartProfiler()
44-
45-
if config := cmd.Flags().Lookup("config").Value.String(); len(config) == 0 {
46-
klog.Fatalf("error: --config is required")
47-
}
48-
49-
unstructured, config, configBytes, err := cfg.Config()
50-
if err != nil {
51-
klog.Fatal(err)
52-
}
42+
Run: runOperator(operator, cfg),
43+
}
44+
cmd.Flags().AddFlagSet(cfg.NewCommand().Flags())
5345

54-
startingFileContent, observedFiles, err := cfg.AddDefaultRotationToConfig(config, configBytes)
55-
if err != nil {
56-
klog.Fatal(err)
57-
}
46+
return cmd
47+
}
5848

59-
// if the service CA is rotated, we want to restart
60-
if data, err := ioutil.ReadFile(serviceCACertPath); err == nil {
61-
startingFileContent[serviceCACertPath] = data
62-
} else {
63-
klog.V(4).Infof("Unable to read service ca bundle: %v", err)
64-
}
65-
observedFiles = append(observedFiles, serviceCACertPath)
66-
67-
exitOnChangeReactorCh := make(chan struct{})
68-
ctx := context.Background()
69-
ctx2, cancel := context.WithCancel(ctx)
70-
go func() {
71-
select {
72-
case <-exitOnChangeReactorCh:
73-
cancel()
74-
case <-ctx.Done():
75-
cancel()
76-
}
77-
}()
78-
79-
builder := controllercmd.NewController("openshift-insights-operator", operator.Run).
80-
WithKubeConfigFile(cmd.Flags().Lookup("kubeconfig").Value.String(), nil).
81-
WithLeaderElection(config.LeaderElection, "", "openshift-insights-operator-lock").
82-
WithServer(config.ServingInfo, config.Authentication, config.Authorization).
83-
WithRestartOnChange(exitOnChangeReactorCh, startingFileContent, observedFiles...)
84-
85-
if err := builder.Run(ctx2, unstructured); err != nil {
86-
klog.Fatal(err)
87-
}
49+
// Create the commad for running the a single gather.
50+
func NewGather() *cobra.Command {
51+
operator := &controller.GatherJob{
52+
Controller: config.Controller{
53+
StoragePath: "/var/lib/insights-operator",
54+
Interval: 30 * time.Minute,
8855
},
8956
}
57+
cfg := controllercmd.NewControllerCommandConfig("openshift-insights-operator", version.Get(), nil)
58+
cmd := &cobra.Command{
59+
Use: "gather",
60+
Short: "Does a single gather, without uploading it",
61+
Run: runGather(*operator, cfg),
62+
}
9063
cmd.Flags().AddFlagSet(cfg.NewCommand().Flags())
9164

9265
return cmd
9366
}
67+
68+
// Starts a single gather, main responsibility is loading in the necessary configs.
69+
func runGather(operator controller.GatherJob, cfg *controllercmd.ControllerCommandConfig) func(cmd *cobra.Command, args []string) {
70+
return func(cmd *cobra.Command, args []string) {
71+
if config := cmd.Flags().Lookup("config").Value.String(); len(config) == 0 {
72+
klog.Fatalf("error: --config is required")
73+
}
74+
unstructured, _, _, err := cfg.Config()
75+
if err != nil {
76+
klog.Fatal(err)
77+
}
78+
cont, err := config.LoadConfig(operator.Controller, unstructured.Object, config.ToDisconnectedController)
79+
if err != nil {
80+
klog.Fatal(err)
81+
}
82+
operator.Controller = cont
83+
84+
kubeConfigPath := cmd.Flags().Lookup("kubeconfig").Value.String()
85+
if len(kubeConfigPath) == 0 {
86+
klog.Fatalf("error: --kubeconfig is required")
87+
}
88+
kubeConfigBytes, err := ioutil.ReadFile(kubeConfigPath)
89+
if err != nil {
90+
klog.Fatal(err)
91+
}
92+
kubeConfig, err := clientcmd.NewClientConfigFromBytes(kubeConfigBytes)
93+
if err != nil {
94+
klog.Fatal(err)
95+
}
96+
clientConfig, err := kubeConfig.ClientConfig()
97+
if err != nil {
98+
klog.Fatal(err)
99+
}
100+
protoConfig := rest.CopyConfig(clientConfig)
101+
protoConfig.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
102+
protoConfig.ContentType = "application/vnd.kubernetes.protobuf"
103+
104+
ctx, cancel := context.WithTimeout(context.Background(), operator.Interval)
105+
err = operator.Gather(ctx, clientConfig, protoConfig)
106+
if err != nil {
107+
klog.Fatal(err)
108+
}
109+
cancel()
110+
os.Exit(0)
111+
}
112+
}
113+
114+
// Boilerplate for running an operator and handling command line arguments.
115+
func runOperator(operator *controller.Operator, cfg *controllercmd.ControllerCommandConfig) func(cmd *cobra.Command, args []string) {
116+
return func(cmd *cobra.Command, args []string) {
117+
// boiler plate for the "normal" command
118+
rand.Seed(time.Now().UTC().UnixNano())
119+
defer serviceability.BehaviorOnPanic(os.Getenv("OPENSHIFT_ON_PANIC"), version.Get())()
120+
defer serviceability.Profile(os.Getenv("OPENSHIFT_PROFILE")).Stop()
121+
serviceability.StartProfiler()
122+
123+
if config := cmd.Flags().Lookup("config").Value.String(); len(config) == 0 {
124+
klog.Fatalf("error: --config is required")
125+
}
126+
127+
unstructured, config, configBytes, err := cfg.Config()
128+
if err != nil {
129+
klog.Fatal(err)
130+
}
131+
132+
startingFileContent, observedFiles, err := cfg.AddDefaultRotationToConfig(config, configBytes)
133+
if err != nil {
134+
klog.Fatal(err)
135+
}
136+
137+
// if the service CA is rotated, we want to restart
138+
if data, err := ioutil.ReadFile(serviceCACertPath); err == nil {
139+
startingFileContent[serviceCACertPath] = data
140+
} else {
141+
klog.V(4).Infof("Unable to read service ca bundle: %v", err)
142+
}
143+
observedFiles = append(observedFiles, serviceCACertPath)
144+
145+
exitOnChangeReactorCh := make(chan struct{})
146+
ctx := context.Background()
147+
ctx2, cancel := context.WithCancel(ctx)
148+
go func() {
149+
select {
150+
case <-exitOnChangeReactorCh:
151+
cancel()
152+
case <-ctx.Done():
153+
cancel()
154+
}
155+
}()
156+
157+
builder := controllercmd.NewController("openshift-insights-operator", operator.Run).
158+
WithKubeConfigFile(cmd.Flags().Lookup("kubeconfig").Value.String(), nil).
159+
WithLeaderElection(config.LeaderElection, "", "openshift-insights-operator-lock").
160+
WithServer(config.ServingInfo, config.Authentication, config.Authorization).
161+
WithRestartOnChange(exitOnChangeReactorCh, startingFileContent, observedFiles...)
162+
if err := builder.Run(ctx2, unstructured); err != nil {
163+
klog.Fatal(err)
164+
}
165+
}
166+
}

Diff for: pkg/config/config.go

+71-22
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package config
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"time"
7+
8+
"k8s.io/apimachinery/pkg/runtime"
9+
"k8s.io/klog/v2"
610
)
711

812
// Serialized defines the standard config for this operator.
@@ -21,7 +25,36 @@ type Serialized struct {
2125
Gather []string `json:"gather"`
2226
}
2327

24-
func (s *Serialized) ToController(cfg *Controller) (*Controller, error) {
28+
// Controller defines the standard config for this operator.
29+
type Controller struct {
30+
Report bool
31+
StoragePath string
32+
Interval time.Duration
33+
Endpoint string
34+
ReportEndpoint string
35+
ReportPullingDelay time.Duration
36+
ReportMinRetryTime time.Duration
37+
ReportPullingTimeout time.Duration
38+
Impersonate string
39+
Gather []string
40+
41+
Username string
42+
Password string
43+
Token string
44+
45+
HTTPConfig HTTPConfig
46+
}
47+
48+
// HTTPConfig configures http proxy and exception settings if they come from config
49+
type HTTPConfig struct {
50+
HTTPProxy string
51+
HTTPSProxy string
52+
NoProxy string
53+
}
54+
55+
type Converter func(s *Serialized, cfg *Controller) (*Controller, error)
56+
57+
func ToController(s *Serialized, cfg *Controller) (*Controller, error) {
2558
if cfg == nil {
2659
cfg = &Controller{}
2760
}
@@ -89,29 +122,45 @@ func (s *Serialized) ToController(cfg *Controller) (*Controller, error) {
89122
return cfg, nil
90123
}
91124

92-
// Controller defines the standard config for this operator.
93-
type Controller struct {
94-
Report bool
95-
StoragePath string
96-
Interval time.Duration
97-
Endpoint string
98-
ReportEndpoint string
99-
ReportPullingDelay time.Duration
100-
ReportMinRetryTime time.Duration
101-
ReportPullingTimeout time.Duration
102-
Impersonate string
103-
Gather []string
125+
func ToDisconnectedController(s *Serialized, cfg *Controller) (*Controller, error) {
126+
if cfg == nil {
127+
cfg = &Controller{}
128+
}
129+
cfg.Report = s.Report
130+
cfg.StoragePath = s.StoragePath
131+
cfg.Impersonate = s.Impersonate
132+
cfg.Gather = s.Gather
104133

105-
Username string
106-
Password string
107-
Token string
134+
if len(s.Interval) > 0 {
135+
d, err := time.ParseDuration(s.Interval)
136+
if err != nil {
137+
return nil, fmt.Errorf("interval must be a valid duration: %v", err)
138+
}
139+
cfg.Interval = d
140+
}
108141

109-
HTTPConfig HTTPConfig
142+
if cfg.Interval <= 0 {
143+
return nil, fmt.Errorf("interval must be a non-negative duration")
144+
}
145+
146+
if len(cfg.StoragePath) == 0 {
147+
return nil, fmt.Errorf("storagePath must point to a directory where snapshots can be stored")
148+
}
149+
return cfg, nil
110150
}
111151

112-
// HTTPConfig configures http proxy and exception settings if they come from config
113-
type HTTPConfig struct {
114-
HTTPProxy string
115-
HTTPSProxy string
116-
NoProxy string
152+
// LoadConfig unmarshalls config from obj and loads it to this Controller struct
153+
func LoadConfig(controller Controller, obj map[string]interface{}, converter Converter) (Controller, error) {
154+
var cfg Serialized
155+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj, &cfg); err != nil {
156+
return controller, fmt.Errorf("unable to load config: %v", err)
157+
}
158+
159+
loadedController, err := converter(&cfg, &controller)
160+
if err != nil {
161+
return controller, err
162+
}
163+
data, _ := json.Marshal(cfg)
164+
klog.V(2).Infof("Current config: %s", string(data))
165+
return *loadedController, nil
117166
}

0 commit comments

Comments
 (0)