mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-02-14 18:29:53 +00:00
Merge pull request #3 from replicatedhq/preflight
Preflights and preflightjobs
This commit is contained in:
14
Makefile
14
Makefile
@@ -79,9 +79,17 @@ snapshot-release:
|
||||
|
||||
.PHONY: local-release
|
||||
local-release: snapshot-release
|
||||
docker tag replicatedhq/troubleshoot:alpha localhost:32000/troubleshoot:alpha
|
||||
docker tag replicatedhq/preflight:alpha localhost:32000/preflight:alpha
|
||||
docker tag replicatedhq/troubleshoot-manager:alpha localhost:32000/troubleshoot-manager:alpha
|
||||
docker tag replicated/troubleshoot:alpha localhost:32000/troubleshoot:alpha
|
||||
docker tag replicated/preflight:alpha localhost:32000/preflight:alpha
|
||||
docker tag replicated/troubleshoot-manager:alpha localhost:32000/troubleshoot-manager:alpha
|
||||
docker push localhost:32000/troubleshoot:alpha
|
||||
docker push localhost:32000/preflight:alpha
|
||||
docker push localhost:32000/troubleshoot-manager:alpha
|
||||
|
||||
.PHONY: run-preflight
|
||||
run-preflight: preflight
|
||||
./bin/preflight run \
|
||||
--collector-image=localhost:32000/troubleshoot:alpha \
|
||||
--collector-pullpolicy=Always \
|
||||
--image=localhost:32000/troubleshoot:alpha \
|
||||
--pullpolicy=Always
|
||||
|
||||
@@ -13,16 +13,17 @@ import (
|
||||
|
||||
func Server() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "run the http server",
|
||||
Long: `...`,
|
||||
Use: "server",
|
||||
Short: "run the http server",
|
||||
Hidden: true,
|
||||
Long: `...`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlag("port", cmd.Flags().Lookup("port"))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
server.Serve(context.Background(), fmt.Sprintf(":%d", v.GetInt("port")))
|
||||
server.ServeCollector(context.Background(), fmt.Sprintf(":%d", v.GetInt("port")))
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
|
||||
67
cmd/preflight/cli/receive.go
Normal file
67
cmd/preflight/cli/receive.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func receivePreflightResults(preflightJobNamespace string, preflightJobName string) error {
|
||||
// poll until there are no more "running" collectors
|
||||
troubleshootClient, err := createTroubleshootK8sClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bundlePath, err := ioutil.TempDir("", "troubleshoot")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(bundlePath)
|
||||
|
||||
receivedPreflights := []string{}
|
||||
for {
|
||||
job, err := troubleshootClient.PreflightJobs(preflightJobNamespace).Get(preflightJobName, metav1.GetOptions{})
|
||||
if err != nil && kuberneteserrors.IsNotFound(err) {
|
||||
// where did it go!
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, readyPreflight := range job.Status.AnalyzersSuccessful {
|
||||
alreadyReceived := false
|
||||
for _, receivedPreflight := range receivedPreflights {
|
||||
if receivedPreflight == readyPreflight {
|
||||
alreadyReceived = true
|
||||
}
|
||||
}
|
||||
|
||||
if alreadyReceived {
|
||||
continue
|
||||
}
|
||||
|
||||
preflightResp, err := http.Get(fmt.Sprintf("http://localhost:8000/preflight/%s", readyPreflight))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer preflightResp.Body.Close()
|
||||
body, err := ioutil.ReadAll(preflightResp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", body)
|
||||
receivedPreflights = append(receivedPreflights, readyPreflight)
|
||||
}
|
||||
|
||||
if len(job.Status.AnalyzersRunning) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,18 @@ import (
|
||||
|
||||
func RootCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "troubleshoot",
|
||||
Short: "Generate and manage support bundles",
|
||||
Long: `A support bundle is an archive of files, output, metrics and state
|
||||
from a server that can be used to assist when troubleshooting a server.`,
|
||||
Use: "preflight",
|
||||
Short: "Run and retrieve preflight checks in a cluster",
|
||||
Long: `A preflight check is a set of validations that can and should be run to ensure
|
||||
that a cluster meets the requirements to run an application.`,
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
cmd.AddCommand(Run())
|
||||
cmd.AddCommand(Server())
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
return cmd
|
||||
}
|
||||
@@ -32,6 +35,6 @@ func InitAndExecute() {
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
viper.SetEnvPrefix("TROUBLESHOOT")
|
||||
viper.SetEnvPrefix("PREFLIGHT")
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
|
||||
139
cmd/preflight/cli/run.go
Normal file
139
cmd/preflight/cli/run.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func Run() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "run a set of preflight checks in a cluster",
|
||||
Long: `run preflight checks and return the results`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
troubleshootClient, err := createTroubleshootK8sClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preflightName := v.GetString("preflight")
|
||||
if preflightName == "" {
|
||||
preflights, err := troubleshootClient.Preflights(v.GetString("namespace")).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(preflights.Items) == 1 {
|
||||
preflightName = preflights.Items[0].Name
|
||||
}
|
||||
}
|
||||
|
||||
if preflightName == "" {
|
||||
return errors.New("unable to fly, try using the --preflight flags")
|
||||
}
|
||||
|
||||
// generate a unique name
|
||||
now := time.Now()
|
||||
suffix := fmt.Sprintf("%d", now.Unix())
|
||||
|
||||
preflightJobName := fmt.Sprintf("%s-job-%s", preflightName, suffix[len(suffix)-4:])
|
||||
preflightJob := troubleshootv1beta1.PreflightJob{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: preflightJobName,
|
||||
Namespace: v.GetString("namespace"),
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "preflightjob.troubleshoot.replicated.com",
|
||||
},
|
||||
Spec: troubleshootv1beta1.PreflightJobSpec{
|
||||
Preflight: troubleshootv1beta1.PreflightRef{
|
||||
Name: preflightName,
|
||||
Namespace: v.GetString("namespace"),
|
||||
},
|
||||
Image: v.GetString("image"),
|
||||
ImagePullPolicy: v.GetString("pullpolicy"),
|
||||
CollectorImage: v.GetString("collector-image"),
|
||||
CollectorImagePullPolicy: v.GetString("collector-pullpolicy"),
|
||||
},
|
||||
}
|
||||
if _, err := troubleshootClient.PreflightJobs(v.GetString("namespace")).Create(&preflightJob); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Poll the status of the Custom Resource for it to include a callback
|
||||
var found *troubleshootv1beta1.PreflightJob
|
||||
start := time.Now()
|
||||
for {
|
||||
current, err := troubleshootClient.PreflightJobs(v.GetString("namespace")).Get(preflightJobName, metav1.GetOptions{})
|
||||
if err != nil && kuberneteserrors.IsNotFound(err) {
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if current.Status.IsServerReady {
|
||||
found = current
|
||||
break
|
||||
}
|
||||
|
||||
if time.Now().Sub(start) > time.Duration(time.Second*10) {
|
||||
return errors.New("preflightjob failed to start")
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
|
||||
// Connect to the callback
|
||||
stopChan, err := k8sutil.PortForward(v.GetString("kubecontext"), 8000, 8000, found.Status.ServerPodNamespace, found.Status.ServerPodName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := receivePreflightResults(found.Namespace, found.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write
|
||||
|
||||
close(stopChan)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().String("preflight", "", "name of the preflight to use")
|
||||
cmd.Flags().String("namespace", "default", "namespace the preflight can be found in")
|
||||
|
||||
cmd.Flags().String("kubecontext", filepath.Join(homeDir(), ".kube", "config"), "the kubecontext to use when connecting")
|
||||
|
||||
cmd.Flags().String("image", "", "the full name of the preflight image to use")
|
||||
cmd.Flags().String("pullpolicy", "", "the pull policy of the preflight image")
|
||||
cmd.Flags().String("collector-image", "", "the full name of the collector image to use")
|
||||
cmd.Flags().String("collector-pullpolicy", "", "the pull policy of the collector image")
|
||||
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func homeDir() string {
|
||||
if h := os.Getenv("HOME"); h != "" {
|
||||
return h
|
||||
}
|
||||
return os.Getenv("USERPROFILE") // windows
|
||||
}
|
||||
43
cmd/preflight/cli/server.go
Normal file
43
cmd/preflight/cli/server.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/replicatedhq/troubleshoot/pkg/server"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func Server() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "run the http server",
|
||||
Hidden: true,
|
||||
Long: `...`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
viper.BindPFlag("port", cmd.Flags().Lookup("port"))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
|
||||
server.ServePreflight(context.Background(), fmt.Sprintf(":%d", v.GetInt("port")))
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
return nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Int("port", 8000, "port to listen on")
|
||||
|
||||
viper.BindPFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
22
cmd/preflight/cli/troubleshoot.go
Normal file
22
cmd/preflight/cli/troubleshoot.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
func createTroubleshootK8sClient() (*troubleshootclientv1beta1.TroubleshootV1beta1Client, error) {
|
||||
v := viper.GetViper()
|
||||
|
||||
config, err := clientcmd.BuildConfigFromFlags("", v.GetString("kubecontext"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return troubleshootClient, nil
|
||||
}
|
||||
@@ -390,12 +390,81 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
collectorImage:
|
||||
type: string
|
||||
collectorImagePullPolicy:
|
||||
type: string
|
||||
imagePullPolicy:
|
||||
type: string
|
||||
preflight:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
preflightImage:
|
||||
type: string
|
||||
required:
|
||||
- preflight
|
||||
type: object
|
||||
status:
|
||||
properties:
|
||||
analyzersFailed:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
analyzersRunning:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
analyzersSuccessful:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
collectorsFailed:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
collectorsRunning:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
collectorsSuccessful:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
isAnalyzersComplete:
|
||||
type: boolean
|
||||
isServerReady:
|
||||
type: boolean
|
||||
serverPodName:
|
||||
type: string
|
||||
serverPodNamespace:
|
||||
type: string
|
||||
serverPodPort:
|
||||
type: integer
|
||||
required:
|
||||
- isServerReady
|
||||
- serverPodName
|
||||
- serverPodNamespace
|
||||
- serverPodPort
|
||||
- collectorsRunning
|
||||
- collectorsSuccessful
|
||||
- collectorsFailed
|
||||
- isAnalyzersComplete
|
||||
- analyzersRunning
|
||||
- analyzersSuccessful
|
||||
- analyzersFailed
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
|
||||
@@ -390,6 +390,98 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
analyzers:
|
||||
items:
|
||||
properties:
|
||||
clusterVersion:
|
||||
properties:
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
type: object
|
||||
storageClass:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
outcomes:
|
||||
items:
|
||||
properties:
|
||||
fail:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
pass:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
warn:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
when:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- outcomes
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
collectors:
|
||||
items:
|
||||
properties:
|
||||
cluster-info:
|
||||
type: object
|
||||
cluster-resources:
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
status:
|
||||
type: object
|
||||
|
||||
@@ -8,6 +8,31 @@ import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Analyze) DeepCopyInto(out *Analyze) {
|
||||
*out = *in
|
||||
if in.ClusterVersion != nil {
|
||||
in, out := &in.ClusterVersion, &out.ClusterVersion
|
||||
*out = new(ClusterVersion)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StorageClass != nil {
|
||||
in, out := &in.StorageClass, &out.StorageClass
|
||||
*out = new(StorageClass)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze.
|
||||
func (in *Analyze) DeepCopy() *Analyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Analyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Analyzer) DeepCopyInto(out *Analyzer) {
|
||||
*out = *in
|
||||
@@ -216,6 +241,32 @@ func (in *ClusterResources) DeepCopy() *ClusterResources {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterVersion) DeepCopyInto(out *ClusterVersion) {
|
||||
*out = *in
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVersion.
|
||||
func (in *ClusterVersion) DeepCopy() *ClusterVersion {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterVersion)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Collect) DeepCopyInto(out *Collect) {
|
||||
*out = *in
|
||||
@@ -445,12 +496,42 @@ func (in *CollectorStatus) DeepCopy() *CollectorStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Outcome) DeepCopyInto(out *Outcome) {
|
||||
*out = *in
|
||||
if in.Fail != nil {
|
||||
in, out := &in.Fail, &out.Fail
|
||||
*out = new(SingleOutcome)
|
||||
**out = **in
|
||||
}
|
||||
if in.Warn != nil {
|
||||
in, out := &in.Warn, &out.Warn
|
||||
*out = new(SingleOutcome)
|
||||
**out = **in
|
||||
}
|
||||
if in.Pass != nil {
|
||||
in, out := &in.Pass, &out.Pass
|
||||
*out = new(SingleOutcome)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Outcome.
|
||||
func (in *Outcome) DeepCopy() *Outcome {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Outcome)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Preflight) DeepCopyInto(out *Preflight) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
@@ -478,7 +559,7 @@ func (in *PreflightJob) DeepCopyInto(out *PreflightJob) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
out.Status = in.Status
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightJob.
|
||||
@@ -534,6 +615,7 @@ func (in *PreflightJobList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PreflightJobSpec) DeepCopyInto(out *PreflightJobSpec) {
|
||||
*out = *in
|
||||
out.Preflight = in.Preflight
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightJobSpec.
|
||||
@@ -549,6 +631,36 @@ func (in *PreflightJobSpec) DeepCopy() *PreflightJobSpec {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PreflightJobStatus) DeepCopyInto(out *PreflightJobStatus) {
|
||||
*out = *in
|
||||
if in.CollectorsRunning != nil {
|
||||
in, out := &in.CollectorsRunning, &out.CollectorsRunning
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.CollectorsSuccessful != nil {
|
||||
in, out := &in.CollectorsSuccessful, &out.CollectorsSuccessful
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.CollectorsFailed != nil {
|
||||
in, out := &in.CollectorsFailed, &out.CollectorsFailed
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AnalyzersRunning != nil {
|
||||
in, out := &in.AnalyzersRunning, &out.AnalyzersRunning
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AnalyzersSuccessful != nil {
|
||||
in, out := &in.AnalyzersSuccessful, &out.AnalyzersSuccessful
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AnalyzersFailed != nil {
|
||||
in, out := &in.AnalyzersFailed, &out.AnalyzersFailed
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightJobStatus.
|
||||
@@ -593,9 +705,46 @@ func (in *PreflightList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PreflightRef) DeepCopyInto(out *PreflightRef) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightRef.
|
||||
func (in *PreflightRef) DeepCopy() *PreflightRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PreflightRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PreflightSpec) DeepCopyInto(out *PreflightSpec) {
|
||||
*out = *in
|
||||
if in.Collectors != nil {
|
||||
in, out := &in.Collectors, &out.Collectors
|
||||
*out = make([]*Collect, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Collect)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.Analyzers != nil {
|
||||
in, out := &in.Analyzers, &out.Analyzers
|
||||
*out = make([]*Analyze, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Analyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightSpec.
|
||||
@@ -622,3 +771,44 @@ func (in *PreflightStatus) DeepCopy() *PreflightStatus {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SingleOutcome) DeepCopyInto(out *SingleOutcome) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleOutcome.
|
||||
func (in *SingleOutcome) DeepCopy() *SingleOutcome {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SingleOutcome)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StorageClass) DeepCopyInto(out *StorageClass) {
|
||||
*out = *in
|
||||
if in.Outcome != nil {
|
||||
in, out := &in.Outcome, &out.Outcome
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageClass.
|
||||
func (in *StorageClass) DeepCopy() *StorageClass {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StorageClass)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,9 +1,51 @@
|
||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
||||
kind: Preflight
|
||||
metadata:
|
||||
labels:
|
||||
controller-tools.k8s.io: "1.0"
|
||||
name: preflight-sample
|
||||
spec:
|
||||
# Add fields here
|
||||
foo: bar
|
||||
analyzers:
|
||||
- clusterVersion:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "< 1.13.0"
|
||||
message: You need more kubernetes
|
||||
- warn:
|
||||
when: "< 1.15.0"
|
||||
message: You have barely enough kubernetes
|
||||
- pass:
|
||||
message: Good job keeping k8s current
|
||||
# - storageClass:
|
||||
# name: "my-custom-storage-class"
|
||||
# fail:
|
||||
# message: The custom storage class thing was not found
|
||||
# pass:
|
||||
# message: All good on storage classes
|
||||
# - manifests:
|
||||
# - secret:
|
||||
# namespace: default
|
||||
# name: shhh
|
||||
# key: top-secret
|
||||
# fail:
|
||||
# message: The top secret secret is missing
|
||||
# pass:
|
||||
# message: You know the secret
|
||||
# - ingress:
|
||||
# namespace: default
|
||||
# name: connect-to-me
|
||||
# fail:
|
||||
# message: The ingress isn't ingressing
|
||||
# pass:
|
||||
# message: All systems ok on ingress
|
||||
# - imagePullSecret:
|
||||
# name: replicated
|
||||
# namespace: my-app
|
||||
# fail:
|
||||
# message: Can't pull the images
|
||||
# pass:
|
||||
# message: Connected to docker registry
|
||||
# - customResourceDefinitions:
|
||||
# name: rook
|
||||
# fail:
|
||||
# message: You don't have rook installed
|
||||
# pass:
|
||||
# message: Found rook!
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
||||
kind: PreflightJob
|
||||
metadata:
|
||||
labels:
|
||||
controller-tools.k8s.io: "1.0"
|
||||
name: preflightjob-sample
|
||||
spec:
|
||||
# Add fields here
|
||||
foo: bar
|
||||
image: localhost:32000/troubleshoot:alpha
|
||||
imagePullPolicy: Always
|
||||
preflight:
|
||||
name: preflight-sample
|
||||
namespace: default
|
||||
|
||||
@@ -84,19 +84,21 @@ archives:
|
||||
dockers:
|
||||
- dockerfile: ./deploy/Dockerfile.troubleshoot
|
||||
image_templates:
|
||||
- "replicatedhq/troubleshoot:alpha"
|
||||
- "replicated/troubleshoot:alpha"
|
||||
binaries:
|
||||
- collector
|
||||
- troubleshoot
|
||||
- dockerfile: ./deploy/Dockerfile.preflight
|
||||
- preflight
|
||||
- dockerfile: ./deploy/Dockerfile.troubleshoot
|
||||
image_templates:
|
||||
- "replicatedhq/preflight:alpha"
|
||||
- "replicated/preflight:alpha"
|
||||
binaries:
|
||||
- collector
|
||||
- troubleshoot
|
||||
- preflight
|
||||
- dockerfile: ./deploy/Dockerfile.manager
|
||||
image_templates:
|
||||
- "replicatedhq/troubleshoot-manager:alpha"
|
||||
- "replicated/troubleshoot-manager:alpha"
|
||||
binaries:
|
||||
- manager
|
||||
snapshot:
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
FROM debian:buster
|
||||
WORKDIR /
|
||||
|
||||
RUN apt-get -qq update \
|
||||
&& apt-get -qq -y install \
|
||||
ca-certificates
|
||||
|
||||
COPY preflight /preflight/preflight
|
||||
COPY collector /preflight/collector
|
||||
|
||||
ENV PATH="/preflight:${PATH}"
|
||||
|
||||
@@ -7,6 +7,7 @@ RUN apt-get -qq update \
|
||||
|
||||
COPY troubleshoot /troubleshoot/troubleshoot
|
||||
COPY collector /troubleshoot/collector
|
||||
COPY preflight /troubleshoot/preflight
|
||||
|
||||
ENV PATH="/troubleshoot:${PATH}"
|
||||
|
||||
|
||||
27
pkg/apis/troubleshoot/v1beta1/analyzer_shared.go
Normal file
27
pkg/apis/troubleshoot/v1beta1/analyzer_shared.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package v1beta1
|
||||
|
||||
type SingleOutcome struct {
|
||||
When string `json:"when,omitempty" yaml:"when,omitempty"`
|
||||
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||
URI string `json:"uri,omitempty" yaml:"uri,omitempty"`
|
||||
}
|
||||
|
||||
type Outcome struct {
|
||||
Fail *SingleOutcome `json:"fail,omitempty" yaml:"fail,omitempty"`
|
||||
Warn *SingleOutcome `json:"warn,omitempty" yaml:"warn,omitempty"`
|
||||
Pass *SingleOutcome `json:"pass,omitempty" yaml:"pass,omitempty"`
|
||||
}
|
||||
|
||||
type ClusterVersion struct {
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type StorageClass struct {
|
||||
Outcome []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
}
|
||||
|
||||
type Analyze struct {
|
||||
ClusterVersion *ClusterVersion `json:"clusterVersion,omitempty" yaml:"clusterVersion,omitempty"`
|
||||
StorageClass *StorageClass `json:"storageClass,omitempty" yaml:"supportBundle,omitempty"`
|
||||
}
|
||||
55
pkg/apis/troubleshoot/v1beta1/analyzer_shared_test.go
Normal file
55
pkg/apis/troubleshoot/v1beta1/analyzer_shared_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestAnalyze_Unmarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
spec string
|
||||
expectObject Analyze
|
||||
}{
|
||||
{
|
||||
name: "clusterVersion",
|
||||
spec: `clusterVersion:
|
||||
outcomes:
|
||||
- fail:
|
||||
message: failed
|
||||
- pass:
|
||||
message: passed`,
|
||||
expectObject: Analyze{
|
||||
ClusterVersion: &ClusterVersion{
|
||||
Outcomes: []*Outcome{
|
||||
&Outcome{
|
||||
Fail: &SingleOutcome{
|
||||
Message: "failed",
|
||||
},
|
||||
},
|
||||
&Outcome{
|
||||
Pass: &SingleOutcome{
|
||||
Message: "passed",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
a := Analyze{}
|
||||
err := yaml.Unmarshal([]byte(test.spec), &a)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, test.expectObject, a)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,10 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// PreflightSpec defines the desired state of Preflight
|
||||
type PreflightSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
Collectors []*Collect `json:"collectors,omitempty"`
|
||||
Analyzers []*Analyze `json:"analyzers,omitempty"`
|
||||
}
|
||||
|
||||
// PreflightStatus defines the observed state of Preflight
|
||||
|
||||
@@ -20,26 +20,43 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
type PreflightRef struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// PreflightJobSpec defines the desired state of PreflightJob
|
||||
type PreflightJobSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
Preflight PreflightRef `json:"preflight"`
|
||||
|
||||
Image string `json:"preflightImage,omitempty"`
|
||||
ImagePullPolicy string `json:"imagePullPolicy,omitempty"`
|
||||
CollectorImage string `json:"collectorImage,omitempty"`
|
||||
CollectorImagePullPolicy string `json:"collectorImagePullPolicy,omitempty"`
|
||||
}
|
||||
|
||||
// PreflightJobStatus defines the observed state of PreflightJob
|
||||
type PreflightJobStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
IsServerReady bool `json:"isServerReady"`
|
||||
ServerPodName string `json:"serverPodName"`
|
||||
ServerPodNamespace string `json:"serverPodNamespace"`
|
||||
ServerPodPort int `json:"serverPodPort"`
|
||||
|
||||
CollectorsRunning []string `json:"collectorsRunning"`
|
||||
CollectorsSuccessful []string `json:"collectorsSuccessful"`
|
||||
CollectorsFailed []string `json:"collectorsFailed"`
|
||||
|
||||
IsAnalyzersComplete bool `json:"isAnalyzersComplete"`
|
||||
AnalyzersRunning []string `json:"analyzersRunning"`
|
||||
AnalyzersSuccessful []string `json:"analyzersSuccessful"`
|
||||
AnalyzersFailed []string `json:"analyzersFailed"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// PreflightJob is the Schema for the preflightjobs API
|
||||
// +k8s:openapi-gen=true
|
||||
// +kubebuilder:subresource:status
|
||||
type PreflightJob struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
@@ -24,6 +24,31 @@ import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Analyze) DeepCopyInto(out *Analyze) {
|
||||
*out = *in
|
||||
if in.ClusterVersion != nil {
|
||||
in, out := &in.ClusterVersion, &out.ClusterVersion
|
||||
*out = new(ClusterVersion)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StorageClass != nil {
|
||||
in, out := &in.StorageClass, &out.StorageClass
|
||||
*out = new(StorageClass)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Analyze.
|
||||
func (in *Analyze) DeepCopy() *Analyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Analyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Analyzer) DeepCopyInto(out *Analyzer) {
|
||||
*out = *in
|
||||
@@ -232,6 +257,32 @@ func (in *ClusterResources) DeepCopy() *ClusterResources {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterVersion) DeepCopyInto(out *ClusterVersion) {
|
||||
*out = *in
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterVersion.
|
||||
func (in *ClusterVersion) DeepCopy() *ClusterVersion {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterVersion)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Collect) DeepCopyInto(out *Collect) {
|
||||
*out = *in
|
||||
@@ -461,12 +512,42 @@ func (in *CollectorStatus) DeepCopy() *CollectorStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Outcome) DeepCopyInto(out *Outcome) {
|
||||
*out = *in
|
||||
if in.Fail != nil {
|
||||
in, out := &in.Fail, &out.Fail
|
||||
*out = new(SingleOutcome)
|
||||
**out = **in
|
||||
}
|
||||
if in.Warn != nil {
|
||||
in, out := &in.Warn, &out.Warn
|
||||
*out = new(SingleOutcome)
|
||||
**out = **in
|
||||
}
|
||||
if in.Pass != nil {
|
||||
in, out := &in.Pass, &out.Pass
|
||||
*out = new(SingleOutcome)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Outcome.
|
||||
func (in *Outcome) DeepCopy() *Outcome {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Outcome)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Preflight) DeepCopyInto(out *Preflight) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
@@ -494,7 +575,7 @@ func (in *PreflightJob) DeepCopyInto(out *PreflightJob) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
out.Status = in.Status
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightJob.
|
||||
@@ -550,6 +631,7 @@ func (in *PreflightJobList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PreflightJobSpec) DeepCopyInto(out *PreflightJobSpec) {
|
||||
*out = *in
|
||||
out.Preflight = in.Preflight
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightJobSpec.
|
||||
@@ -565,6 +647,36 @@ func (in *PreflightJobSpec) DeepCopy() *PreflightJobSpec {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PreflightJobStatus) DeepCopyInto(out *PreflightJobStatus) {
|
||||
*out = *in
|
||||
if in.CollectorsRunning != nil {
|
||||
in, out := &in.CollectorsRunning, &out.CollectorsRunning
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.CollectorsSuccessful != nil {
|
||||
in, out := &in.CollectorsSuccessful, &out.CollectorsSuccessful
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.CollectorsFailed != nil {
|
||||
in, out := &in.CollectorsFailed, &out.CollectorsFailed
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AnalyzersRunning != nil {
|
||||
in, out := &in.AnalyzersRunning, &out.AnalyzersRunning
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AnalyzersSuccessful != nil {
|
||||
in, out := &in.AnalyzersSuccessful, &out.AnalyzersSuccessful
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AnalyzersFailed != nil {
|
||||
in, out := &in.AnalyzersFailed, &out.AnalyzersFailed
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightJobStatus.
|
||||
@@ -609,9 +721,46 @@ func (in *PreflightList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PreflightRef) DeepCopyInto(out *PreflightRef) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightRef.
|
||||
func (in *PreflightRef) DeepCopy() *PreflightRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PreflightRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PreflightSpec) DeepCopyInto(out *PreflightSpec) {
|
||||
*out = *in
|
||||
if in.Collectors != nil {
|
||||
in, out := &in.Collectors, &out.Collectors
|
||||
*out = make([]*Collect, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Collect)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.Analyzers != nil {
|
||||
in, out := &in.Analyzers, &out.Analyzers
|
||||
*out = make([]*Analyze, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Analyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreflightSpec.
|
||||
@@ -638,3 +787,44 @@ func (in *PreflightStatus) DeepCopy() *PreflightStatus {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SingleOutcome) DeepCopyInto(out *SingleOutcome) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleOutcome.
|
||||
func (in *SingleOutcome) DeepCopy() *SingleOutcome {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SingleOutcome)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StorageClass) DeepCopyInto(out *StorageClass) {
|
||||
*out = *in
|
||||
if in.Outcome != nil {
|
||||
in, out := &in.Outcome, &out.Outcome
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageClass.
|
||||
func (in *StorageClass) DeepCopy() *StorageClass {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StorageClass)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -34,11 +34,6 @@ import (
|
||||
|
||||
var log = logf.Log.WithName("controller")
|
||||
|
||||
/**
|
||||
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
|
||||
* business logic. Delete these comments after modifying this file.*
|
||||
*/
|
||||
|
||||
// Add creates a new Collector Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
|
||||
// and Start it when the Manager is Started.
|
||||
func Add(mgr manager.Manager) error {
|
||||
|
||||
127
pkg/controller/collectorjob/collector_server.go
Normal file
127
pkg/controller/collectorjob/collector_server.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package collectorjob
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
func (r *ReconcileCollectorJob) createCollectorServer(instance *troubleshootv1beta1.CollectorJob) error {
|
||||
name := fmt.Sprintf("%s-%s", instance.Name, "collector")
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
}
|
||||
|
||||
found := &corev1.Pod{}
|
||||
err := r.Get(context.Background(), namespacedName, found)
|
||||
if err == nil || !kuberneteserrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
imageName := "replicatedhq/troubleshoot:latest"
|
||||
imagePullPolicy := corev1.PullAlways
|
||||
|
||||
if instance.Spec.Image != "" {
|
||||
imageName = instance.Spec.Image
|
||||
}
|
||||
if instance.Spec.ImagePullPolicy != "" {
|
||||
imagePullPolicy = corev1.PullPolicy(instance.Spec.ImagePullPolicy)
|
||||
}
|
||||
|
||||
podLabels := make(map[string]string)
|
||||
podLabels["collector"] = instance.Name
|
||||
podLabels["troubleshoot-role"] = "collector"
|
||||
|
||||
pod := corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
Labels: podLabels,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Image: imageName,
|
||||
ImagePullPolicy: imagePullPolicy,
|
||||
Name: "collector",
|
||||
Command: []string{"collector"},
|
||||
Args: []string{"server"},
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
ContainerPort: 8000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(instance, &pod, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Create(context.Background(), &pod); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service := corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Selector: podLabels,
|
||||
Type: corev1.ServiceTypeClusterIP,
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 8000,
|
||||
TargetPort: intstr.FromInt(8000),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(instance, &service, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Create(context.Background(), &service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
instance.Status.ServerPodName = name
|
||||
instance.Status.ServerPodNamespace = instance.Namespace
|
||||
instance.Status.ServerPodPort = 8000
|
||||
instance.Status.IsServerReady = true
|
||||
|
||||
// wait for the server to be ready
|
||||
// TODO
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
if err := r.Update(context.Background(), instance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -37,7 +37,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
@@ -145,118 +144,6 @@ func (r *ReconcileCollectorJob) Reconcile(request reconcile.Request) (reconcile.
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *ReconcileCollectorJob) createCollectorServer(instance *troubleshootv1beta1.CollectorJob) error {
|
||||
name := fmt.Sprintf("%s-%s", instance.Name, "collector")
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
}
|
||||
|
||||
found := &corev1.Pod{}
|
||||
err := r.Get(context.Background(), namespacedName, found)
|
||||
if err == nil || !kuberneteserrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
imageName := "replicatedhq/troubleshoot:latest"
|
||||
imagePullPolicy := corev1.PullAlways
|
||||
|
||||
if instance.Spec.Image != "" {
|
||||
imageName = instance.Spec.Image
|
||||
}
|
||||
if instance.Spec.ImagePullPolicy != "" {
|
||||
imagePullPolicy = corev1.PullPolicy(instance.Spec.ImagePullPolicy)
|
||||
}
|
||||
|
||||
podLabels := make(map[string]string)
|
||||
podLabels["collector"] = instance.Name
|
||||
podLabels["troubleshoot-role"] = "collector"
|
||||
|
||||
pod := corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
Labels: podLabels,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Image: imageName,
|
||||
ImagePullPolicy: imagePullPolicy,
|
||||
Name: "collector",
|
||||
Command: []string{"collector"},
|
||||
Args: []string{"server"},
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
ContainerPort: 8000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(instance, &pod, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Create(context.Background(), &pod); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service := corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Selector: podLabels,
|
||||
Type: corev1.ServiceTypeClusterIP,
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 8000,
|
||||
TargetPort: intstr.FromInt(8000),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(instance, &service, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Create(context.Background(), &service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
instance.Status.ServerPodName = name
|
||||
instance.Status.ServerPodNamespace = instance.Namespace
|
||||
instance.Status.ServerPodPort = 8000
|
||||
instance.Status.IsServerReady = true
|
||||
|
||||
// wait for the server to be ready
|
||||
// TODO
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
if err := r.Update(context.Background(), instance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcileCollectorJob) getCollectorSpec(namespace string, name string) (*troubleshootv1beta1.Collector, error) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
|
||||
@@ -18,18 +18,12 @@ package preflight
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
@@ -39,11 +33,6 @@ import (
|
||||
|
||||
var log = logf.Log.WithName("controller")
|
||||
|
||||
/**
|
||||
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
|
||||
* business logic. Delete these comments after modifying this file.*
|
||||
*/
|
||||
|
||||
// Add creates a new Preflight Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
|
||||
// and Start it when the Manager is Started.
|
||||
func Add(mgr manager.Manager) error {
|
||||
@@ -69,16 +58,6 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(user): Modify this to be the types you create
|
||||
// Uncomment watch a Deployment created by Preflight - change this for objects you create
|
||||
err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{
|
||||
IsController: true,
|
||||
OwnerType: &troubleshootv1beta1.Preflight{},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -104,7 +83,7 @@ func (r *ReconcilePreflight) Reconcile(request reconcile.Request) (reconcile.Res
|
||||
instance := &troubleshootv1beta1.Preflight{}
|
||||
err := r.Get(context.TODO(), request.NamespacedName, instance)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if kuberneteserrors.IsNotFound(err) {
|
||||
// Object not found, return. Created objects are automatically garbage collected.
|
||||
// For additional cleanup logic use finalizers.
|
||||
return reconcile.Result{}, nil
|
||||
@@ -113,55 +92,5 @@ func (r *ReconcilePreflight) Reconcile(request reconcile.Request) (reconcile.Res
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// TODO(user): Change this to be the object type created by your controller
|
||||
// Define the desired Deployment object
|
||||
deploy := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: instance.Name + "-deployment",
|
||||
Namespace: instance.Namespace,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"deployment": instance.Name + "-deployment"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"deployment": instance.Name + "-deployment"}},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := controllerutil.SetControllerReference(instance, deploy, r.scheme); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// TODO(user): Change this for the object type created by your controller
|
||||
// Check if the Deployment already exists
|
||||
found := &appsv1.Deployment{}
|
||||
err = r.Get(context.TODO(), types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, found)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
log.Info("Creating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
|
||||
err = r.Create(context.TODO(), deploy)
|
||||
return reconcile.Result{}, err
|
||||
} else if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// TODO(user): Change this for the object type created by your controller
|
||||
// Update the found object and write the result back if there are any changes
|
||||
if !reflect.DeepEqual(deploy.Spec, found.Spec) {
|
||||
found.Spec = deploy.Spec
|
||||
log.Info("Updating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
|
||||
err = r.Update(context.TODO(), found)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
70
pkg/controller/preflightjob/analyzers.go
Normal file
70
pkg/controller/preflightjob/analyzers.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package preflightjob
|
||||
|
||||
import (
|
||||
"context"
|
||||
// "fmt"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
// "gopkg.in/yaml.v2"
|
||||
// corev1 "k8s.io/api/core/v1"
|
||||
// kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
// "k8s.io/apimachinery/pkg/types"
|
||||
// "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
type AnalysisResult struct {
|
||||
Success bool
|
||||
TextOutput string
|
||||
}
|
||||
|
||||
func (r *ReconcilePreflightJob) reconcilePreflightAnalyzers(instance *troubleshootv1beta1.PreflightJob, preflight *troubleshootv1beta1.Preflight) error {
|
||||
for _, analyzer := range preflight.Spec.Analyzers {
|
||||
if err := r.reconcileOnePreflightAnalyzer(instance, analyzer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcilePreflightJob) reconcileOnePreflightAnalyzer(instance *troubleshootv1beta1.PreflightJob, analyze *troubleshootv1beta1.Analyze) error {
|
||||
if contains(instance.Status.AnalyzersRunning, idForAnalyzer(analyze)) {
|
||||
// these are the analyzers we want to attempt to run
|
||||
if analyze.ClusterVersion != nil {
|
||||
result, err := r.analyzeClusterVersion(instance, analyze.ClusterVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result.Success == false {
|
||||
return nil // collectors are not yet ready
|
||||
}
|
||||
|
||||
instance.Status.AnalyzersSuccessful = append(instance.Status.AnalyzersSuccessful, idForAnalyzer(analyze))
|
||||
instance.Status.AnalyzersRunning = remove(instance.Status.AnalyzersRunning, idForAnalyzer(analyze))
|
||||
|
||||
if err := r.Update(context.Background(), instance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func idForAnalyzer(analyzer *troubleshootv1beta1.Analyze) string {
|
||||
if analyzer.ClusterVersion != nil {
|
||||
return "cluster-version"
|
||||
}
|
||||
|
||||
if analyzer.StorageClass != nil {
|
||||
return "storage-classes"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
11
pkg/controller/preflightjob/cluster_version.go
Normal file
11
pkg/controller/preflightjob/cluster_version.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package preflightjob
|
||||
|
||||
import (
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
)
|
||||
|
||||
func (r *ReconcilePreflightJob) analyzeClusterVersion(instance *troubleshootv1beta1.PreflightJob, clusterVersion *troubleshootv1beta1.ClusterVersion) (*AnalysisResult, error) {
|
||||
return &AnalysisResult{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
233
pkg/controller/preflightjob/collectors.go
Normal file
233
pkg/controller/preflightjob/collectors.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package preflightjob
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
"gopkg.in/yaml.v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
func (r *ReconcilePreflightJob) reconcilePreflightCollectors(instance *troubleshootv1beta1.PreflightJob, preflight *troubleshootv1beta1.Preflight) error {
|
||||
requestedCollectorIDs := make([]string, 0, 0)
|
||||
for _, collector := range preflight.Spec.Collectors {
|
||||
requestedCollectorIDs = append(requestedCollectorIDs, idForCollector(collector))
|
||||
if err := r.reconcileOnePreflightCollector(instance, collector); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !contains(requestedCollectorIDs, "cluster-info") {
|
||||
clusterInfo := troubleshootv1beta1.Collect{
|
||||
ClusterInfo: &troubleshootv1beta1.ClusterInfo{},
|
||||
}
|
||||
if err := r.reconcileOnePreflightCollector(instance, &clusterInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !contains(requestedCollectorIDs, "cluster-resources") {
|
||||
clusterResources := troubleshootv1beta1.Collect{
|
||||
ClusterResources: &troubleshootv1beta1.ClusterResources{},
|
||||
}
|
||||
if err := r.reconcileOnePreflightCollector(instance, &clusterResources); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcilePreflightJob) reconcileOnePreflightCollector(instance *troubleshootv1beta1.PreflightJob, collect *troubleshootv1beta1.Collect) error {
|
||||
if contains(instance.Status.CollectorsRunning, idForCollector(collect)) {
|
||||
// preflight just leaves these stopped containers.
|
||||
// it's playing with fire a little, but the analyzers can just
|
||||
// read from the stdout of the stopped container
|
||||
//
|
||||
// in the very common use case (what we are building for today)
|
||||
// there's not too much risk in something destroying and reaping that stopped pod
|
||||
// immediately. this is a longer term problem to solve, maybe something,
|
||||
// the mananger? can broker these collector results. but, ya know...
|
||||
|
||||
instance.Status.CollectorsSuccessful = append(instance.Status.CollectorsSuccessful, idForCollector(collect))
|
||||
instance.Status.CollectorsRunning = remove(instance.Status.CollectorsRunning, idForCollector(collect))
|
||||
|
||||
if err := r.Update(context.Background(), instance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := r.createCollectorSpecInConfigMap(instance, collect); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.createCollectorPod(instance, collect); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcilePreflightJob) createCollectorSpecInConfigMap(instance *troubleshootv1beta1.PreflightJob, collector *troubleshootv1beta1.Collect) error {
|
||||
name := fmt.Sprintf("%s-%s", instance.Name, idForCollector(collector))
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
}
|
||||
|
||||
found := &corev1.ConfigMap{}
|
||||
err := r.Get(context.Background(), namespacedName, found)
|
||||
if err == nil || !kuberneteserrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
specContents, err := yaml.Marshal(collector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
specData := make(map[string]string)
|
||||
specData["collector.yaml"] = string(specContents)
|
||||
|
||||
configMap := corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
Data: specData,
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(instance, &configMap, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Create(context.Background(), &configMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReconcilePreflightJob) createCollectorPod(instance *troubleshootv1beta1.PreflightJob, collector *troubleshootv1beta1.Collect) error {
|
||||
name := fmt.Sprintf("%s-%s", instance.Name, idForCollector(collector))
|
||||
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
}
|
||||
|
||||
found := &corev1.Pod{}
|
||||
err := r.Get(context.Background(), namespacedName, found)
|
||||
if err == nil || !kuberneteserrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
imageName := "replicatedhq/troubleshoot:latest"
|
||||
imagePullPolicy := corev1.PullAlways
|
||||
|
||||
if instance.Spec.Image != "" {
|
||||
imageName = instance.Spec.Image
|
||||
}
|
||||
if instance.Spec.ImagePullPolicy != "" {
|
||||
imagePullPolicy = corev1.PullPolicy(instance.Spec.ImagePullPolicy)
|
||||
}
|
||||
|
||||
pod := corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Image: imageName,
|
||||
ImagePullPolicy: imagePullPolicy,
|
||||
Name: idForCollector(collector),
|
||||
Command: []string{"collector"},
|
||||
Args: []string{
|
||||
"run",
|
||||
"--collector",
|
||||
"/troubleshoot/specs/collector.yaml",
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{
|
||||
Name: "collector",
|
||||
MountPath: "/troubleshoot/specs",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Volumes: []corev1.Volume{
|
||||
{
|
||||
Name: "collector",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
ConfigMap: &corev1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(instance, &pod, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Create(context.Background(), &pod); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
instance.Status.CollectorsRunning = append(instance.Status.CollectorsRunning, idForCollector(collector))
|
||||
if err := r.Update(context.Background(), instance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Todo these will overlap with troubleshoot containers running at the same time
|
||||
func idForCollector(collector *troubleshootv1beta1.Collect) string {
|
||||
if collector.ClusterInfo != nil {
|
||||
return "cluster-info"
|
||||
} else if collector.ClusterResources != nil {
|
||||
return "cluster-resources"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func remove(s []string, r string) []string {
|
||||
for i, v := range s {
|
||||
if v == r {
|
||||
return append(s[:i], s[i+1:]...)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
126
pkg/controller/preflightjob/preflight_server.go
Normal file
126
pkg/controller/preflightjob/preflight_server.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package preflightjob
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
func (r *ReconcilePreflightJob) createPreflightServer(instance *troubleshootv1beta1.PreflightJob) error {
|
||||
name := fmt.Sprintf("%s-%s", instance.Name, "preflight")
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
}
|
||||
|
||||
found := &corev1.Pod{}
|
||||
err := r.Get(context.Background(), namespacedName, found)
|
||||
if err == nil || !kuberneteserrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
imageName := "replicatedhq/troubleshoot:latest"
|
||||
imagePullPolicy := corev1.PullAlways
|
||||
|
||||
if instance.Spec.Image != "" {
|
||||
imageName = instance.Spec.Image
|
||||
}
|
||||
if instance.Spec.ImagePullPolicy != "" {
|
||||
imagePullPolicy = corev1.PullPolicy(instance.Spec.ImagePullPolicy)
|
||||
}
|
||||
|
||||
podLabels := make(map[string]string)
|
||||
podLabels["preflight"] = instance.Name
|
||||
podLabels["troubleshoot-role"] = "preflight"
|
||||
|
||||
pod := corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
Labels: podLabels,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Image: imageName,
|
||||
ImagePullPolicy: imagePullPolicy,
|
||||
Name: "preflight",
|
||||
Command: []string{"preflight"},
|
||||
Args: []string{"server"},
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
ContainerPort: 8000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(instance, &pod, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Create(context.Background(), &pod); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service := corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: instance.Namespace,
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Selector: podLabels,
|
||||
Type: corev1.ServiceTypeClusterIP,
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 8000,
|
||||
TargetPort: intstr.FromInt(8000),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(instance, &service, r.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Create(context.Background(), &service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
instance.Status.ServerPodName = name
|
||||
instance.Status.ServerPodNamespace = instance.Namespace
|
||||
instance.Status.ServerPodPort = 8000
|
||||
instance.Status.IsServerReady = true
|
||||
|
||||
// wait for the server to be ready
|
||||
// TODO
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
if err := r.Update(context.Background(), instance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -18,18 +18,15 @@ package preflightjob
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
troubleshootclientv1beta1 "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1"
|
||||
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
@@ -39,11 +36,6 @@ import (
|
||||
|
||||
var log = logf.Log.WithName("controller")
|
||||
|
||||
/**
|
||||
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
|
||||
* business logic. Delete these comments after modifying this file.*
|
||||
*/
|
||||
|
||||
// Add creates a new PreflightJob Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
|
||||
// and Start it when the Manager is Started.
|
||||
func Add(mgr manager.Manager) error {
|
||||
@@ -69,16 +61,6 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(user): Modify this to be the types you create
|
||||
// Uncomment watch a Deployment created by PreflightJob - change this for objects you create
|
||||
err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{
|
||||
IsController: true,
|
||||
OwnerType: &troubleshootv1beta1.PreflightJob{},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -104,7 +86,7 @@ func (r *ReconcilePreflightJob) Reconcile(request reconcile.Request) (reconcile.
|
||||
instance := &troubleshootv1beta1.PreflightJob{}
|
||||
err := r.Get(context.TODO(), request.NamespacedName, instance)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if kuberneteserrors.IsNotFound(err) {
|
||||
// Object not found, return. Created objects are automatically garbage collected.
|
||||
// For additional cleanup logic use finalizers.
|
||||
return reconcile.Result{}, nil
|
||||
@@ -113,55 +95,65 @@ func (r *ReconcilePreflightJob) Reconcile(request reconcile.Request) (reconcile.
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// TODO(user): Change this to be the object type created by your controller
|
||||
// Define the desired Deployment object
|
||||
deploy := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: instance.Name + "-deployment",
|
||||
Namespace: instance.Namespace,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"deployment": instance.Name + "-deployment"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"deployment": instance.Name + "-deployment"}},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := controllerutil.SetControllerReference(instance, deploy, r.scheme); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// TODO(user): Change this for the object type created by your controller
|
||||
// Check if the Deployment already exists
|
||||
found := &appsv1.Deployment{}
|
||||
err = r.Get(context.TODO(), types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, found)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
log.Info("Creating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
|
||||
err = r.Create(context.TODO(), deploy)
|
||||
return reconcile.Result{}, err
|
||||
} else if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// TODO(user): Change this for the object type created by your controller
|
||||
// Update the found object and write the result back if there are any changes
|
||||
if !reflect.DeepEqual(deploy.Spec, found.Spec) {
|
||||
found.Spec = deploy.Spec
|
||||
log.Info("Updating Deployment", "namespace", deploy.Namespace, "name", deploy.Name)
|
||||
err = r.Update(context.TODO(), found)
|
||||
if err != nil {
|
||||
if !instance.Status.IsServerReady {
|
||||
if err := r.createPreflightServer(instance); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
namespace := instance.Namespace
|
||||
if instance.Spec.Preflight.Namespace != "" {
|
||||
namespace = instance.Spec.Preflight.Namespace
|
||||
}
|
||||
|
||||
preflightSpec, err := r.getPreflightSpec(namespace, instance.Spec.Preflight.Name)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
if len(instance.Status.AnalyzersRunning) == 0 && len(instance.Status.AnalyzersSuccessful) == 0 && len(instance.Status.AnalyzersFailed) == 0 {
|
||||
// Add them all!
|
||||
analyzersRunning := []string{}
|
||||
for _, analyzer := range preflightSpec.Spec.Analyzers {
|
||||
analyzersRunning = append(analyzersRunning, idForAnalyzer(analyzer))
|
||||
}
|
||||
|
||||
instance.Status.AnalyzersRunning = analyzersRunning
|
||||
if err := r.Update(context.Background(), instance); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.reconcilePreflightCollectors(instance, preflightSpec); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
if err := r.reconcilePreflightAnalyzers(instance, preflightSpec); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// just finished, nothing to do
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *ReconcilePreflightJob) getPreflightSpec(namespace string, name string) (*troubleshootv1beta1.Preflight, error) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
troubleshootClient, err := troubleshootclientv1beta1.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
preflight, err := troubleshootClient.Preflights(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if kuberneteserrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return preflight, nil
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var queue = make(map[string][]byte)
|
||||
var collectorQueue = make(map[string][]byte)
|
||||
|
||||
func Serve(ctx context.Context, address string) {
|
||||
func ServeCollector(ctx context.Context, address string) {
|
||||
g := gin.New()
|
||||
|
||||
root := g.Group("/")
|
||||
@@ -35,20 +35,20 @@ func putCollectorOutput(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
queue[collectorID] = body
|
||||
collectorQueue[collectorID] = body
|
||||
|
||||
fmt.Printf("queue = %#v\n", queue)
|
||||
fmt.Printf("collectorQueue = %#v\n", collectorQueue)
|
||||
c.Status(201)
|
||||
}
|
||||
|
||||
func getCollectorOutput(c *gin.Context) {
|
||||
encoded := base64.StdEncoding.EncodeToString(queue[c.Param("id")])
|
||||
encoded := base64.StdEncoding.EncodeToString(collectorQueue[c.Param("id")])
|
||||
c.String(200, encoded)
|
||||
}
|
||||
|
||||
func getQueuedCollectors(c *gin.Context) {
|
||||
keys := make([]string, 0, len(queue))
|
||||
for k := range queue {
|
||||
keys := make([]string, 0, len(collectorQueue))
|
||||
for k := range collectorQueue {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
56
pkg/server/preflight.go
Normal file
56
pkg/server/preflight.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var preflightQueue = make(map[string][]byte)
|
||||
|
||||
func ServePreflight(ctx context.Context, address string) {
|
||||
g := gin.New()
|
||||
|
||||
root := g.Group("/")
|
||||
root.PUT("/", putPreflightOutput)
|
||||
root.GET("/", getQueuedPreflights)
|
||||
root.GET("/preflight/:id", getPreflightOutput)
|
||||
|
||||
srvr := http.Server{Addr: address, Handler: g}
|
||||
go func() {
|
||||
srvr.ListenAndServe()
|
||||
}()
|
||||
}
|
||||
|
||||
func putPreflightOutput(c *gin.Context) {
|
||||
preflightID := c.Request.Header.Get("collector-id")
|
||||
|
||||
body, err := ioutil.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.AbortWithStatus(500)
|
||||
return
|
||||
}
|
||||
|
||||
preflightQueue[preflightID] = body
|
||||
|
||||
fmt.Printf("preflightQueue = %#v\n", preflightQueue)
|
||||
c.Status(201)
|
||||
}
|
||||
|
||||
func getPreflightOutput(c *gin.Context) {
|
||||
encoded := base64.StdEncoding.EncodeToString(preflightQueue[c.Param("id")])
|
||||
c.String(200, encoded)
|
||||
}
|
||||
|
||||
func getQueuedPreflights(c *gin.Context) {
|
||||
keys := make([]string, 0, len(preflightQueue))
|
||||
for k := range preflightQueue {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
c.JSON(200, keys)
|
||||
}
|
||||
Reference in New Issue
Block a user