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

feat: KBC ignore, pull command #2136

Merged
merged 11 commits into from
Nov 19, 2024
Merged
2 changes: 2 additions & 0 deletions internal/pkg/model/objectstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ type ConfigState struct {
*ConfigManifest
Remote *Config
Local *Config
Ignore bool
}

type ConfigRowState struct {
*ConfigRowManifest
Remote *ConfigRow
Local *ConfigRow
Ignore bool
}

// ToAPIObjectKey ...
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/plan/push/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/keboola/keboola-as-code/internal/pkg/utils/errors"
)

func NewPlan(diffResults *diff.Results, allowTargetEnv bool) (*diffop.Plan, error) {
func NewPlan(diffResults *diff.Results) (*diffop.Plan, error) {
plan := diffop.NewPlan(`push`)
for _, result := range diffResults.Results {
switch result.State {
Expand Down
111 changes: 111 additions & 0 deletions internal/pkg/state/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package registry

import (
"context"
"strings"
"sync"

"github.com/keboola/go-utils/pkg/orderedmap"

"github.com/keboola/keboola-as-code/internal/pkg/filesystem"
"github.com/keboola/keboola-as-code/internal/pkg/filesystem/knownpaths"
. "github.com/keboola/keboola-as-code/internal/pkg/model"
"github.com/keboola/keboola-as-code/internal/pkg/naming"
Expand All @@ -14,6 +16,8 @@ import (

type pathsRO = knownpaths.PathsReadOnly

const KBCIgnoreFilePath = ".keboola/kbc_ignore"
Matovidlo marked this conversation as resolved.
Show resolved Hide resolved
Matovidlo marked this conversation as resolved.
Show resolved Hide resolved

type Registry struct {
*pathsRO
paths *knownpaths.Paths
Expand Down Expand Up @@ -150,6 +154,35 @@ func (s *Registry) Configs() (configs []*ConfigState) {
return configs
}

func (s *Registry) IgnoreConfig(ignoreID string, componentID string) {
for _, object := range s.All() {
if v, ok := object.(*ConfigState); ok {
if v.ID.String() == ignoreID && v.ComponentID.String() == componentID {
// ignore configuration
v.Ignore = true

// ignore rows of the configuration
if len(s.ConfigRowsFrom(v.ConfigKey)) > 0 {
jachym-tousek-keboola marked this conversation as resolved.
Show resolved Hide resolved
for _, configRowState := range s.ConfigRowsFrom(v.ConfigKey) {
configRowState.Ignore = true
}
}
}
}
}
}

func (s *Registry) IgnoredConfigs() (configs []*ConfigState) {
for _, object := range s.All() {
if v, ok := object.(*ConfigState); ok {
if v.Ignore {
configs = append(configs, v)
}
}
}
return configs
}

func (s *Registry) ConfigsFrom(branch BranchKey) (configs []*ConfigState) {
for _, object := range s.All() {
if v, ok := object.(*ConfigState); ok {
Expand All @@ -171,6 +204,27 @@ func (s *Registry) ConfigRows() (rows []*ConfigRowState) {
return rows
}

func (s *Registry) IgnoreConfigRow(configID, rowID string) {
for _, object := range s.All() {
if v, ok := object.(*ConfigRowState); ok {
if v.ConfigID.String() == configID && v.ID.String() == rowID {
v.Ignore = true
}
}
}
}

func (s *Registry) IgnoredConfigRows() (rows []*ConfigRowState) {
for _, object := range s.All() {
if v, ok := object.(*ConfigRowState); ok {
if v.Ignore {
rows = append(rows, v)
}
}
}
return rows
}

func (s *Registry) ConfigRowsFrom(config ConfigKey) (rows []*ConfigRowState) {
for _, object := range s.All() {
if v, ok := object.(*ConfigRowState); ok {
Expand All @@ -183,6 +237,19 @@ func (s *Registry) ConfigRowsFrom(config ConfigKey) (rows []*ConfigRowState) {
return rows
}

func (s *Registry) SetIgnoredConfigsOrRows(ctx context.Context, fs filesystem.Fs, path string) error {
content, err := fs.ReadFile(ctx, filesystem.NewFileDef(path))
if err != nil {
return err
}

if content.Content == "" {
return nil
}

return s.applyIgnoredPatterns(content.Content)
}

func (s *Registry) GetPath(key Key) (AbsPath, bool) {
objectState, found := s.Get(key)
if !found {
Expand Down Expand Up @@ -261,3 +328,47 @@ func (s *Registry) GetOrCreateFrom(manifest ObjectManifest) (ObjectState, error)

return s.CreateFrom(manifest)
}

// applyIgnorePattern applies a single ignore pattern, marking the appropriate config or row as ignored.
func (s *Registry) applyIgnorePattern(pattern string) error {
parts := strings.Split(pattern, "/")

switch len(parts) {
case 2:
hosekpeter marked this conversation as resolved.
Show resolved Hide resolved
// Ignore config by ID and name.
configID, componentID := parts[1], parts[0]
s.IgnoreConfig(configID, componentID)
case 3:
// Ignore specific config row.
configID, rowID := parts[1], parts[2]
s.IgnoreConfigRow(configID, rowID)
default:
return errors.Errorf("invalid ignore pattern format: %s", pattern)
}

return nil
}

// applyIgnoredPatterns parses the content for ignore patterns and applies them to configurations or rows.
func (s *Registry) applyIgnoredPatterns(content string) error {
for _, pattern := range parseIgnoredPatterns(content) {
if err := s.applyIgnorePattern(pattern); err != nil {
continue
}
}
return nil
}

func parseIgnoredPatterns(content string) []string {
var ignorePatterns []string
lines := strings.Split(content, "\n")
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
// Skip empty lines and comments
if trimmedLine != "" && !strings.HasPrefix(trimmedLine, "#") {
ignorePatterns = append(ignorePatterns, trimmedLine)
}
}

return ignorePatterns
}
140 changes: 140 additions & 0 deletions internal/pkg/state/registry/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package registry

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/keboola/keboola-as-code/internal/pkg/filesystem"
"github.com/keboola/keboola-as-code/internal/pkg/filesystem/aferofs"
Expand Down Expand Up @@ -175,6 +177,21 @@ func TestStateTrackRecordInvalid(t *testing.T) {
assert.Empty(t, s.UntrackedPaths())
}

func TestRegistry_SetIgnoredConfigsOrRows(t *testing.T) {
t.Parallel()
ctx := context.Background()
registry := newTestState(t, knownpaths.NewNop(ctx))
fs := aferofs.NewMemoryFs()
require.NoError(t, fs.WriteFile(ctx, filesystem.NewRawFile(`foo/bar1`, "keboola.bar/678/34\nkeboola.foo/345")))

require.NoError(t, registry.SetIgnoredConfigsOrRows(ctx, fs, "foo/bar1"))

assert.Len(t, registry.IgnoredConfigRows(), 1)
assert.Len(t, registry.IgnoredConfigs(), 1)
assert.Equal(t, registry.IgnoredConfigRows()[0].ID.String(), "34")
assert.Equal(t, registry.IgnoredConfigs()[0].ID.String(), "345")
}

func TestRegistry_GetPath(t *testing.T) {
t.Parallel()
ctx := context.Background()
Expand Down Expand Up @@ -301,3 +318,126 @@ func newTestState(t *testing.T, paths *knownpaths.Paths) *Registry {

return registry
}

func Test_parseIgnoredPatterns(t *testing.T) {
t.Parallel()
type args struct {
content string
}
tests := []struct {
name string
args args
want []string
}{
{
name: "with comments",
args: args{
content: "##dataapps\nkeboola.data-apps/12345\n##dataapps2\nkeboola.data-apps/123/rowId123\n##dataaps3\nkeboola.data-apps/12",
},
want: []string{
"keboola.data-apps/12345",
"keboola.data-apps/123/rowId123",
"keboola.data-apps/12",
},
},
{
name: "with empty lines",
args: args{
content: "##dataapps\n\n\n\n\n\nkeboola.data-apps/12345\n\t##dataapps2\nkeboola.data-apps/123\n##dataaps3\nkeboola.data-apps/12",
},
want: []string{
"keboola.data-apps/12345",
"keboola.data-apps/123",
"keboola.data-apps/12",
},
},
{
name: "empty file",
args: args{
content: "\n",
},
want: nil,
},
{
name: "error",
args: args{
content: "##dataapps\n\n\n\n\n\nkeboola.data-apps/12345\n\t##dataapps2\nkeboola.data-apps/123\n##dataaps3\nkeboola.data-apps/12",
},
want: []string{
"keboola.data-apps/12345",
"keboola.data-apps/123",
"keboola.data-apps/12",
},
},
{
name: "empty file",
args: args{
content: "\n",
},
want: nil,
},
}

for _, tt := range tests {
got := parseIgnoredPatterns(tt.args.content)
assert.Equal(t, tt.want, got)
}
}

func TestRegistry_applyIgnoredPatterns(t *testing.T) {
t.Parallel()
ctx := context.Background()
registry := newTestState(t, knownpaths.NewNop(ctx))

type args struct {
pattern string
}

tests := []struct {
name string
args args
wantErr assert.ErrorAssertionFunc
}{
{
name: "empty patterns",
args: args{
pattern: "",
},
wantErr: assert.Error,
},
{
name: "wrong pattern",
args: args{
pattern: "wrong pattern",
},
wantErr: assert.Error,
},
{
name: "too long pattern",
hosekpeter marked this conversation as resolved.
Show resolved Hide resolved
args: args{
pattern: "keboola.bar/687/1234/1234",
},
wantErr: assert.Error,
},
{
name: "short pattern",
args: args{
pattern: "keboola.bar",
},
wantErr: assert.Error,
},
{
name: "correct pattern",
args: args{
pattern: "keboola.bar/687",
},
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tt.wantErr(t, registry.applyIgnorePattern(tt.args.pattern), fmt.Sprintf("applyIgnoredPatterns(%v)", tt.args.pattern))
})
}
}
22 changes: 22 additions & 0 deletions pkg/lib/operation/project/sync/pull/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/keboola/keboola-as-code/internal/pkg/plan/pull"
"github.com/keboola/keboola-as-code/internal/pkg/project"
"github.com/keboola/keboola-as-code/internal/pkg/project/cachefile"
"github.com/keboola/keboola-as-code/internal/pkg/state/registry"
"github.com/keboola/keboola-as-code/internal/pkg/telemetry"
"github.com/keboola/keboola-as-code/internal/pkg/utils/errors"
saveManifest "github.com/keboola/keboola-as-code/pkg/lib/operation/project/local/manifest/save"
Expand Down Expand Up @@ -49,6 +50,13 @@ func Run(ctx context.Context, projectState *project.State, o Options, d dependen

logger := d.Logger()

if projectState.Fs().Exists(ctx, registry.KBCIgnoreFilePath) {
if err := projectState.SetIgnoredConfigsOrRows(ctx, projectState.Fs(), registry.KBCIgnoreFilePath); err != nil {
return err
}
ignoreConfigsAndRows(projectState)
}

// Diff
results, err := createDiff.Run(ctx, createDiff.Options{Objects: projectState}, d, diff.WithIgnoreBranchName(projectState.ProjectManifest().AllowTargetENV()))
if err != nil {
Expand Down Expand Up @@ -118,3 +126,17 @@ func Run(ctx context.Context, projectState *project.State, o Options, d dependen

return nil
}

func ignoreConfigsAndRows(projectState *project.State) {
if len(projectState.IgnoredConfigRows()) > 0 {
jachym-tousek-keboola marked this conversation as resolved.
Show resolved Hide resolved
for _, v := range projectState.IgnoredConfigRows() {
v.SetRemoteState(nil)
}
}

if len(projectState.IgnoredConfigs()) > 0 {
jachym-tousek-keboola marked this conversation as resolved.
Show resolved Hide resolved
for _, v := range projectState.IgnoredConfigs() {
v.SetRemoteState(nil)
}
}
}
2 changes: 1 addition & 1 deletion pkg/lib/operation/project/sync/push/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func Run(ctx context.Context, projectState *project.State, o Options, d dependen
}

// Get plan
plan, err := push.NewPlan(results, projectState.ProjectManifest().AllowTargetENV())
plan, err := push.NewPlan(results)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions test/cli/allow-target-env/pull-ignore-configs/args
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pull --storage-api-token %%TEST_KBC_STORAGE_API_TOKEN%%
2 changes: 2 additions & 0 deletions test/cli/allow-target-env/pull-ignore-configs/env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
KBC_PROJECT_ID=%%TEST_KBC_PROJECT_ID%%
KBC_BRANCH_ID=%%TEST_BRANCH_MAIN_ID%%
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
Empty file.
Loading