This commit is contained in:
Marc Campbell
2019-07-12 14:30:38 +00:00
parent 50cf684213
commit 978a4a13ba
15 changed files with 455 additions and 60 deletions

View File

@@ -85,3 +85,11 @@ local-release: snapshot-release
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

View File

@@ -5,7 +5,6 @@ import (
"io/ioutil"
"net/http"
"os"
"time"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -34,12 +33,6 @@ func receivePreflightResults(preflightJobNamespace string, preflightJobName stri
return err
}
// If the collectors are still running, hang tight.
if !job.Status.IsCollectorsComplete {
time.Sleep(time.Millisecond * 400)
continue
}
for _, readyPreflight := range job.Status.AnalyzersSuccessful {
alreadyReceived := false
for _, receivedPreflight := range receivedPreflights {
@@ -67,22 +60,8 @@ func receivePreflightResults(preflightJobNamespace string, preflightJobName stri
receivedPreflights = append(receivedPreflights, readyPreflight)
}
// if len(job.Status.Running) == 0 {
// tarGz := archiver.TarGz{
// Tar: &archiver.Tar{
// ImplicitTopLevelFolder: false,
// },
// }
// paths := make([]string, 0, 0)
// for _, id := range receivedCollectors {
// paths = append(paths, filepath.Join(bundlePath, id))
// }
// if err := tarGz.Archive(paths, "support-bundle.tar.gz"); err != nil {
// return err
// }
// return nil
// }
if len(job.Status.AnalyzersRunning) == 0 {
return nil
}
}
}

View File

@@ -125,8 +125,6 @@ func Run() *cobra.Command {
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")
cmd.Flags().String("analyzer-image", "", "the full name of the analyzer image to use")
cmd.Flags().String("analyzer-pullpolicy", "", "the pull policy of the analyzer image")
viper.BindPFlags(cmd.Flags())

View File

@@ -391,10 +391,6 @@ spec:
type: object
spec:
properties:
analyzerImage:
type: string
analyzerImagePullPolicy:
type: string
collectorImage:
type: string
collectorImagePullPolicy:
@@ -443,8 +439,6 @@ spec:
type: array
isAnalyzersComplete:
type: boolean
isCollectorsComplete:
type: boolean
isServerReady:
type: boolean
serverPodName:
@@ -458,7 +452,6 @@ spec:
- serverPodName
- serverPodNamespace
- serverPodPort
- isCollectorsComplete
- collectorsRunning
- collectorsSuccessful
- collectorsFailed

View File

@@ -393,6 +393,84 @@ 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:

View File

@@ -11,6 +11,16 @@ import (
// 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.
@@ -231,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
@@ -460,6 +496,36 @@ 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
@@ -675,7 +741,7 @@ func (in *PreflightSpec) DeepCopyInto(out *PreflightSpec) {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Analyze)
**out = **in
(*in).DeepCopyInto(*out)
}
}
}
@@ -705,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
}

View File

@@ -5,14 +5,15 @@ metadata:
spec:
analyzers:
- clusterVersion:
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
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:

View File

@@ -1,4 +1,27 @@
package v1beta1
type Analyze struct {
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"`
}

View 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)
})
}
}

View File

@@ -33,9 +33,6 @@ type PreflightJobSpec struct {
ImagePullPolicy string `json:"imagePullPolicy,omitempty"`
CollectorImage string `json:"collectorImage,omitempty"`
CollectorImagePullPolicy string `json:"collectorImagePullPolicy,omitempty"`
AnalyzerImage string `json:"analyzerImage,omitempty"`
AnalyzerImagePullPolicy string `json:"analyzerImagePullPolicy,omitempty"`
}
// PreflightJobStatus defines the observed state of PreflightJob
@@ -45,7 +42,6 @@ type PreflightJobStatus struct {
ServerPodNamespace string `json:"serverPodNamespace"`
ServerPodPort int `json:"serverPodPort"`
IsCollectorsComplete bool `json:"isCollectorsComplete"`
CollectorsRunning []string `json:"collectorsRunning"`
CollectorsSuccessful []string `json:"collectorsSuccessful"`
CollectorsFailed []string `json:"collectorsFailed"`

View File

@@ -27,6 +27,16 @@ import (
// 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.
@@ -247,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
@@ -476,6 +512,36 @@ 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
@@ -691,7 +757,7 @@ func (in *PreflightSpec) DeepCopyInto(out *PreflightSpec) {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Analyze)
**out = **in
(*in).DeepCopyInto(*out)
}
}
}
@@ -721,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
}

View File

@@ -1,7 +1,7 @@
package preflightjob
import (
// "context"
"context"
// "fmt"
troubleshootv1beta1 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
@@ -13,17 +13,43 @@ import (
// "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
}
@@ -32,5 +58,13 @@ func (r *ReconcilePreflightJob) reconcileOnePreflightAnalyzer(instance *troubles
}
func idForAnalyzer(analyzer *troubleshootv1beta1.Analyze) string {
if analyzer.ClusterVersion != nil {
return "cluster-version"
}
if analyzer.StorageClass != nil {
return "storage-classes"
}
return ""
}

View 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
}

View File

@@ -56,10 +56,6 @@ func (r *ReconcilePreflightJob) reconcileOnePreflightCollector(instance *trouble
instance.Status.CollectorsSuccessful = append(instance.Status.CollectorsSuccessful, idForCollector(collect))
instance.Status.CollectorsRunning = remove(instance.Status.CollectorsRunning, idForCollector(collect))
if len(instance.Status.CollectorsRunning) == 0 {
instance.Status.IsCollectorsComplete = true
}
if err := r.Update(context.Background(), instance); err != nil {
return err
}

View File

@@ -111,17 +111,26 @@ func (r *ReconcilePreflightJob) Reconcile(request reconcile.Request) (reconcile.
return reconcile.Result{}, err
}
// the preflight job will be in collector mode or analyzer mode
if !instance.Status.IsCollectorsComplete {
if err := r.reconcilePreflightCollectors(instance, preflightSpec); err != nil {
return reconcile.Result{}, err
}
} else if !instance.Status.IsAnalyzersComplete {
if err := r.reconcilePreflightAnalyzers(instance, preflightSpec); err != nil {
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
}