Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DEVOPS-646] Refactor all output into plugins similar to Fact & Analyse #68

Merged
merged 8 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ ADD . $GOPATH/src/github.com/salsadigitalauorg/shipshape/

WORKDIR $GOPATH/src/github.com/salsadigitalauorg/shipshape

ENV CGO_ENABLED 0
ENV CGO_ENABLED=0

ARG TARGETOS TARGETARCH

RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} && \
go mod tidy && \
RUN go mod tidy && \
go generate ./... && \
go build -ldflags="-s -w -X github.com/salsadigitalauorg/shipshape/cmd.version=${VERSION} \
-X github.com/salsadigitalauorg/shipshape/cmd.commit=${COMMIT}" -o build/shipshape
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build \
-ldflags="-s -w \
-X github.com/salsadigitalauorg/shipshape/cmd.version=${VERSION} \
-X github.com/salsadigitalauorg/shipshape/cmd.commit=${COMMIT}" \
-o build/shipshape

FROM scratch

Expand Down
10 changes: 8 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"fmt"
"os"

"github.com/salsadigitalauorg/shipshape/pkg/config"
log "github.com/sirupsen/logrus"

"github.com/spf13/cobra"

"github.com/salsadigitalauorg/shipshape/pkg/config"
"github.com/salsadigitalauorg/shipshape/pkg/flagsprovider"
)

// Version information.
Expand Down Expand Up @@ -55,6 +56,11 @@ func Execute() {
}

func init() {
// Register all flags providers.
for pluginName, fp := range flagsprovider.Registry {
flagsprovider.FlagProviders[pluginName] = fp()
}

// Initial config.
rootCmd.PersistentFlags().StringSliceVarP(&config.Files, "file", "f",
[]string{"shipshape.yml"}, `Path to the file containing the checks.
Expand Down
80 changes: 14 additions & 66 deletions cmd/run.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package cmd

import (
"bufio"
"os"
"strconv"
"strings"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/salsadigitalauorg/shipshape/pkg/config"
"github.com/salsadigitalauorg/shipshape/pkg/lagoon"
"github.com/salsadigitalauorg/shipshape/pkg/flagsprovider"
"github.com/salsadigitalauorg/shipshape/pkg/output"
"github.com/salsadigitalauorg/shipshape/pkg/result"
"github.com/salsadigitalauorg/shipshape/pkg/shipshape"
)
Expand All @@ -32,35 +31,7 @@ var runCmd = &cobra.Command{
}
}

outputFormatEnv := os.Getenv("SHIPSHAPE_OUTPUT_FORMAT")
if outputFormatEnv != "" {
shipshape.OutputFormat = outputFormatEnv
}

lagoonApiBaseUrlEnv := os.Getenv("LAGOON_API_BASE_URL")
if lagoonApiBaseUrlEnv != "" {
lagoon.ApiBaseUrl = lagoonApiBaseUrlEnv
}

lagoonApiTokenEnv := os.Getenv("LAGOON_API_TOKEN")
if lagoonApiTokenEnv != "" {
lagoon.ApiToken = lagoonApiTokenEnv
}

if !shipshape.ValidateOutputFormat() {
log.Fatalf("Invalid output format; needs to be one of: %s.",
strings.Join(shipshape.OutputFormats, "|"))
}

// simple check to ensure we have everything we need to write to the API if required.
if lagoon.PushProblemsToInsightRemote {
if lagoon.ApiBaseUrl == "" {
log.Fatal("lagoon api base url not provided")
}
if lagoon.ApiToken == "" {
log.Fatal("lagoon api token not provided")
}
}
flagsprovider.ApplyEnvironmentOverridesAll()
},

Run: func(cmd *cobra.Command, args []string) {
Expand All @@ -75,25 +46,20 @@ var runCmd = &cobra.Command{

if shipshape.IsV2 {
shipshape.RunV2()

// If we're only collecting facts, we don't need to output anything,
// but not exit either, since it is then assumed this command was called
// from collectCmd.
if shipshape.FactsOnly {
return
}
} else {
shipshape.Run()
}

// If we're only collecting facts, we don't need to output anything,
// but not exit either, since it is then assumed this command was called
// from collectCmd.
if shipshape.FactsOnly {
return
}

shipshape.Output()

if lagoon.PushProblemsToInsightRemote {
w := bufio.NewWriter(os.Stdout)
err := lagoon.ProcessResultList(w, shipshape.RunResultList)
if err != nil {
log.Fatal(err)
}
log.Print("outputting results")
if err := output.OutputAll(&shipshape.RunResultList, os.Stdout); err != nil {
log.Fatal(err)
}

if shipshape.RunResultList.Status() == result.Fail &&
Expand All @@ -118,25 +84,7 @@ should exit with an error`)
"error-code", "e", false, `Exit with error code if a failure is
detected (env: SHIPSHAPE_ERROR_ON_FAILURE)`)

// Output.
runCmd.Flags().StringVarP(&shipshape.OutputFormat, "output",
"o", "simple", `Output format [json|junit|simple|table]
(env: SHIPSHAPE_OUTPUT_FORMAT)`)

// Lagoon.
runCmd.Flags().StringVar(&lagoon.ApiBaseUrl, "lagoon-api-base-url",
"", `Base url for the Lagoon API when pushing
problems to API (env: LAGOON_API_BASE_URL)`)
runCmd.Flags().StringVar(&lagoon.ApiToken, "lagoon-api-token", "",
`Lagoon API token when pushing problems
to API (env: LAGOON_API_TOKEN)`)
runCmd.Flags().BoolVar(&lagoon.PushProblemsToInsightRemote,
"lagoon-push-problems-to-insights", false,
"Push audit facts to Lagoon via Insights Remote")
runCmd.Flags().StringVar(&lagoon.LagoonInsightsRemoteEndpoint,
"lagoon-insights-remote-endpoint",
"http://lagoon-remote-insights-remote.lagoon.svc/problems",
"Insights Remote Problems endpoint\n")
flagsprovider.AddFlagsAll(runCmd)

rootCmd.AddCommand(runCmd)
}
8 changes: 8 additions & 0 deletions examples/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,11 @@ analyse:
deprecated: *deprecated-images
exclude-keys:
- test

output:
stdout:
format: pretty
lagoon:
source: shipshape:paas:docker
api-base-url: https://api.lagoon.amazeeio.cloud
api-token: ${LAGOON_API_TOKEN}
8 changes: 4 additions & 4 deletions pkg/analyse/allowedlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import (
"io"
"testing"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

. "github.com/salsadigitalauorg/shipshape/pkg/analyse"
"github.com/salsadigitalauorg/shipshape/pkg/breach"
"github.com/salsadigitalauorg/shipshape/pkg/data"
"github.com/salsadigitalauorg/shipshape/pkg/fact"
"github.com/salsadigitalauorg/shipshape/pkg/fact/testdata"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)

func TestAllowedListInit(t *testing.T) {

assert := assert.New(t)

// Test that the yaml:key plugin is registered.
// Test that the plugin is registered.
plugin := Registry["allowed:list"]("testAllowedList")
assert.NotNil(plugin)
analyser, ok := plugin.(*AllowedList)
Expand Down
3 changes: 2 additions & 1 deletion pkg/analyse/analyse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ func TestParseConfig(t *testing.T) {

t.Run(tc.name, func(t *testing.T) {
assert.Len(Analysers, 0)
registryBackup := Registry
if tc.registry != nil {
Registry = tc.registry
}
ParseConfig(tc.config)
defer func() {
Registry = map[string]func(string) Analyser{}
Registry = registryBackup
Analysers = map[string]Analyser{}
}()
assert.Len(Analysers, tc.expectAnalyserCount)
Expand Down
96 changes: 96 additions & 0 deletions pkg/analyse/notempty_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package analyse_test

import (
"io"
"testing"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

. "github.com/salsadigitalauorg/shipshape/pkg/analyse"
"github.com/salsadigitalauorg/shipshape/pkg/breach"
"github.com/salsadigitalauorg/shipshape/pkg/data"
"github.com/salsadigitalauorg/shipshape/pkg/fact"
"github.com/salsadigitalauorg/shipshape/pkg/fact/testdata"
)

func TestNotEmptyInit(t *testing.T) {
assert := assert.New(t)

// Test that the plugin is registered.
plugin := Registry["not:empty"]("testNotEmpty")
assert.NotNil(plugin)
analyser, ok := plugin.(*NotEmpty)
assert.True(ok)
assert.Equal("testNotEmpty", analyser.Id)
}

func TestNotEmptyPluginName(t *testing.T) {
instance := NotEmpty{Id: "testNotEmpty"}
assert.Equal(t, "not:empty", instance.PluginName())
}

func TestNotEmptyAnalyse(t *testing.T) {
tt := []struct {
name string
input fact.Facter
expectedBreaches []breach.Breach
}{
{
name: "mapNestedStringNil",
input: &testdata.TestFacter{
Name: "testFacter",
TestInputDataFormat: data.FormatMapNestedString,
TestInputData: map[string]map[string]string(nil),
},
expectedBreaches: []breach.Breach{},
},
{
name: "mapNestedStringEmpty",
input: &testdata.TestFacter{
Name: "testFacter",
TestInputDataFormat: data.FormatMapNestedString,
TestInputData: map[string]map[string]string{},
},
expectedBreaches: []breach.Breach{},
},
{
name: "mapNestedStringNotEmpty",
input: &testdata.TestFacter{
Name: "testFacter",
TestInputDataFormat: data.FormatMapNestedString,
TestInputData: map[string]map[string]string{
"key1": {"subKey1": "value1"},
},
},
expectedBreaches: []breach.Breach{
&breach.KeyValueBreach{
BreachType: "key-value",
CheckName: "mapNestedStringNotEmpty",
Key: "key1",
ValueLabel: "subKey1",
Value: "value1",
},
},
},
}

for _, tc := range tt {
assert := assert.New(t)

currLogOut := logrus.StandardLogger().Out
defer logrus.SetOutput(currLogOut)
logrus.SetOutput(io.Discard)

t.Run(tc.name, func(t *testing.T) {
analyser := NotEmpty{Id: tc.name}

tc.input.Collect()
analyser.SetInput(tc.input)
analyser.Analyse()

assert.Len(analyser.Result.Breaches, len(tc.expectedBreaches))
assert.ElementsMatch(tc.expectedBreaches, analyser.Result.Breaches)
})
}
}
Loading
Loading