diff --git a/.gitignore b/.gitignore index f44c08c..331c58f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ .idea -vendor -**.so -_glooe \ No newline at end of file +vendor \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 6a27b89..0000000 --- a/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# This stage is parametrized to replicate the same environment GlooE was built in. -# All ARGs need to be set via the docker `--build-arg` flags. -ARG GO_BUILD_IMAGE -FROM $GO_BUILD_IMAGE AS build-env - -ARG GC_FLAGS -ARG VERIFY_SCRIPT - -# Fail if VERIFY_SCRIPT not set -RUN if [[ ! $VERIFY_SCRIPT ]]; then echo "Required VERIFY_SCRIPT build argument not set" && exit 1; fi - -RUN apk add --no-cache gcc musl-dev - -ADD . /go/src/github.com/solo-io/ext-auth-plugins/ -WORKDIR /go/src/github.com/solo-io/ext-auth-plugins - -# De-vendor all the dependencies and move them to the GOPATH. -# We need this so that the import paths for any library shared between the plugins and Gloo are the same. -RUN cp -a vendor/. /go/src/ && rm -rf vendor - -# Build plugins with CGO enabled -RUN CGO_ENABLED=1 GOARCH=amd64 GOOS=linux go build -buildmode=plugin -gcflags="$GC_FLAGS" -o examples/RequiredHeader.so examples/required_header/plugin.go - -# Verify that plugins can be loaded by GlooE -RUN chmod +x $VERIFY_SCRIPT -RUN $VERIFY_SCRIPT -pluginDir examples -manifest examples/plugin_manifest.yaml - -# This stage builds the final image containing just the plugin .so files -FROM alpine:3.10.1 -RUN mkdir /compiled-auth-plugins -COPY --from=build-env /go/src/github.com/solo-io/ext-auth-plugins/examples/RequiredHeader.so /compiled-auth-plugins/ -CMD cp /compiled-auth-plugins/* /auth-plugins/ \ No newline at end of file diff --git a/Gopkg.lock b/Gopkg.lock index 946fdd2..dfa9101 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -118,56 +118,6 @@ revision = "65fb64232476ad9046e57c26cd0bff3d3a8dc6cd" version = "v1.4.3" -[[projects]] - digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "UT" - revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" - version = "v1.2.0" - -[[projects]] - digest = "1:2c5d346ada219cd2b445a72035122d3589d349b7e3389294a1873376b90ed13e" - name = "github.com/solo-io/go-utils" - packages = [ - "clicore/constants", - "contextutils", - ] - pruneopts = "UT" - revision = "71704af17a52b1dce01b6bb8bcab447f59e59f07" - version = "v0.9.17" - -[[projects]] - digest = "1:3c1a69cdae3501bf75e76d0d86dc6f2b0a7421bc205c0cb7b96b19eed464a34d" - name = "go.uber.org/atomic" - packages = ["."] - pruneopts = "UT" - revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289" - version = "v1.3.2" - -[[projects]] - digest = "1:60bf2a5e347af463c42ed31a493d817f8a72f102543060ed992754e689805d1a" - name = "go.uber.org/multierr" - packages = ["."] - pruneopts = "UT" - revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" - version = "v1.1.0" - -[[projects]] - digest = "1:676160e6a4722b08e0e26b11521d575c2cb2b6f0c679e1ee6178c5d8dee51e5e" - name = "go.uber.org/zap" - packages = [ - ".", - "buffer", - "internal/bufferpool", - "internal/color", - "internal/exit", - "zapcore", - ] - pruneopts = "UT" - revision = "27376062155ad36be76b0f12cf1572a221d3a48c" - version = "v1.10.0" - [[projects]] digest = "1:964072608f7bddc0cfe856b6872e5bd2314fe9288f7aeb679845f12be19d97dd" name = "golang.org/x/net" @@ -294,15 +244,11 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ - "github.com/envoyproxy/go-control-plane/envoy/api/v2/core", "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2", "github.com/envoyproxy/go-control-plane/envoy/type", "github.com/gogo/googleapis/google/rpc", "github.com/onsi/ginkgo", "github.com/onsi/gomega", - "github.com/pelletier/go-toml", - "github.com/solo-io/go-utils/contextutils", - "go.uber.org/zap", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 8ef6255..6dd8398 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -2,78 +2,6 @@ go-tests = true unused-packages = true -[[override]] - name = "gopkg.in/fsnotify.v1" - source = "https://github.com/fsnotify/fsnotify.git" - -[[constraint]] - name = "github.com/solo-io/go-utils" - version = "=v0.9.17" - [[constraint]] name = "github.com/envoyproxy/go-control-plane" - version = "=v0.8.2" - -[[override]] - name = "github.com/envoyproxy/protoc-gen-validate" - revision = "9b492f1473afa33350d86b62c298b45da868a33f" - -[[override]] - name = "go.uber.org/zap" - version = "=v1.10.0" - -[[override]] - name = "go.uber.org/atomic" - version = "=v1.3.2" - -[[override]] - name = "github.com/golang/protobuf" - version = "=v1.2.0" - -[[override]] - name = "gopkg.in/tomb.v1" - revision = "c131134a1947e9afd9cecfe11f4c6dff0732ae58" - -[[override]] - name = "github.com/hpcloud/tail" - revision = "a1dbeea552b7c8df4b542c66073e393de198a800" - -[[override]] - name = "github.com/onsi/ginkgo" - version = "=v1.7.0" - -[[override]] - name = "github.com/onsi/gomega" - version = "=v1.4.3" - -[[override]] - name = "gopkg.in/yaml.v2" - version = "=v2.2.1" - -[[override]] - name = "google.golang.org/grpc" - revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" - -[[override]] - name = "github.com/gogo/googleapis" - version = "=v1.0.0" - -[[override]] - name = "github.com/pelletier/go-toml" - version = "=v1.2.0" - -[[override]] - name = "google.golang.org/genproto" - revision = "02b4e95473316948020af0b7a4f0f22c73929b0e" - -[[override]] - name = "golang.org/x/sys" - revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4" - -[[override]] - name = "golang.org/x/net" - revision = "3f473d35a33aa6fdd203e306dc439b797820e3f1" - -[[override]] - name = "golang.org/x/text" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" \ No newline at end of file + version = "=v0.8.2" \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index a8131b7..0000000 --- a/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -GLOOE_VERSION := 0.18.12 -BUILD_ID := $(BUILD_ID) -RELEASE := "true" -ifeq ($(TAGGED_VERSION),) - TAGGED_VERSION := v$(BUILD_ID) - RELEASE := "false" -endif -VERSION ?= $(shell echo $(TAGGED_VERSION) | cut -c 2-) - -#---------------------------------------------------------------------------------- -# Retrieve GlooE build information -#---------------------------------------------------------------------------------- -GLOOE_DIR := _glooe -_ := $(shell mkdir -p $(GLOOE_DIR)) - -.PHONY: get-glooe-info -get-glooe-info: $(GLOOE_DIR)/Gopkg.lock $(GLOOE_DIR)/verify-plugins-linux-amd64 $(GLOOE_DIR)/build_env - -$(GLOOE_DIR)/Gopkg.lock: - curl -o $@ http://storage.googleapis.com/gloo-ee-dependencies/$(GLOOE_VERSION)/Gopkg.lock - -$(GLOOE_DIR)/verify-plugins-linux-amd64: - curl -o $@ http://storage.googleapis.com/gloo-ee-dependencies/$(GLOOE_VERSION)/verify-plugins-linux-amd64 - -$(GLOOE_DIR)/build_env: - curl -o $@ http://storage.googleapis.com/gloo-ee-dependencies/$(GLOOE_VERSION)/build_env - - -#---------------------------------------------------------------------------------- -# Compare dependencies against GlooE -#---------------------------------------------------------------------------------- - -.PHONY: compare-deps -compare-deps: Gopkg.lock $(GLOOE_DIR)/Gopkg.lock - go run scripts/compare_dependencies.go Gopkg.lock $(GLOOE_DIR)/Gopkg.lock - - -#---------------------------------------------------------------------------------- -# Build plugins -#---------------------------------------------------------------------------------- -EXAMPLES_DIR := examples -SOURCES := $(shell find . -name "*.go" | grep -v test) - -define get_glooe_var -$(shell grep $(1) $(GLOOE_DIR)/build_env | cut -d '=' -f 2-) -endef - -.PHONY: build-plugins -build-plugins: $(GLOOE_DIR)/build_env $(GLOOE_DIR)/verify-plugins-linux-amd64 - docker build --no-cache -t quay.io/solo-io/ext-auth-plugins:$(VERSION) \ - --build-arg GO_BUILD_IMAGE=$(call get_glooe_var,GO_BUILD_IMAGE) \ - --build-arg GC_FLAGS=$(call get_glooe_var,GC_FLAGS) \ - --build-arg VERIFY_SCRIPT=$(GLOOE_DIR)/verify-plugins-linux-amd64 \ - . - -.PHONY: build-plugins-for-tests -build-plugins-for-tests: $(EXAMPLES_DIR)/required_header/RequiredHeader.so - -$(EXAMPLES_DIR)/required_header/RequiredHeader.so: $(SOURCES) - go build -buildmode=plugin -o $(EXAMPLES_DIR)/required_header/RequiredHeader.so $(EXAMPLES_DIR)/required_header/plugin.go - - -#---------------------------------------------------------------------------------- -# Release plugins -#---------------------------------------------------------------------------------- - -.PHONY: release-plugins -release-plugins: build-plugins -ifeq ($(RELEASE),"true") - docker push quay.io/solo-io/ext-auth-plugins:$(VERSION) -else - @echo This is not a release build. Example plugins will not be published. -endif \ No newline at end of file diff --git a/README.md b/README.md index 555070b..49d7583 100644 --- a/README.md +++ b/README.md @@ -1 +1,6 @@ -# ext-auth-plugins \ No newline at end of file +# External auth plugins +This repository contains two public interfaces: +- `AuthService`: is the interface implemented by all [Gloo ext auth implementations](https://gloo.solo.io/gloo_routing/virtual_services/authentication/) +- `ExtAuthPlugin`: is the interface that needs to be implemented by custom go ext auth plugins + +Be sure to check out the docs at gloo.solo.io for more information! \ No newline at end of file diff --git a/api/api_suite_test.go b/api/api_suite_test.go new file mode 100644 index 0000000..62c09f9 --- /dev/null +++ b/api/api_suite_test.go @@ -0,0 +1,49 @@ +package api_test + +import ( + "context" + "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2" + "github.com/solo-io/ext-auth-plugins/api" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestApi(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Api Suite") +} + +var _ = Describe("api has no errors", func() { + + It("can compile everything", func() { + var pluginImpl api.ExtAuthPlugin = &pluginImpl{} + _, err := pluginImpl.NewConfigInstance(context.Background()) + Expect(err).NotTo(HaveOccurred()) + + svc, err := pluginImpl.GetAuthService(context.Background(), nil) + Expect(err).NotTo(HaveOccurred()) + Expect(svc).NotTo(BeNil()) + }) +}) + +type pluginImpl struct{} + +func (pluginImpl) NewConfigInstance(ctx context.Context) (configInstance interface{}, err error) { + return nil, nil +} + +func (pluginImpl) GetAuthService(ctx context.Context, configInstance interface{}) (api.AuthService, error) { + return &serviceImpl{}, nil +} + +type serviceImpl struct{} + +func (serviceImpl) Start(ctx context.Context) error { + return nil +} + +func (serviceImpl) Authorize(ctx context.Context, request *v2.CheckRequest) (*api.AuthorizationResponse, error) { + return nil, nil +} diff --git a/api/interface.go b/api/interface.go index 6b82891..9d5914c 100644 --- a/api/interface.go +++ b/api/interface.go @@ -58,11 +58,12 @@ type ExtAuthPlugin interface { // configs: // extauth: // plugin_auth: - // name: MyAuthPlugin - // config: - // some_key: value-1 - // some_struct: - // another_key: value-2 + // plugins: + // - name: MyAuthPlugin + // config: + // some_key: value-1 + // some_struct: + // another_key: value-2 // // the `NewConfigInstance` function on your `ExtAuthPlugin` implementation should return a pointer to // the following Go struct: diff --git a/changelog/v0.0.3/verify-plugins-script.yaml b/changelog/v0.0.3/verify-plugins-script.yaml new file mode 100644 index 0000000..6e0270b --- /dev/null +++ b/changelog/v0.0.3/verify-plugins-script.yaml @@ -0,0 +1,5 @@ +changelog: +- type: NON_USER_FACING + description: > + Move example plugins and build tools to [new repo](https://github.com/solo-io/ext-auth-plugin-examples). + This is needed to avoid circular dependencies between GlooE and this repo. \ No newline at end of file diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 97e393e..47933e3 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -8,63 +8,12 @@ steps: env: - 'PROJECT_ROOT=github.com/solo-io/ext-auth-plugins' -- name: 'gcr.io/$PROJECT_ID/go-make:0.1.12' - id: 'compare-dependencies' - args: ['compare-deps'] - env: - - 'PROJECT_ROOT=github.com/solo-io/ext-auth-plugins' - - 'GOPATH=/workspace/gopath' - - 'TAGGED_VERSION=$TAG_NAME' - - 'BUILD_ID=$BUILD_ID' - dir: './gopath/src/github.com/solo-io/ext-auth-plugins' - -- name: 'gcr.io/$PROJECT_ID/go-make:0.1.12' - id: 'build-plugins-for-tests' - args: ['build-plugins-for-tests'] - env: - - 'PROJECT_ROOT=github.com/solo-io/ext-auth-plugins' - - 'GOPATH=/workspace/gopath' - - 'TAGGED_VERSION=$TAG_NAME' - - 'BUILD_ID=$BUILD_ID' - dir: './gopath/src/github.com/solo-io/ext-auth-plugins' - - name: 'gcr.io/$PROJECT_ID/ginkgo:0.1.12' - id: 'test-plugins' + id: 'test' args: ['-r'] env: - 'PROJECT_ROOT=github.com/solo-io/ext-auth-plugins' - 'GOPATH=/workspace/gopath' - 'TAGGED_VERSION=$TAG_NAME' - 'BUILD_ID=$BUILD_ID' - dir: './gopath/src/github.com/solo-io/ext-auth-plugins' - -- name: 'gcr.io/$PROJECT_ID/go-make:0.1.12' - id: 'build-plugins' - args: ['build-plugins'] - env: - - 'PROJECT_ROOT=github.com/solo-io/ext-auth-plugins' - - 'GOPATH=/workspace/gopath' - - 'TAGGED_VERSION=$TAG_NAME' - - 'BUILD_ID=$BUILD_ID' - dir: './gopath/src/github.com/solo-io/ext-auth-plugins' - -- name: 'gcr.io/cloud-builders/docker' - entrypoint: 'bash' - args: ['-c', 'docker login quay.io --username "solo-io+solobot" --password $$QUAY_IO_PASSWORD'] - secretEnv: ['QUAY_IO_PASSWORD'] - id: 'docker-login' - -- name: 'gcr.io/$PROJECT_ID/go-make:0.1.12' - args: ['release-plugins'] - env: - - 'PROJECT_ROOT=github.com/solo-io/ext-auth-plugins' - - 'GOPATH=/workspace/gopath' - - 'TAGGED_VERSION=$TAG_NAME' - - 'BUILD_ID=$BUILD_ID' - dir: './gopath/src/github.com/solo-io/ext-auth-plugins' - id: 'release-plugins' - -secrets: -- kmsKeyName: projects/solo-public/locations/global/keyRings/build/cryptoKeys/build-key - secretEnv: - QUAY_IO_PASSWORD: CiQABlzmSRx5TcOqbldXa/d/+bkmAfpNAWa3PTS06WvuloZL+vASaQCCPGSGCogonVZVEUNx4G3YJtWi18gSuNx4PvLe08q8xAflTMFkjsyQirAOK3Y2oCvgYwiw/ITcuydjkpMjxDygFyENXS9FKFJoAXHlPQE5qidKr8xxmxF5ezhmjGB0gjyjXIIkbSEnBg== \ No newline at end of file + dir: './gopath/src/github.com/solo-io/ext-auth-plugins' \ No newline at end of file diff --git a/examples/plugin_manifest.yaml b/examples/plugin_manifest.yaml deleted file mode 100644 index 581e721..0000000 --- a/examples/plugin_manifest.yaml +++ /dev/null @@ -1,10 +0,0 @@ -plugins: -- name: RequiredHeader - pluginFileName: RequiredHeader.so - exportedSymbolName: Plugin - config: - RequiredHeader: my-auth-header - AllowedValues: - - foo - - bar - - baz \ No newline at end of file diff --git a/examples/required_header/pkg/impl.go b/examples/required_header/pkg/impl.go deleted file mode 100644 index ec5d063..0000000 --- a/examples/required_header/pkg/impl.go +++ /dev/null @@ -1,97 +0,0 @@ -package pkg - -import ( - "context" - "errors" - "fmt" - envoycorev2 "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" - envoyauthv2 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2" - "github.com/solo-io/ext-auth-plugins/api" - "github.com/solo-io/go-utils/contextutils" - "go.uber.org/zap" -) - -var ( - UnexpectedConfigError = func(typ interface{}) error { - return errors.New(fmt.Sprintf("unexpected config type %T", typ)) - } - _ api.ExtAuthPlugin = new(RequiredHeaderPlugin) -) - -type RequiredHeaderPlugin struct{} - -type Config struct { - RequiredHeader string - AllowedValues []string -} - -func (p *RequiredHeaderPlugin) NewConfigInstance(ctx context.Context) (interface{}, error) { - return &Config{}, nil -} - -func (p *RequiredHeaderPlugin) GetAuthService(ctx context.Context, configInstance interface{}) (api.AuthService, error) { - config, ok := configInstance.(*Config) - if !ok { - return nil, UnexpectedConfigError(configInstance) - } - - logger(ctx).Infow("Parsed RequiredHeaderAuthService config", - zap.Any("requiredHeader", config.RequiredHeader), - zap.Any("allowedHeaderValues", config.AllowedValues), - ) - - valueMap := map[string]bool{} - for _, v := range config.AllowedValues { - valueMap[v] = true - } - - return &RequiredHeaderAuthService{ - RequiredHeader: config.RequiredHeader, - AllowedValues: valueMap, - }, nil -} - -type RequiredHeaderAuthService struct { - RequiredHeader string - AllowedValues map[string]bool -} - -// You can use the provided context to perform operations that are bound to the services lifecycle. -func (c *RequiredHeaderAuthService) Start(context.Context) error { - // no-op - return nil -} - -func (c *RequiredHeaderAuthService) Authorize(ctx context.Context, request *envoyauthv2.CheckRequest) (*api.AuthorizationResponse, error) { - for key, value := range request.Attributes.Request.Http.Headers { - if key == c.RequiredHeader { - logger(ctx).Infow("Found required header, checking value.", "header", key, "value", value) - - if _, ok := c.AllowedValues[value]; ok { - logger(ctx).Infow("Header value match. Allowing request.") - response := api.AuthorizedResponse() - - // Append extra header - response.CheckResponse.HttpResponse = &envoyauthv2.CheckResponse_OkResponse{ - OkResponse: &envoyauthv2.OkHttpResponse{ - Headers: []*envoycorev2.HeaderValueOption{{ - Header: &envoycorev2.HeaderValue{ - Key: "matched-allowed-headers", - Value: "true", - }, - }}, - }, - } - return response, nil - } - logger(ctx).Infow("Header value does not match allowed values, denying access.") - return api.UnauthorizedResponse(), nil - } - } - logger(ctx).Infow("Required header not found, denying access") - return api.UnauthorizedResponse(), nil -} - -func logger(ctx context.Context) *zap.SugaredLogger { - return contextutils.LoggerFrom(contextutils.WithLogger(ctx, "header_value_plugin")) -} diff --git a/examples/required_header/plugin.go b/examples/required_header/plugin.go deleted file mode 100644 index c76900c..0000000 --- a/examples/required_header/plugin.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "github.com/solo-io/ext-auth-plugins/api" - impl "github.com/solo-io/ext-auth-plugins/examples/required_header/pkg" -) - -func main() {} - -// Compile-time assertion -var _ api.ExtAuthPlugin = new(impl.RequiredHeaderPlugin) - -// This is the exported symbol that GlooE will look for. -//noinspection GoUnusedGlobalVariable -var Plugin impl.RequiredHeaderPlugin diff --git a/examples/required_header/plugin_test.go b/examples/required_header/plugin_test.go deleted file mode 100644 index c0413c3..0000000 --- a/examples/required_header/plugin_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "context" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/solo-io/ext-auth-plugins/api" - impl "github.com/solo-io/ext-auth-plugins/examples/required_header/pkg" - "plugin" -) - -var _ = Describe("Plugin", func() { - - It("can be loaded", func() { - - goPlugin, err := plugin.Open("RequiredHeader.so") - Expect(err).NotTo(HaveOccurred()) - - pluginStructPtr, err := goPlugin.Lookup("Plugin") - Expect(err).NotTo(HaveOccurred()) - - extAuthPlugin, ok := pluginStructPtr.(api.ExtAuthPlugin) - Expect(ok).To(BeTrue()) - - instance, err := extAuthPlugin.NewConfigInstance(context.TODO()) - Expect(err).NotTo(HaveOccurred()) - - typedInstance, ok := instance.(*impl.Config) - Expect(ok).To(BeTrue()) - - Expect(typedInstance.RequiredHeader).To(BeEmpty()) - Expect(typedInstance.AllowedValues).To(BeEmpty()) - }) -}) diff --git a/examples/required_header/required_header_suite_test.go b/examples/required_header/required_header_suite_test.go deleted file mode 100644 index 21c2e57..0000000 --- a/examples/required_header/required_header_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package main_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestHeader(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "RequiredHeader Suite") -} diff --git a/scripts/compare_dependencies.go b/scripts/compare_dependencies.go deleted file mode 100644 index b2b5f0b..0000000 --- a/scripts/compare_dependencies.go +++ /dev/null @@ -1,162 +0,0 @@ -package main - -import ( - "encoding/json" - "errors" - "fmt" - "github.com/pelletier/go-toml" - "io/ioutil" - "os" -) - -const errorReportFile = "mismatched_dependencies.json" - -type depProjects map[string]dependencyInfo - -// `Revision` is always present on a project stanza. Additionally, either `branch` or `version` can be present. -// Check the [dep docs](https://golang.github.io/dep/docs/Gopkg.lock.html#projects) for full reference. -type dependencyInfo struct { - Name string `json:"name"` - Version string `json:"version,omitempty"` - Revision string `json:"revision"` - Branch string `json:"branch,omitempty"` -} - -type dependencyInfoPair struct { - Plugin dependencyInfo `json:"pluginDependencies"` - GlooE dependencyInfo `json:"glooeDependencies"` -} - -func (d dependencyInfo) matches(that dependencyInfo) bool { - // `Revision` is the ultimate source of truth, `version` or `branch` are potentially floating references - if d.Revision == that.Revision { - return true - } - return false -} - -func main() { - - if len(os.Args) != 3 { - fmt.Printf("Must provide 2 arguments: \n\t- plugin Gopkg.lock file path \n\t- Glooe Gopkg.lock file path\n") - os.Exit(1) - } - - pluginDepLockFile := os.Args[1] - glooeDepLockFile := os.Args[2] - - if err := checkFile(pluginDepLockFile); err != nil { - fmt.Printf("%s\n", err.Error()) - os.Exit(1) - } - - if err := checkFile(glooeDepLockFile); err != nil { - fmt.Printf("%s\n", err.Error()) - os.Exit(1) - } - - pluginDependencies, err := getDependencies(pluginDepLockFile) - if err != nil { - fmt.Printf("Failed to get plugin dependencies: %s/n", err.Error()) - os.Exit(1) - } - glooeDependencies, err := getDependencies(glooeDepLockFile) - if err != nil { - fmt.Printf("Failed to get GlooE dependencies: %s/n", err.Error()) - os.Exit(1) - } - - var nonMatchingDeps []dependencyInfoPair - for name, depInfo := range pluginDependencies { - - // Just check libraries that are shared with GlooE - if glooeEquivalent, ok := glooeDependencies[name]; ok { - if !glooeEquivalent.matches(depInfo) { - nonMatchingDeps = append(nonMatchingDeps, dependencyInfoPair{ - Plugin: depInfo, - GlooE: glooeEquivalent, - }) - } - } - } - - if len(nonMatchingDeps) == 0 { - fmt.Println("All shared dependencies match") - os.Exit(0) - } - - fmt.Printf("Plugin and GlooE dependencies do not match!\n") - - reportBytes, err := json.MarshalIndent(nonMatchingDeps, "", " ") - if err != nil { - fmt.Printf("Failed to marshall error report to JSON: %s/n", err.Error()) - os.Exit(1) - } - - fmt.Println(string(reportBytes)) - - fmt.Printf("Writing error report file [%s]\n", errorReportFile) - if err := ioutil.WriteFile(errorReportFile, reportBytes, 0644); err != nil { - fmt.Printf("Failed to write error report file: %s/n", err.Error()) - } - os.Exit(1) -} - -func getDependencies(depLockFilePath string) (depProjects, error) { - pluginDependencies, err := parseGoPkgLock(depLockFilePath) - if err != nil { - return nil, err - } - return collectDependencyInfo(pluginDependencies), nil -} - -func parseGoPkgLock(path string) ([]*toml.Tree, error) { - config, err := toml.LoadFile(path) - if err != nil { - return nil, errors.New(fmt.Sprintf("failed to parse %s file: %s", path, err.Error())) - } - - tomlTree := config.Get("projects") - - switch typedTree := tomlTree.(type) { - case []*toml.Tree: - return typedTree, nil - default: - return nil, fmt.Errorf("unable to parse toml tree") - } -} - -func collectDependencyInfo(deps []*toml.Tree) depProjects { - dependencies := make(depProjects) - - for _, t := range deps { - - name := t.Get("name").(string) - - info := dependencyInfo{ - Name: name, - Revision: t.Get("revision").(string), - } - - if version, ok := t.Get("version").(string); ok { - info.Version = version - } - if branch, ok := t.Get("branch").(string); ok { - info.Branch = branch - } - - dependencies[name] = info - } - return dependencies -} - -func checkFile(filename string) error { - info, err := os.Stat(filename) - if os.IsNotExist(err) { - return errors.New(filename + " file not found") - } - if info.IsDir() { - return errors.New(filename + " is a directory") - } - return nil -}