Skip to content

Commit

Permalink
Merge pull request #1 from lightstep/create-repo
Browse files Browse the repository at this point in the history
First working state
  • Loading branch information
jaronoff97 authored Jun 28, 2023
2 parents be62c27 + d66c8ce commit 4200f5f
Show file tree
Hide file tree
Showing 25 changed files with 2,196 additions and 2 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Go Build
on: [push]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.20.x'
- name: Install dependencies
run: go get .
- name: Build
run: go build -v ./...
- name: Test with the Go CLI
run: go test ./...
28 changes: 28 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
on:
release:
types: [created]

jobs:
releases-matrix:
name: Release Go Binary
runs-on: ubuntu-latest
strategy:
matrix:
# build and publish in parallel: linux/386, linux/amd64, linux/arm64, windows/386, windows/amd64, darwin/amd64, darwin/arm64
goos: [linux, windows, darwin]
goarch: ["386", amd64, arm64]
exclude:
- goarch: "386"
goos: darwin
- goarch: arm64
goos: windows
steps:
- uses: actions/checkout@v3
- uses: wangyoucao577/go-release-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
goversion: "1.20.5"
binary_name: "collector-cluster-check"
extra_files: LICENSE README.md
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/

# Go workspace file
go.work

.idea

collector-cluster-check
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,40 @@
# collector-cluster-check
A shippable binary that you can run to confirm you can successfully run a collector in Kubernetes to send data to Lightstep

This tool allows you to test various parts of your infrastructure and observability stack.
The goal is to provide you with confidence that you are able to effectively use the OpenTelemetry Operator,
create OpenTelemetry Collectors, and send data to your desired destination.

```
Usage:
collector-cluster-check [command]
Available Commands:
check runs one of multiple checks, use -h for more
completion Generate the autocompletion script for the specified shell
help Help about any command
Flags:
--config string config file (default is $HOME/.collector-cluster-check.yaml)
-h, --help help for collector-cluster-check
-t, --toggle Help message for toggle
Use "collector-cluster-check [command] --help" for more information about a command.
runs one of multiple checks, use -h for more
```

## `check` Command

```
Usage:
collector-cluster-check check [metrics|tracing|preflight|all] [flags]
Flags:
--accessToken string access token to send data to Lightstep
-h, --help help for check
--http should telemetry be sent over http
--kubeConfig string (optional) absolute path to the kubeconfig file (default "/Users/jacob.aronoff/.kube/config")
Global Flags:
--config string config file (default is $HOME/.collector-cluster-check.yaml)
```
183 changes: 183 additions & 0 deletions cmd/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
Copyright © 2023 Jacob Aronoff <[email protected]>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"k8s.io/client-go/util/homedir"

"github.com/lightstep/collector-cluster-check/pkg/checks"
"github.com/lightstep/collector-cluster-check/pkg/checks/certmanager"
"github.com/lightstep/collector-cluster-check/pkg/checks/kubernetes"
"github.com/lightstep/collector-cluster-check/pkg/checks/lightstep"
"github.com/lightstep/collector-cluster-check/pkg/checks/oteloperator"
"github.com/lightstep/collector-cluster-check/pkg/checks/prometheus"
"github.com/lightstep/collector-cluster-check/pkg/dependencies"
)

type checkGroup struct {
dependencies []dependencies.Initializer
checkers []checks.NewChecker
}

var (
kubeConfig string
accessToken string
http bool
availableChecks = map[string]checkGroup{
"metrics": {
dependencies: []dependencies.Initializer{dependencies.MetricInitializer},
checkers: []checks.NewChecker{lightstep.NewMetricCheck},
},
"tracing": {
dependencies: []dependencies.Initializer{dependencies.TraceInitializer},
checkers: []checks.NewChecker{lightstep.NewTraceCheck},
},
"preflight": {
dependencies: []dependencies.Initializer{dependencies.KubernetesClientInitializer, dependencies.CustomResourceClientInitializer},
checkers: []checks.NewChecker{kubernetes.NewVersionCheck, prometheus.NewCheck, certmanager.NewCheck, oteloperator.NewCheck},
},
"all": {
dependencies: []dependencies.Initializer{dependencies.KubernetesClientInitializer, dependencies.CustomResourceClientInitializer, dependencies.MetricInitializer, dependencies.TraceInitializer},
checkers: []checks.NewChecker{kubernetes.NewVersionCheck, prometheus.NewCheck, certmanager.NewCheck, oteloperator.NewCheck, lightstep.NewMetricCheck, lightstep.NewTraceCheck},
},
}
)

// checkCmd represents the check command
var checkCmd = &cobra.Command{
Use: "check [metrics|tracing|preflight|all]",
Short: "runs one of multiple checks, use -h for more",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("must specify at least one check to run")
}
var validArgs []string
for _, v := range cmd.ValidArgs {
validArgs = append(validArgs, strings.Split(v, "\t")[0])
}
for _, v := range args {
if _, ok := availableChecks[v]; !ok {
return fmt.Errorf("invalid argument %q for %q", v, cmd.CommandPath())
}
}
return nil
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var comps []string
if len(args) == 0 {
comps = cobra.AppendActiveHelp(comps, "You must choose at least one check to run")
} else {
for _, arg := range args {
if _, ok := availableChecks[arg]; !ok {
comps = cobra.AppendActiveHelp(comps, fmt.Sprintf("%s is not a valid check", arg))
}
}
}
return comps, cobra.ShellCompDirectiveNoFileComp
},
Run: func(cmd *cobra.Command, args []string) {
for _, c := range args {
group := availableChecks[c]
var depResults []*checks.Check
var results map[string]checks.CheckerResult
var opts []checks.RunnerOption
for _, d := range group.dependencies {
runnerOption, checkResult := d.Apply(cmd.Context(), http, accessToken, kubeConfig)
depResults = append(depResults, checkResult)
if checkResult.IsFailure() {
return
}
opts = append(opts, runnerOption)
}
runner := checks.NewRunner(group.checkers, opts...)
results = runner.Run(cmd.Context())
prettyPrintDependenciesResults(depResults)
prettyPrint(results)
}
},
}

func prettyPrintDependenciesResults(results []*checks.Check) {
t := table.NewWriter()
rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
t.AppendHeader(table.Row{"dependency", "Result", "Message", "Error"})
t.SetColumnConfigs([]table.ColumnConfig{
{Number: 1, AutoMerge: true},
})
for _, check := range results {
prettyResult := "🟩"
if !check.IsSuccess() {
prettyResult = "🟥"
}
t.AppendRow(table.Row{check.Name, prettyResult, check.Message, check.Error}, rowConfigAutoMerge)
}
t.SetOutputMirror(os.Stdout)
t.SetStyle(table.StyleLight)
t.Style().Options.SeparateRows = true
t.Render()
}

func prettyPrint(results map[string]checks.CheckerResult) {
t := table.NewWriter()
rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
t.AppendHeader(table.Row{"Checker", "Result", "Check Name", "Message", "Error"})
t.SetColumnConfigs([]table.ColumnConfig{
{Number: 1, AutoMerge: true},
})
for checker, result := range results {
for _, check := range result {
prettyResult := "🟩"
if !check.IsSuccess() {
prettyResult = "🟥"
}
t.AppendRow(table.Row{checker, prettyResult, check.Name, check.Message, check.Error}, rowConfigAutoMerge)
}
}
t.SetOutputMirror(os.Stdout)
t.SetStyle(table.StyleLight)
t.Style().Options.SeparateRows = true
t.Render()
}

func init() {
rootCmd.AddCommand(checkCmd)

if home := homedir.HomeDir(); home != "" {
checkCmd.PersistentFlags().StringVarP(&kubeConfig, "kubeConfig", "", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
checkCmd.PersistentFlags().StringVarP(&kubeConfig, "kubeconfig", "", "", "absolute path to the kubeconfig file")
}
checkCmd.PersistentFlags().StringVarP(&accessToken, "accessToken", "", os.Getenv("LS_TOKEN"), "access token to send data to Lightstep")
checkCmd.PersistentFlags().BoolVarP(&http, "http", "", false, "should telemetry be sent over http")

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// checkCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// checkCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
86 changes: 86 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
Copyright © 2023 Jacob Aronoff <[email protected]>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var cfgFile string

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "collector-cluster-check",
Short: "this tool is used to run a variety of checks in your current kube context",
Long: `This tool allows you to test various parts of your infrastructure and observability stack.
The goal is to provide you with confidence that you are able to effectively use the OpenTelemetry Operator,
create OpenTelemetry Collectors, and send data to your desired destination.`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}

func init() {
cobra.OnInitialize(initConfig)

// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.

rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.collector-cluster-check.yaml)")

// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := os.UserHomeDir()
cobra.CheckErr(err)

// Search config in home directory with name ".collector-cluster-check" (without extension).
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName(".collector-cluster-check")
}

viper.AutomaticEnv() // read in environment variables that match

// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}
}
Loading

0 comments on commit 4200f5f

Please sign in to comment.