Skip to content

Commit

Permalink
feat(api): add grpc plugin action handlers (#3308)
Browse files Browse the repository at this point in the history
  • Loading branch information
bnjjj authored and yesnault committed Sep 13, 2018
1 parent c75c514 commit be2cee5
Show file tree
Hide file tree
Showing 94 changed files with 5,880 additions and 115 deletions.
35 changes: 33 additions & 2 deletions cli/cdsctl/admin_grpc_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (

"github.com/ovh/cds/cli"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/exportentities"
"github.com/ovh/cds/sdk/grpcplugin"
)

var (
Expand All @@ -26,6 +28,7 @@ var (
cli.NewCommand(adminPluginsExportCmd, adminPluginsExportFunc, nil),
cli.NewDeleteCommand(adminPluginsDeleteCmd, adminPluginsDeleteFunc, nil),
cli.NewCommand(adminPluginsAddBinaryCmd, adminPluginsAddBinaryFunc, nil),
cli.NewCommand(adminPluginsDocCmd, adminPluginsDocFunc, nil),
},
)
)
Expand Down Expand Up @@ -59,11 +62,12 @@ func adminPluginsImportFunc(v cli.Values) error {
return fmt.Errorf("unable to read file %s: %v", v.GetString("file"), err)
}

m := new(sdk.GRPCPlugin)
if err := yaml.Unmarshal(b, m); err != nil {
var expGPRCPlugin exportentities.GRPCPlugin
if err := yaml.Unmarshal(b, &expGPRCPlugin); err != nil {
return fmt.Errorf("unable to load file: %v", err)
}

m := expGPRCPlugin.GRPCPlugin()
existing, err := client.PluginsGet(m.Name)
if err != nil && !sdk.ErrorIs(err, sdk.ErrNotFound) {
return fmt.Errorf("unable to get plugin: %v", err)
Expand Down Expand Up @@ -191,3 +195,30 @@ func adminPluginsAddBinaryFunc(v cli.Values) error {

return nil
}

var adminPluginsDocCmd = cli.Command{
Name: "doc",
Short: "Generate documentation in markdown for a plugin",
Args: []cli.Arg{
{
Name: "path",
},
},
}

func adminPluginsDocFunc(v cli.Values) error {
btes, errRead := ioutil.ReadFile(v.GetString("path"))
if errRead != nil {
return fmt.Errorf("Error while reading file: %s", errRead)
}

var expGPRCPlugin exportentities.GRPCPlugin
if err := yaml.Unmarshal(btes, &expGPRCPlugin); err != nil {
return fmt.Errorf("unable to load file: %v", err)
}

plg := expGPRCPlugin.GRPCPlugin()
fmt.Println(grpcplugin.InfoMarkdown(*plg))

return nil
}
2 changes: 1 addition & 1 deletion cli/cdsctl/admin_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var adminPluginsActionAddBinaryCmd = cli.Command{

var adminPluginsActionUpdateBinaryCmd = cli.Command{
Name: "binary-update",
Short: "Add a binary",
Short: "Update a binary",
Args: []cli.Arg{
{
Name: "filename",
Expand Down
3 changes: 3 additions & 0 deletions contrib/grpcplugins/action/clair/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
clair.exe
clair
dist/
57 changes: 57 additions & 0 deletions contrib/grpcplugins/action/clair/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.PHONY: clean

VERSION := $(if ${CDS_SEMVER},${CDS_SEMVER},snapshot)

TARGET_DIR = ./dist
TARGET_NAME = clair

define PLUGIN_MANIFEST_BINARY
os: %os%
arch: %arch%
cmd: ./%filename%
endef
export PLUGIN_MANIFEST_BINARY

TARGET_LDFLAGS = -ldflags "-X github.com/ovh/cds/sdk.VERSION=$(VERSION)"
TARGET_OS = $(if ${OS},${OS},windows darwin linux freebsd)
TARGET_ARCH = $(if ${ARCH},${ARCH},amd64 arm 386)

GO_BUILD = go build -v


$(TARGET_DIR):
$(info create $(TARGET_DIR) directory)
@mkdir -p $(TARGET_DIR)

default: build

clean:
@rm -rf $(TARGET_DIR)

build: $(TARGET_DIR)
@cp $(TARGET_NAME).yml $(TARGET_DIR)/plugin.yml
@for GOOS in $(TARGET_OS); do \
for GOARCH in $(TARGET_ARCH); do \
EXTENSION=""; \
if test "$$GOOS" = "windows" ; then EXTENSION=".exe"; fi; \
echo Compiling $(TARGET_DIR)/$(TARGET_NAME)-$$GOOS-$$GOARCH$$EXTENSION $(VERSION); \
FILENAME=$(TARGET_NAME)-$$GOOS-$$GOARCH$$EXTENSION; \
GOOS=$$GOOS GOARCH=$$GOARCH $(GO_BUILD) $(TARGET_LDFLAGS) -o $(TARGET_DIR)/$$FILENAME; \
echo "$$PLUGIN_MANIFEST_BINARY" > $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml; \
sed -i "" "s/%os%/$$GOOS/" $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml; \
sed -i "" "s/%arch%/$$GOARCH/" $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml; \
sed -i "" "s/%filename%/$$FILENAME/" $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml; \
done; \
done

publish:
@echo "Updating plugin..."
cdsctl admin plugins import $(TARGET_DIR)/plugin.yml
@for GOOS in $(TARGET_OS); do \
for GOARCH in $(TARGET_ARCH); do \
EXTENSION=""; \
if test "$$GOOS" = "windows" ; then EXTENSION=".exe"; fi; \
echo "Updating plugin binary $(TARGET_NAME)-$$GOOS-$$GOARCH$$EXTENSION"; \
cdsctl admin plugins binary-add plugin-clair $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml $(TARGET_DIR)/$(TARGET_NAME)-$$GOOS-$$GOARCH$$EXTENSION; \
done; \
done
8 changes: 8 additions & 0 deletions contrib/grpcplugins/action/clair/clair.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: plugin-clair
type: action
author: "Steven GUIHEUX <steven.guiheux@corp.ovh.com>"
description: This is a plugin to run clair analysis
parameters:
image:
type: string
description: Image to analyze
139 changes: 139 additions & 0 deletions contrib/grpcplugins/action/clair/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package main

import (
"context"
"fmt"

"github.com/docker/distribution"
"github.com/docker/distribution/reference"
"github.com/golang/protobuf/ptypes/empty"
"github.com/jgsqware/clairctl/clair"
"github.com/jgsqware/clairctl/config"
"github.com/jgsqware/clairctl/docker"
"github.com/spf13/viper"

"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/grpcplugin/actionplugin"
"github.com/ovh/cds/sdk/plugin"
)

/*
$ make build
$ make publish
*/

type clairActionPlugin struct {
actionplugin.Common
}

func (actPlugin *clairActionPlugin) Manifest(ctx context.Context, _ *empty.Empty) (*actionplugin.ActionPluginManifest, error) {
return &actionplugin.ActionPluginManifest{
Name: "plugin-clair",
Author: "Steven GUIHEUX <steven.guiheux@corp.ovh.com>",
Description: `This is a plugin to run clair analysis`,
Version: sdk.VERSION,
}, nil
}

func (actPlugin *clairActionPlugin) Run(ctx context.Context, q *actionplugin.ActionQuery) (*actionplugin.ActionResult, error) {
// get clair configuration
fmt.Printf("Retrieve clair configuration...")
serv, err := plugin.GetExternalServices(actPlugin.HTTPPort, "clair")
if err != nil {
return fail("Unable to get clair configuration: %s", err)
}
viper.Set("clair.uri", serv.URL)
viper.Set("clair.port", serv.Port)
viper.Set("clair.healthPort", serv.HealthPort)
viper.Set("clair.report.path", ".")
viper.Set("clair.report.format", "json")
clair.Config()

dockerImage := q.GetOptions()["image"]

fmt.Printf("Pushing image %s into clair", dockerImage)
image, manifest, err := pushImage(dockerImage)
if err != nil {
return fail("Unable to push image on Clair: %s", err)
}

fmt.Printf("Running analysis")
analysis := clair.Analyze(image, manifest)

fmt.Printf("Creating report")

var vulnerabilities []sdk.Vulnerability
summary := make(map[string]int64)
if analysis.MostRecentLayer().Layer != nil {
for _, feat := range analysis.MostRecentLayer().Layer.Features {
for _, vuln := range feat.Vulnerabilities {
v := sdk.Vulnerability{
Version: feat.Version,
Component: feat.Name,
Description: vuln.Description,
Link: vuln.Link,
FixIn: vuln.FixedBy,
Severity: sdk.ToVulnerabilitySeverity(vuln.Severity),
Origin: feat.AddedBy,
CVE: vuln.Name,
Title: fmt.Sprintf("%s %s", feat.Name, feat.Version),
}
vulnerabilities = append(vulnerabilities, v)

count := summary[v.Severity]
summary[v.Severity] = count + 1

}
}
}

report := sdk.VulnerabilityWorkerReport{
Vulnerabilities: vulnerabilities,
Summary: summary,
Type: "docker",
}
if err := plugin.SendVulnerabilityReport(actPlugin.HTTPPort, report); err != nil {
return fail("Unable to send report: %s", err)
}
return &actionplugin.ActionResult{
Status: sdk.StatusSuccess.String(),
}, nil
}

func (actPlugin *clairActionPlugin) WorkerHTTPPort(ctx context.Context, q *actionplugin.WorkerHTTPPortQuery) (*empty.Empty, error) {
actPlugin.HTTPPort = q.Port
return &empty.Empty{}, nil
}

func main() {
actPlugin := clairActionPlugin{}
if err := actionplugin.Start(context.Background(), &actPlugin); err != nil {
panic(err)
}
return

}

func fail(format string, args ...interface{}) (*actionplugin.ActionResult, error) {
msg := fmt.Sprintf(format, args...)
fmt.Println(msg)
return &actionplugin.ActionResult{
Details: msg,
Status: sdk.StatusFail.String(),
}, nil
}

func pushImage(dockerImage string) (reference.NamedTagged, distribution.Manifest, error) {
config.ImageName = dockerImage
image, manifest, err := docker.RetrieveManifest(config.ImageName, true)
if err != nil {
return image, manifest, fmt.Errorf("pushImage> unable to retrieve manifest: %v", err)
}

if err := clair.Push(image, manifest); err != nil {
if err != nil {
return image, manifest, fmt.Errorf("pushImage> unable to push image %q: %v", image.String(), err)
}
}
return image, manifest, nil
}
3 changes: 3 additions & 0 deletions contrib/grpcplugins/action/download/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
download
download.exe
dist/
57 changes: 57 additions & 0 deletions contrib/grpcplugins/action/download/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.PHONY: clean

VERSION := $(if ${CDS_SEMVER},${CDS_SEMVER},snapshot)

TARGET_DIR = ./dist
TARGET_NAME = download

define PLUGIN_MANIFEST_BINARY
os: %os%
arch: %arch%
cmd: ./%filename%
endef
export PLUGIN_MANIFEST_BINARY

TARGET_LDFLAGS = -ldflags "-X github.com/ovh/cds/sdk.VERSION=$(VERSION)"
TARGET_OS = $(if ${OS},${OS},windows darwin linux freebsd)
TARGET_ARCH = $(if ${ARCH},${ARCH},amd64 arm 386)

GO_BUILD = go build -v


$(TARGET_DIR):
$(info create $(TARGET_DIR) directory)
@mkdir -p $(TARGET_DIR)

default: build

clean:
@rm -rf $(TARGET_DIR)

build: $(TARGET_DIR)
@cp $(TARGET_NAME).yml $(TARGET_DIR)/plugin.yml
@for GOOS in $(TARGET_OS); do \
for GOARCH in $(TARGET_ARCH); do \
EXTENSION=""; \
if test "$$GOOS" = "windows" ; then EXTENSION=".exe"; fi; \
echo Compiling $(TARGET_DIR)/$(TARGET_NAME)-$$GOOS-$$GOARCH$$EXTENSION $(VERSION); \
FILENAME=$(TARGET_NAME)-$$GOOS-$$GOARCH$$EXTENSION; \
GOOS=$$GOOS GOARCH=$$GOARCH $(GO_BUILD) $(TARGET_LDFLAGS) -o $(TARGET_DIR)/$$FILENAME; \
echo "$$PLUGIN_MANIFEST_BINARY" > $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml; \
sed -i "" "s/%os%/$$GOOS/" $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml; \
sed -i "" "s/%arch%/$$GOARCH/" $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml; \
sed -i "" "s/%filename%/$$FILENAME/" $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml; \
done; \
done

publish:
@echo "Updating plugin..."
cdsctl admin plugins import $(TARGET_DIR)/plugin.yml
@for GOOS in $(TARGET_OS); do \
for GOARCH in $(TARGET_ARCH); do \
EXTENSION=""; \
if test "$$GOOS" = "windows" ; then EXTENSION=".exe"; fi; \
echo "Updating plugin binary $(TARGET_NAME)-$$GOOS-$$GOARCH$$EXTENSION"; \
cdsctl admin plugins binary-add plugin-download $(TARGET_DIR)/plugin-$$GOOS-$$GOARCH.yml $(TARGET_DIR)/$(TARGET_NAME)-$$GOOS-$$GOARCH$$EXTENSION; \
done; \
done
16 changes: 16 additions & 0 deletions contrib/grpcplugins/action/download/download.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: plugin-download
type: action
author: Benjamin COENEN <benjamin.coenen@corp.ovh.com>
description: This is a plugin to download file from URL
parameters:
url:
type: string
description: The url of your file
default: '{{.cds.app.downloadUrl}}'
filepath:
type: string
description: The destination of your file to be copied
default: '.'
headers:
type: text
description: Specific headers to add to your request ("headerName"="value" newline separated list)
Loading

0 comments on commit be2cee5

Please sign in to comment.