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

Add example code #61

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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: 12 additions & 0 deletions cmd/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cmd

import (
"github.com/app-sre/go-qontract-reconcile/internal/example"
"github.com/app-sre/go-qontract-reconcile/pkg/reconcile"
)

func exampleIntegration() {
notifier := example.NewExample()
runner := reconcile.NewIntegrationRunner(notifier, example.EXAMPLE_INTEGRATION_NAME)
runner.Run()
}
11 changes: 11 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ var (
gitPartitionSyncProducer()
},
}

exampleCmd = &cobra.Command{
Use: "example",
Short: "Example integration",
Long: "Example integration",
Run: func(cmd *cobra.Command, args []string) {
exampleIntegration()
},
}
)

// Execute executes the rootCmd
Expand All @@ -59,10 +68,12 @@ func init() {
rootCmd.AddCommand(userValidatorCmd)
rootCmd.AddCommand(accountNotifierCmd)
rootCmd.AddCommand(gitPartitionSyncProducerCmd)
rootCmd.AddCommand(exampleCmd)
rootCmd.PersistentFlags().StringVarP(&logLevel, "logLevel", "l", "info", "Log level")
userValidatorCmd.Flags().StringVarP(&cfgFile, "cfgFile", "c", "", "Configuration File")
accountNotifierCmd.Flags().StringVarP(&cfgFile, "cfgFile", "c", "", "Configuration File")
gitPartitionSyncProducerCmd.Flags().StringVarP(&cfgFile, "cfgFile", "c", "", "Configuration File")
exampleCmd.Flags().StringVarP(&cfgFile, "cfgFile", "c", "", "Configuration File")

cobra.OnInitialize(initConfig)
cobra.OnInitialize(configureLogging)
Expand Down
175 changes: 175 additions & 0 deletions internal/example/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package example

import (
"context"
"io/ioutil"
"os"

"github.com/app-sre/go-qontract-reconcile/pkg/reconcile"
"github.com/app-sre/go-qontract-reconcile/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/viper"
)

var EXAMPLE_INTEGRATION_NAME = "example"

type ListDirectoryFunc func(string) ([]os.FileInfo, error)
type ReadFileFunc func(string) ([]byte, error)
type GetUsers func(context.Context) (*UsersResponse, error)

type ExampleConfig struct {
Tempdir string
}

func newExampleConfig() *ExampleConfig {
var ec ExampleConfig
sub := util.EnsureViperSub(viper.GetViper(), "example")
sub.SetDefault("tempdir", "/tmp/example")
sub.BindEnv("tempdir", "EXAMPLE_TEMPDIR")
if err := sub.Unmarshal(&ec); err != nil {
util.Log().Fatalw("Error while unmarshalling configuration %s", err.Error())
}
return &ec
}

type Example struct {
config *ExampleConfig

listDirectoryFunc ListDirectoryFunc
readFileFunc ReadFileFunc
getUsersFunc GetUsers
}

func NewExample() *Example {
ec := newExampleConfig()
return &Example{
config: ec,
listDirectoryFunc: func(path string) ([]os.FileInfo, error) {
return ioutil.ReadDir(path)
},
readFileFunc: func(path string) ([]byte, error) {
return ioutil.ReadFile(path)
},
getUsersFunc: func(ctx context.Context) (*UsersResponse, error) {
return Users(ctx)
},
}
}

type UserFiles struct {
FileNames string
GpgKey string
}

func (e *Example) CurrentState(ctx context.Context, ri *reconcile.ResourceInventory) error {
util.Log().Infow("Getting current state")

files, err := e.listDirectoryFunc(e.config.Tempdir)
if err != nil {
return errors.Wrap(err, "Error while reading workdir")
}

for _, f := range files {
absolutePath := e.config.Tempdir + "/" + f.Name()
util.Log().Debugw("Found file", "file", absolutePath)
content, err := e.readFileFunc(absolutePath)
if err != nil {
return errors.Wrap(err, "Error while reading file")
}
rs := &reconcile.ResourceState{
Current: &UserFiles{
FileNames: f.Name(),
GpgKey: string(content),
},
}
ri.AddResourceState(f.Name(), rs)
}

return nil
}

func (e *Example) DesiredState(ctx context.Context, ri *reconcile.ResourceInventory) error {
util.Log().Infow("Getting desired state")

users, err := e.getUsersFunc(ctx)

if err != nil {
return errors.Wrap(err, "Error while getting users")
}

for _, user := range users.GetUsers_v1() {
state := ri.GetResourceState(user.GetOrg_username())
if state == nil {
state = &reconcile.ResourceState{}
ri.AddResourceState(user.GetOrg_username(), state)
}
state.Config = user
state.Desired = &UserFiles{
FileNames: user.GetOrg_username(),
GpgKey: user.GetPublic_gpg_key(),
}
}

return nil
}

func (e *Example) Reconcile(ctx context.Context, ri *reconcile.ResourceInventory) error {
util.Log().Infow("Reconciling")

for _, state := range ri.State {
var current, desired *UserFiles
if state.Current != nil {
current = state.Current.(*UserFiles)
}
if state.Desired != nil {
desired = state.Desired.(*UserFiles)
}

if current != nil && desired == nil {
absolutePath := e.config.Tempdir + "/" + current.FileNames
util.Log().Infow("Deleting file", "file", current.FileNames)
err := os.Remove(absolutePath)
if err != nil {
return errors.Wrap(err, "Error while deleting file")
}
} else if current == nil || current.GpgKey != desired.GpgKey {
absolutePath := e.config.Tempdir + "/" + desired.FileNames
util.Log().Infow("Writing file", "file", desired.FileNames)
err := ioutil.WriteFile(absolutePath, []byte(desired.GpgKey), 0644)
if err != nil {
return errors.Wrap(err, "Error while writing file")
}
}

}
return nil
}

func (e *Example) LogDiff(ri *reconcile.ResourceInventory) {
util.Log().Debugw("Logging diff")

for _, state := range ri.State {
var current, desired *UserFiles
if state.Current != nil {
current = state.Current.(*UserFiles)
}
if state.Desired != nil {
desired = state.Desired.(*UserFiles)
}
if current != nil && desired == nil {
util.Log().Infow("Deleting", "file", current.FileNames)
} else if current == nil || current.GpgKey != desired.GpgKey {
util.Log().Infow("Updating", "file", desired.FileNames)
}
}
}

func (e *Example) Setup(context.Context) error {
util.Log().Infow("Setting up example integration")
err := os.MkdirAll(e.config.Tempdir, os.ModePerm)
if err != nil {
return errors.Wrap(err, "Error while creating workdir")
}

return nil
}
109 changes: 109 additions & 0 deletions internal/example/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package example

import (
"context"
"os"
"testing"
"time"

"github.com/app-sre/go-qontract-reconcile/pkg/reconcile"
"github.com/stretchr/testify/assert"
)

type TestFileInfo struct {
name string
content string
}

func (t *TestFileInfo) Name() string {
return t.name
}
func (t *TestFileInfo) Size() int64 {
return int64(len(t.content))
}
func (t *TestFileInfo) Mode() os.FileMode {
return os.ModePerm
}
func (t *TestFileInfo) ModTime() time.Time {
return time.Now()
}
func (t *TestFileInfo) IsDir() bool {
return false
}
func (t *TestFileInfo) Sys() any {
return nil
}

func TestCurrentEmpty(t *testing.T) {
e := NewExample()
called := false

e.listDirectoryFunc = func(path string) ([]os.FileInfo, error) {
called = true
return []os.FileInfo{}, nil
}

ri := reconcile.NewResourceInventory()
ctx := context.Background()

err := e.CurrentState(ctx, ri)
assert.NoError(t, err)

assert.Len(t, ri.State, 0)
assert.True(t, called)
}

func TestCurrent(t *testing.T) {
e := NewExample()

fileInfo := &TestFileInfo{
name: "file1",
content: "content",
}

e.listDirectoryFunc = func(path string) ([]os.FileInfo, error) {
return []os.FileInfo{fileInfo}, nil
}
e.readFileFunc = func(path string) ([]byte, error) {
return []byte(fileInfo.content), nil
}

ri := reconcile.NewResourceInventory()
ctx := context.Background()

err := e.CurrentState(ctx, ri)

assert.NoError(t, err)

state := ri.GetResourceState(fileInfo.Name())

assert.NotNil(t, state)
current := state.Current.(*UserFiles)

assert.Equal(t, fileInfo.Name(), current.FileNames)
assert.Equal(t, fileInfo.content, current.GpgKey)
}

func TestDesired(t *testing.T) {
e := NewExample()

e.getUsersFunc = func(ctx context.Context) (*UsersResponse, error) {
return &UsersResponse{
Users_v1: []UsersUsers_v1User_v1{
{
Org_username: "test",
Public_gpg_key: "key",
},
},
}, nil
}

ctx := context.Background()
ri := reconcile.NewResourceInventory()

err := e.DesiredState(ctx, ri)
assert.NoError(t, err)

state := ri.GetResourceState("test")
assert.NotNil(t, state)
}