Skip to content

Commit

Permalink
Merge pull request #3 from replicatedhq/preflight
Browse files Browse the repository at this point in the history
Preflights and preflightjobs
  • Loading branch information
divolgin authored Jul 15, 2019
2 parents 5e1893b + 978a4a1 commit 339b19e
Show file tree
Hide file tree
Showing 31 changed files with 1,693 additions and 313 deletions.
14 changes: 11 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 5 additions & 4 deletions cmd/collector/cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
67 changes: 67 additions & 0 deletions cmd/preflight/cli/receive.go
Original file line number Diff line number Diff line change
@@ -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
}
}
}
13 changes: 8 additions & 5 deletions cmd/preflight/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -32,6 +35,6 @@ func InitAndExecute() {
}

func initConfig() {
viper.SetEnvPrefix("TROUBLESHOOT")
viper.SetEnvPrefix("PREFLIGHT")
viper.AutomaticEnv()
}
139 changes: 139 additions & 0 deletions cmd/preflight/cli/run.go
Original file line number Diff line number Diff line change
@@ -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 changes: 43 additions & 0 deletions cmd/preflight/cli/server.go
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions cmd/preflight/cli/troubleshoot.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 339b19e

Please sign in to comment.