Skip to content

Commit

Permalink
feat(prvalidation): PR validation for dinghyfiles (#101)
Browse files Browse the repository at this point in the history
* feat(prvalidation): PR validation for dinghyfiles
  • Loading branch information
Jossuecito authored Jul 23, 2020
1 parent 8c1035b commit 1dde8b9
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 28 deletions.
7 changes: 6 additions & 1 deletion cmd/dinghy.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func Setup() (*logr.Logger, *web.WebAPI) {
}

client := setupPlankClient(config, log)
clientReadOnly := util.PlankReadOnly{Plank: client}

// Create the EventClient
ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -105,7 +106,11 @@ func Setup() (*logr.Logger, *web.WebAPI) {
if _, err := redisClient.Client.Ping().Result(); err != nil {
log.Fatalf("Redis Server at %s could not be contacted: %v", config.Redis.BaseURL, err)
}
api := web.NewWebAPI(config, redisClient, client, ec, log)
redisClientReadOnly := cache.RedisCacheReadOnly{
Client: redisClient.Client,
Logger: redisClient.Logger,
}
api := web.NewWebAPI(config, redisClient, client, ec, log, &redisClientReadOnly, clientReadOnly)
api.AddDinghyfileUnmarshaller(&dinghyfile.DinghyJsonUnmarshaller{})
if config.ParserFormat == "json" {
api.SetDinghyfileParser(dinghyfile.NewDinghyfileParser(&dinghyfile.PipelineBuilder{}))
Expand Down
12 changes: 10 additions & 2 deletions pkg/cache/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ func (c *RedisCache) SetDeps(parent string, deps []string) {

// GetRoots grabs roots
func (c *RedisCache) GetRoots(url string) []string {
return returnRoots(c.Client, url)
}

func returnRoots (c *redis.Client, url string) []string {
roots := make([]string, 0)
visited := map[string]bool{}
loge := log.WithFields(log.Fields{"func": "GetRoots"})
Expand All @@ -151,7 +155,7 @@ func (c *RedisCache) GetRoots(url string) []string {
visited[curr] = true

key := compileKey("parents", curr)
parents, err := c.Client.SMembers(key).Result()
parents, err := c.SMembers(key).Result()
if err != nil {
loge.WithFields(log.Fields{"operation": "parents", "key": key}).Error(err)
break
Expand Down Expand Up @@ -186,10 +190,14 @@ func (c *RedisCache) SetRawData(url string, rawData string) error{
}

func (c *RedisCache) GetRawData(url string) (string, error) {
return returnRawData(c.Client, url)
}

func returnRawData(c *redis.Client, url string) (string, error) {
loge := log.WithFields(log.Fields{"func": "GetRawData"})
key := compileKey("rawdata", url)

stringCmd := c.Client.Get(key)
stringCmd := c.Get(key)
if stringCmd.Err() != nil {
loge.WithFields(log.Fields{"operation": "get value", "key": key}).Error(stringCmd.Err())
return "", stringCmd.Err()
Expand Down
56 changes: 56 additions & 0 deletions pkg/cache/redis_readonly.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2020 Armory, Inc.
* 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 cache

import (
"context"
"github.com/go-redis/redis"
log "github.com/sirupsen/logrus"
"os"
)

// RedisCacheReadOnly maintains a dependency graph inside Redis
type RedisCacheReadOnly struct {
Client *redis.Client
Logger *log.Entry
ctx context.Context
stop chan os.Signal
}


// SetDeps sets dependencies for a parent
func (c *RedisCacheReadOnly) SetDeps(parent string, deps []string) {

}

// GetRoots grabs roots
func (c *RedisCacheReadOnly) GetRoots(url string) []string {
return returnRoots(c.Client, url)
}

// Set RawData
func (c *RedisCacheReadOnly) SetRawData(url string, rawData string) error{
return nil
}

func (c *RedisCacheReadOnly) GetRawData(url string) (string, error) {
return returnRawData(c.Client, url)
}

// Clear clears everything
func (c *RedisCacheReadOnly) Clear() {
}
24 changes: 18 additions & 6 deletions pkg/dinghyfile/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/armory/dinghy/pkg/dinghyfile/pipebuilder"
"path/filepath"
"regexp"
"time"
Expand Down Expand Up @@ -58,6 +59,7 @@ type PipelineBuilder struct {
GlobalVariablesMap map[string]interface{}
RepositoryRawdataProcessing bool
RebuildingModules bool
Action pipebuilder.BuilderAction
}

// DependencyManager is an interface for assigning dependencies and looking up root nodes
Expand Down Expand Up @@ -264,10 +266,14 @@ func (b *PipelineBuilder) ProcessDinghyfile(org, repo, path, branch string) erro
}
b.Logger.Info("Validations for app notifications were successful")

if err := b.updatePipelines(&d.ApplicationSpec, d.Pipelines, d.DeleteStalePipelines, b.AutolockPipelines); err != nil {
b.Logger.Errorf("Failed to update Pipelines for %s: %s", path, err.Error())
b.NotifyFailure(org, repo, path, err, buf.String() )
return err
if b.Action == pipebuilder.Validate {
b.Logger.Info("Validation finished successfully")
} else {
if err := b.updatePipelines(&d.ApplicationSpec, d.Pipelines, d.DeleteStalePipelines, b.AutolockPipelines); err != nil {
b.Logger.Errorf("Failed to update Pipelines for %s: %s", path, err.Error())
b.NotifyFailure(org, repo, path, err, buf.String() )
return err
}
}

b.NotifySuccess(org, repo, path, d.ApplicationSpec.Notifications)
Expand Down Expand Up @@ -338,11 +344,17 @@ func (b *PipelineBuilder) RebuildModuleRoots(org, repo, path, branch string) err
}

if errEncountered {
b.Logger.Error("The following dinghyfiles weren't updated successfully:")
var word string
if b.Action == pipebuilder.Validate {
word = "validated"
} else {
word = "updated"
}
b.Logger.Errorf("The following dinghyfiles weren't %v successfully:", word)
for _, url := range failedUpdates {
b.Logger.Error(url)
}
return errors.New("Not all upstream dinghyfiles were updated successfully")
return fmt.Errorf("Not all upstream dinghyfiles were %v successfully", word)
}
return nil
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/dinghyfile/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package dinghyfile
import (
"bytes"
"errors"
"github.com/armory/dinghy/pkg/dinghyfile/pipebuilder"
"reflect"
"testing"

Expand Down Expand Up @@ -68,6 +69,39 @@ func TestProcessDinghyfile(t *testing.T) {
assert.Nil(t, pb.ProcessDinghyfile("myorg", "myrepo", "the/full/path", "mybranch"))
}



func TestProcessDinghyfileValidate(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

rendered := `{"application":"biff"}`

renderer := NewMockParser(ctrl)
renderer.EXPECT().Parse(gomock.Eq("myorg"), gomock.Eq("myrepo"), gomock.Eq("the/full/path"), gomock.Eq("mybranch"), gomock.Any()).Return(bytes.NewBuffer([]byte(rendered)), nil).Times(1)

client := NewMockPlankClient(ctrl)

logger := mock.NewMockFieldLogger(ctrl)
logger.EXPECT().Infof(gomock.Eq("Unmarshalled: %v"), gomock.Any()).Times(1)
logger.EXPECT().Infof(gomock.Eq("Dinghyfile struct: %v"), gomock.Any()).Times(1)
logger.EXPECT().Infof(gomock.Eq("Updated: %s"), gomock.Any()).Times(1)
logger.EXPECT().Infof(gomock.Eq("Compiled: %s"), gomock.Any()).Times(1)
logger.EXPECT().Info(gomock.Eq("Validations for stage refs were successful")).Times(1)
logger.EXPECT().Info(gomock.Eq("Validations for app notifications were successful")).Times(1)
logger.EXPECT().Info(gomock.Eq("Validation finished successfully")).Times(1)

// Because we've set the renderer, we should NOT get this message...
logger.EXPECT().Info(gomock.Eq("Calling DetermineRenderer")).Times(0)

pb := testPipelineBuilder()
pb.Action = pipebuilder.Validate
pb.Parser = renderer
pb.Client = client
pb.Logger = logger
assert.Nil(t, pb.ProcessDinghyfile("myorg", "myrepo", "the/full/path", "mybranch"))
}

// Test updateapp
func TestUpdateApplication(t *testing.T) {
ctrl := gomock.NewController(t)
Expand Down
26 changes: 26 additions & 0 deletions pkg/dinghyfile/pipebuilder/action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2020 Armory, Inc.
* 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 pipebuilder

// BuilderAction wires to the action the builder will do
type BuilderAction string

// BuilderAction types
const (
Validate BuilderAction = "validate"
Process BuilderAction = "process"
)
2 changes: 2 additions & 0 deletions pkg/dinghyfile/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package dinghyfile

import (
"fmt"
"github.com/armory/dinghy/pkg/dinghyfile/pipebuilder"
"strings"

"github.com/armory/dinghy/pkg/cache"
Expand Down Expand Up @@ -52,6 +53,7 @@ func testBasePipelineBuilder() *PipelineBuilder {
Logger: logrus.New(),
Ums: []Unmarshaller{&DinghyJsonUnmarshaller{}},
TemplateOrg: "armory",
Action: pipebuilder.Process,
}
}

Expand Down
15 changes: 15 additions & 0 deletions pkg/git/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package git

import pipebuilder "github.com/armory/dinghy/pkg/dinghyfile/pipebuilder"

// Status wires up to the green check or red x next to a GitHub commit.
type Status string

Expand All @@ -27,4 +29,17 @@ const (
StatusFailure = "failure"
DefaultPendingMessage string = "Updating pipeline definitions..."
DefaultSuccessMessage string = "Pipeline definitions updated!"
DefaultValidatePendingMessage string = "Validating pipeline definitions..."
DefaultValidateSuccessMessage string = "Pipeline definitions validation was successful!"
)

var DefaultMessagesByBuilderAction = map[pipebuilder.BuilderAction]map[Status]string {
pipebuilder.Process: {
StatusPending: DefaultPendingMessage,
StatusSuccess: DefaultSuccessMessage,
},
pipebuilder.Validate: {
StatusPending: DefaultValidatePendingMessage,
StatusSuccess: DefaultValidateSuccessMessage,
},
}
66 changes: 66 additions & 0 deletions pkg/util/plank_readonly.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2020 Armory, Inc.
* 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 util

import "github.com/armory/plank/v3"

type PlankReadOnly struct {
Plank *plank.Client
}

func (p PlankReadOnly) GetApplication(string string) (*plank.Application, error){
return p.Plank.GetApplication(string)
}

func (p PlankReadOnly) UpdateApplicationNotifications(plank.NotificationsType, string) error{
return nil
}

func (p PlankReadOnly) GetApplicationNotifications(app string) (*plank.NotificationsType, error){
return p.Plank.GetApplicationNotifications(app)
}

func (p PlankReadOnly) CreateApplication(*plank.Application) error{
return nil
}

func (p PlankReadOnly) UpdateApplication(plank.Application) error{
return nil
}

func (p PlankReadOnly) GetPipelines(string string) ([]plank.Pipeline, error){
return p.Plank.GetPipelines(string)
}

func (p PlankReadOnly) DeletePipeline(plank.Pipeline) error{
return nil
}

func (p PlankReadOnly) UpsertPipeline(pipe plank.Pipeline, str string) error{
return nil
}

func (p PlankReadOnly) ResyncFiat() error{
return nil
}

func (p PlankReadOnly) ArmoryEndpointsEnabled() bool{
return false
}

func (p PlankReadOnly) EnableArmoryEndpoints(){
}
Loading

0 comments on commit 1dde8b9

Please sign in to comment.