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

Adds support for events in IaC files. #200

Merged
merged 7 commits into from
Nov 4, 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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# 0.0.126 (Oct 31, 2024)
# 0.0.126 (Nov 04, 2024)
* `nullstone iac test` will find IaC files with `.yml` *or* `.yaml` file extension.
* Added support for `events` in IaC files.

# 0.0.125 (Oct 25, 2024)
* `nullstone iac test` now emits previous and updated values for `module_version`, `env_variable`, `variable`, and `connection` changes.
Expand Down
2 changes: 1 addition & 1 deletion cmd/iac.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ var IacTest = &cli.Command{
return err
}

return iac2.Apply(ctx, cfg, curDir, stdout, *stack, *env, *pmr)
return iac2.Test(ctx, cfg, stdout, *stack, *env, *pmr)
})
})
},
Expand Down
28 changes: 28 additions & 0 deletions git/get_vcs_url.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package git

import (
"github.com/go-git/go-git/v5"
"net/url"
"strings"
)

func GetVcsUrl(repo *git.Repository) (*url.URL, error) {
remote, err := repo.Remote("origin")
if err != nil {
return nil, err
}
if remote == nil || remote.Config() == nil {
return nil, nil
}
urls := remote.Config().URLs
if len(urls) < 1 {
return nil, nil
}

clean := strings.TrimSuffix(urls[0], ".git")
if strings.HasPrefix(urls[0], "[email protected]:") {
clean = strings.Replace(urls[0], "[email protected]:", "https://github.com/", 1)
}

return url.Parse(clean)
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ require (
github.com/gosuri/uilive v0.0.4
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
github.com/nullstone-io/deployment-sdk v0.0.0-20240704122950-a55160b619cd
github.com/nullstone-io/iac v0.0.0-20241031195625-8151ddbc32b5
github.com/nullstone-io/iac v0.0.0-20241104165455-5b50a5757a06
github.com/nullstone-io/module v0.2.9
github.com/ryanuber/columnize v2.1.2+incompatible
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.3.0
golang.org/x/crypto v0.18.0
golang.org/x/sync v0.5.0
gopkg.in/nullstone-io/go-api-client.v0 v0.0.0-20241025125133-d3e3cc5ae079
gopkg.in/nullstone-io/go-api-client.v0 v0.0.0-20241104165420-28a5aae528a6
k8s.io/api v0.27.2
k8s.io/apimachinery v0.27.2
k8s.io/client-go v0.27.2
Expand Down
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -773,10 +773,8 @@ github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nullstone-io/deployment-sdk v0.0.0-20240704122950-a55160b619cd h1:3DWDfyQ9UGUfkfrK0PshA43fCp7PZuC/BjqYUBTZhi8=
github.com/nullstone-io/deployment-sdk v0.0.0-20240704122950-a55160b619cd/go.mod h1:7wc/0tmIELFoBTjNB5+hsyvTQD60mnt+BJ9/AU2/Cb8=
github.com/nullstone-io/iac v0.0.0-20241025133315-2ceba267e009 h1:n/N00nAI27Og5ohn9Zg9Gw/1QKgAYNt4W3sFHuZlO/o=
github.com/nullstone-io/iac v0.0.0-20241025133315-2ceba267e009/go.mod h1:QPw/RbUF3Fu4zs2cu63i+LFon5mU8HEkOCUrSJ7hC6U=
github.com/nullstone-io/iac v0.0.0-20241031195625-8151ddbc32b5 h1:3G1Y+YdoKx0yTtOnqGfBjNyHs6G56J4jVbai1UaUTvo=
github.com/nullstone-io/iac v0.0.0-20241031195625-8151ddbc32b5/go.mod h1:QPw/RbUF3Fu4zs2cu63i+LFon5mU8HEkOCUrSJ7hC6U=
github.com/nullstone-io/iac v0.0.0-20241104165455-5b50a5757a06 h1:ex3xZvrPC/ms1tcSgq8YRdzIJfRglL6PrmMJdTB9eoQ=
github.com/nullstone-io/iac v0.0.0-20241104165455-5b50a5757a06/go.mod h1:tRZPp425TN0XVAkrkICbZ1EAkmmhNb7ueq7xBLX2goM=
github.com/nullstone-io/module v0.2.9 h1:PcYhPEemBbc+RdP+Q/DF0+XlwJkkNb5R17Hfv8qaYyc=
github.com/nullstone-io/module v0.2.9/go.mod h1:btQiO0giVWDvvaQ7CLnPmuPPakJc55lAr8OlE1LK6hg=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
Expand Down Expand Up @@ -1462,8 +1460,8 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/nullstone-io/go-api-client.v0 v0.0.0-20241025125133-d3e3cc5ae079 h1:QM+6o67PfrdBSEEEbxkFWSmNU7d1kD3Ex6aVTJsqgtg=
gopkg.in/nullstone-io/go-api-client.v0 v0.0.0-20241025125133-d3e3cc5ae079/go.mod h1:EHFRYFcg303Tc9FDxBmvUrT8AncZ4KMT8FrRoGJnqDk=
gopkg.in/nullstone-io/go-api-client.v0 v0.0.0-20241104165420-28a5aae528a6 h1:pBqC7B6hjZH+GeFfrKb9cCSabCAlQ8o2Ij0o96GTwUY=
gopkg.in/nullstone-io/go-api-client.v0 v0.0.0-20241104165420-28a5aae528a6/go.mod h1:m/JNiW4XSXjRLAedd7LYOO0l/FfCiKffRFolkKpfpgA=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM=
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
Expand Down
26 changes: 16 additions & 10 deletions iac/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import (
"strings"
)

var (
blankVcsUrl = url.URL{
Scheme: "https",
Host: "localhost",
Path: "local/repo",
}
)

func Discover(dir string, w io.Writer) (*iac.ParseMapResult, error) {
pmr, err := parseIacFiles(dir)
if err != nil {
Expand Down Expand Up @@ -43,18 +51,16 @@ func parseIacFiles(dir string) (*iac.ParseMapResult, error) {
rootDir = dir
}

parseContext := "local"
remote, err := repo.Remote("origin")
if err == nil && remote != nil {
if remoteConfig := remote.Config(); remoteConfig != nil && len(remoteConfig.URLs) > 0 {
remoteUrl, err := url.Parse(remoteConfig.URLs[0])
if err == nil && remoteUrl != nil {
parseContext = strings.TrimSuffix(strings.TrimPrefix(remoteUrl.Path, "/"), ".git")
}
}
repoUrl, err := git.GetVcsUrl(repo)
if err != nil {
return nil, fmt.Errorf("error trying to discover the repo url of the local repository: %w", err)
}
if repoUrl == nil {
repoUrl = &blankVcsUrl
}

pmr, err := iac.ParseConfigDir(parseContext, filepath.Join(rootDir, ".nullstone"))
repoName := strings.TrimPrefix(repoUrl.Path, "/")
pmr, err := iac.ParseConfigDir(repoUrl.String(), repoName, filepath.Join(rootDir, ".nullstone"))
if err != nil {
return nil, fmt.Errorf("error parsing nullstone IaC files: %w", err)
}
Expand Down
31 changes: 31 additions & 0 deletions iac/emit_changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package iac
import (
"fmt"
"github.com/mitchellh/colorstring"
"github.com/nullstone-io/iac/events"
"github.com/nullstone-io/iac/workspace"
"gopkg.in/nullstone-io/go-api-client.v0/types"
"io"
Expand Down Expand Up @@ -134,3 +135,33 @@ func compareWorkspaceChange(a, b types.WorkspaceChange) int {
return -1

}

func emitEventChanges(w io.Writer, changes events.Changes) {
if len(changes) == 0 {
return
}
s := "s"
if len(changes) == 1 {
s = ""
}
indent := indentStep
colorstring.Fprintf(w, "%s[bold]events[reset] => %d change%s\n", indent, len(changes), s)
indent += indentStep
for _, change := range changes {
emitEventChangeLabel(w, indent, change)
if change.Action == events.ChangeActionUpdate {
// TODO: emitEventUpdateChangeDiff(w, indent, change)
}
}
}

func emitEventChangeLabel(w io.Writer, indent string, change events.Change) {
switch change.Action {
case events.ChangeActionAdd:
colorstring.Fprintf(w, "%s[green]+ %s[reset]\n", indent, change.Desired.Name)
case events.ChangeActionDelete:
colorstring.Fprintf(w, "%s[red]- %s[reset]\n", indent, change.Current.Name)
case events.ChangeActionUpdate:
colorstring.Fprintf(w, "%s[yellow]~ %s[reset]\n", indent, change.Desired.Name)
}
}
38 changes: 35 additions & 3 deletions iac/apply.go → iac/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"github.com/mitchellh/colorstring"
"github.com/nullstone-io/iac"
iacEvents "github.com/nullstone-io/iac/events"
"github.com/nullstone-io/iac/workspace"
"gopkg.in/nullstone-io/go-api-client.v0"
"gopkg.in/nullstone-io/go-api-client.v0/types"
Expand All @@ -15,7 +16,17 @@ const (
indentStep = " "
)

func Apply(ctx context.Context, cfg api.Config, curDir string, w io.Writer, stack types.Stack, env types.Environment, pmr iac.ParseMapResult) error {
func Test(ctx context.Context, cfg api.Config, w io.Writer, stack types.Stack, env types.Environment, pmr iac.ParseMapResult) error {
if err := testWorkspaces(ctx, cfg, w, stack, env, pmr); err != nil {
return err
}
if err := testEvents(ctx, cfg, w, stack, env, pmr); err != nil {
return err
}
return nil
}

func testWorkspaces(ctx context.Context, cfg api.Config, w io.Writer, stack types.Stack, env types.Environment, pmr iac.ParseMapResult) error {
blockNames := pmr.BlockNames(env)
apiClient := &api.Client{Config: cfg}
allBlocks, err := apiClient.Blocks().List(ctx, stack.Id)
Expand All @@ -37,7 +48,7 @@ func Apply(ctx context.Context, cfg api.Config, curDir string, w io.Writer, stac
}
colorstring.Fprintf(w, "[bold]Detecting changes for %d block%s in %s/%s...[reset]\n", len(blocks), plural, stack.Name, env.Name)
for _, block := range blocks {
if err := applyWorkspace(ctx, apiClient, w, stack, block, env, pmr); err != nil {
if err := testWorkspace(ctx, apiClient, w, stack, block, env, pmr); err != nil {
colorstring.Fprintf(w, "[red]An error occurred: %s[reset]\n", err)
hasError = true
}
Expand All @@ -49,7 +60,7 @@ func Apply(ctx context.Context, cfg api.Config, curDir string, w io.Writer, stac
return nil
}

func applyWorkspace(ctx context.Context, apiClient *api.Client, w io.Writer, stack types.Stack, block types.Block, env types.Environment, pmr iac.ParseMapResult) error {
func testWorkspace(ctx context.Context, apiClient *api.Client, w io.Writer, stack types.Stack, block types.Block, env types.Environment, pmr iac.ParseMapResult) error {
effective, err := apiClient.WorkspaceConfigs().GetEffective(ctx, stack.Id, block.Id, env.Id)
if err != nil {
return fmt.Errorf("error retrieving workspace: %w", err)
Expand All @@ -72,3 +83,24 @@ func applyWorkspace(ctx context.Context, apiClient *api.Client, w io.Writer, sta
emitWorkspaceChanges(w, block, changes)
return nil
}

func testEvents(ctx context.Context, cfg api.Config, w io.Writer, stack types.Stack, env types.Environment, pmr iac.ParseMapResult) error {
apiClient := api.Client{Config: cfg}
existingEvents, err := apiClient.EnvEvents().List(ctx, stack.Id, env.Id)
if err != nil {
return fmt.Errorf("error looking up existing events: %w", err)
}

current := map[string]types.EnvEvent{}
for _, cur := range existingEvents {
if cur.OwningRepoUrl == pmr.Config.IacContext.RepoUrl {
current[cur.Name] = cur
}
}

colorstring.Fprintf(w, "[bold]Detecting changes for events in %s/%s...[reset]\n", stack.Name, env.Name)
desired := iacEvents.Get(pmr, env)
changes := iacEvents.Diff(current, desired, pmr.Config.IacContext.RepoUrl)
emitEventChanges(w, changes)
return nil
}