Skip to content
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
1 change: 1 addition & 0 deletions api/client.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package api provides the Kubernetes client and configuration for the nine.ch API.
package api

import (
Expand Down
1 change: 1 addition & 0 deletions api/config/extension.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package config provides the nctl specific configuration stored in the kubeconfig.
package config

import (
Expand Down
38 changes: 18 additions & 20 deletions api/list.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package api

import (
"cmp"
"context"
"errors"
"fmt"
"reflect"
"slices"
"sort"
"strings"
"sync"

management "github.com/ninech/apis/management/v1alpha1"
"github.com/ninech/nctl/internal/format"
"golang.org/x/sync/errgroup"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/conversion"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -136,29 +135,29 @@ func (c *Client) ListObjects(ctx context.Context, list runtimeclient.ObjectList,
}

projectsSize := len(projects)
var wg sync.WaitGroup
wg.Add(projectsSize)

wg := errgroup.Group{}
ch := make(chan ProjectItems, projectsSize)

for _, proj := range projects {
tempOpts := slices.Clone(opts.clientListOptions)
go func() {
defer wg.Done()
// we ensured the list is a pointer type and that is has an
// 'Items' field which is a slice above, so we don't need to do
// this again here and instead use the reflect functions directly.
wg.Go(func() error {
tempOpts := slices.Clone(opts.clientListOptions)
tempList := reflect.New(reflect.TypeOf(list).Elem()).Interface().(runtimeclient.ObjectList)
tempList.GetObjectKind().SetGroupVersionKind(list.GetObjectKind().GroupVersionKind())
if err := c.List(ctx, tempList, append(tempOpts, runtimeclient.InNamespace(proj.Name))...); err != nil {
format.PrintWarningf("error when searching in project %s: %s\n", proj.Name, err)
return
return fmt.Errorf("error when searching in project %s: %w", proj.Name, err)
}

tempListItems := reflect.ValueOf(tempList).Elem().FieldByName("Items")
ch <- ProjectItems{projectName: proj.Name, items: tempListItems}
}()

return nil
})
}

wg.Wait()
if err := wg.Wait(); err != nil {
return err
}
close(ch)

// Collect and sort by project name
Expand All @@ -167,8 +166,8 @@ func (c *Client) ListObjects(ctx context.Context, list runtimeclient.ObjectList,
collected = append(collected, pi)
}

sort.Slice(collected, func(i, j int) bool {
return collected[i].projectName < collected[j].projectName
slices.SortFunc(collected, func(i, j ProjectItems) int {
return cmp.Compare(i.projectName, j.projectName)
})

for _, pi := range collected {
Expand All @@ -177,9 +176,8 @@ func (c *Client) ListObjects(ctx context.Context, list runtimeclient.ObjectList,
}
}

// if the user did not search for a specific named resource we can already
// quit as this case should not throw an error if no item could be
// found
// If the user did not search for a specific named resource we can already
// quit as this case should not throw an error if no item could be found.
if opts.searchForName == "" {
return nil
}
Expand Down
1 change: 1 addition & 0 deletions api/log/client.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package log provides functionality to interact with the Nine logging API.
package log

import (
Expand Down
1 change: 1 addition & 0 deletions api/util/apps.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package util provides utility functions for interacting with various Nine resources.
package util

import (
Expand Down
File renamed without changes.
6 changes: 5 additions & 1 deletion api/validation/apps.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Package validation provides functionality to validate git repositories
// and their access configurations.
package validation

import (
Expand All @@ -15,6 +17,8 @@ import (

// RepositoryValidator validates a git repository
type RepositoryValidator struct {
format.Writer

GitInformationServiceURL string
Token string
Debug bool
Expand All @@ -27,7 +31,7 @@ func (v *RepositoryValidator) Validate(ctx context.Context, git *apps.GitTarget,
return err
}
msg := " testing repository access 🔐"
spinner, err := format.NewSpinner(msg, msg)
spinner, err := v.Spinner(msg, msg)
if err != nil {
return err
}
Expand Down
13 changes: 10 additions & 3 deletions apply/apply.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// Package apply provides the implementation for the apply command,
// allowing users to apply resources from files.
package apply

import "os"
import (
"os"

"github.com/ninech/nctl/internal/format"
)

type Cmd struct {
Filename *os.File `short:"f" completion-predictor:"file"`
FromFile fromFile `cmd:"" default:"1" name:"-f <file>" help:"Apply any resource from a yaml or json file."`
format.Writer `hidden:""`
Filename *os.File `short:"f" completion-predictor:"file"`
FromFile fromFile `cmd:"" default:"1" name:"-f <file>" help:"Apply any resource from a yaml or json file."`
}
18 changes: 10 additions & 8 deletions apply/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

type fromFile struct {
}
type fromFile struct{}

func (cmd *Cmd) Run(ctx context.Context, client *api.Client, apply *Cmd) error {
return File(ctx, client, apply.Filename, UpdateOnExists())
return File(ctx, cmd.Writer, client, apply.Filename, UpdateOnExists())
}

type Option func(*config)
Expand All @@ -40,7 +39,7 @@ func Delete() Option {
}
}

func File(ctx context.Context, client *api.Client, file *os.File, opts ...Option) error {
func File(ctx context.Context, w format.Writer, client *api.Client, file *os.File, opts ...Option) error {
if file == nil {
return fmt.Errorf("missing flag -f, --filename=STRING")
}
Expand All @@ -60,19 +59,23 @@ func File(ctx context.Context, client *api.Client, file *os.File, opts ...Option
if err := client.Delete(ctx, obj); err != nil {
return err
}
format.PrintSuccessf("🗑", "deleted %s", formatObj(obj))
w.Successf("🗑", "deleted %s", formatObj(obj))

return nil
}

if err := client.Create(ctx, obj); err != nil {
if errors.IsAlreadyExists(err) && cfg.updateOnExists {
return update(ctx, client, obj)
if err := update(ctx, client, obj); err != nil {
return err
}
w.Successf("🏗", "applied %s", formatObj(obj))
return nil
}
return err
}

format.PrintSuccessf("🏗", "created %s", formatObj(obj))
w.Successf("🏗", "created %s", formatObj(obj))
return nil
}

Expand All @@ -99,7 +102,6 @@ func update(ctx context.Context, client *api.Client, obj *unstructured.Unstructu
return err
}

format.PrintSuccessf("🏗", "applied %s", formatObj(obj))
return nil
}

Expand Down
6 changes: 4 additions & 2 deletions apply/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

runtimev1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
iam "github.com/ninech/apis/iam/v1alpha1"
"github.com/ninech/nctl/internal/format"
"github.com/ninech/nctl/internal/test"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -59,6 +60,7 @@ func TestFile(t *testing.T) {
ctx := context.Background()
apiClient, err := test.SetupClient()
require.NoError(t, err)
w := format.NewWriter(t.Output())

tests := map[string]struct {
name string
Expand Down Expand Up @@ -128,7 +130,7 @@ func TestFile(t *testing.T) {
if tc.delete || tc.update {
fileToCreate, err := os.Open(f.Name())
require.NoError(t, err)
err = File(ctx, apiClient, fileToCreate) // This will close fileToCreate
err = File(ctx, w, apiClient, fileToCreate) // This will close fileToCreate
require.NoError(t, err)
}

Expand All @@ -150,7 +152,7 @@ func TestFile(t *testing.T) {
fileToApply, err := os.Open(f.Name())
require.NoError(t, err)

if err := File(ctx, apiClient, fileToApply, opts...); err != nil {
if err := File(ctx, w, apiClient, fileToApply, opts...); err != nil {
if tc.expectedErr {
return
}
Expand Down
9 changes: 6 additions & 3 deletions auth/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import (
"github.com/ninech/nctl/api"
"github.com/ninech/nctl/api/config"
"github.com/ninech/nctl/api/util"
"github.com/ninech/nctl/internal/format"

"k8s.io/apimachinery/pkg/types"
)

type ClusterCmd struct {
Name string `arg:"" help:"Name of the cluster to authenticate with. Also accepts 'name/project' format."`
ExecPlugin bool `help:"Automatically run exec plugin after writing the kubeconfig."`
format.Writer `hidden:""`
Name string `arg:"" help:"Name of the cluster to authenticate with. Also accepts 'name/project' format."`
ExecPlugin bool `help:"Automatically run exec plugin after writing the kubeconfig."`
}

func (a *ClusterCmd) Run(ctx context.Context, client *api.Client) error {
Expand Down Expand Up @@ -91,7 +94,7 @@ func (a *ClusterCmd) Run(ctx context.Context, client *api.Client) error {
}
}

if err := login(cfg, client.KubeconfigPath, userInfo.User, "", switchCurrentContext()); err != nil {
if err := login(a.Writer, cfg, client.KubeconfigPath, userInfo.User, "", switchCurrentContext()); err != nil {
return fmt.Errorf("error logging in to cluster %s: %w", name, err)
}

Expand Down
Loading