Skip to content
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
2 changes: 1 addition & 1 deletion internal/run/api/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestWrapError(t *testing.T) {
assert.Error(t, got)
assert.Contains(t, got.Error(), tt.wantSubString)
if tt.wantWrapped {
assert.ErrorIs(t, got, tt.inputErr) // Checks if it wraps the original error
assert.ErrorIs(t, got, tt.inputErr) // Checks if it wraps the original error
assert.NotEqual(t, tt.inputErr, got) // Should not be exactly the same object (wrapped)
} else {
assert.Equal(t, tt.inputErr, got) // Should be exactly the same
Expand Down
4 changes: 2 additions & 2 deletions internal/run/api/domainmapping/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,11 @@ func (c *GCPClient) ListDomainMappings(ctx context.Context, project, region stri
} else {
pageToken = ""
}

if pageToken == "" {
break
}
}

return domainMappings, nil
}
}
40 changes: 24 additions & 16 deletions internal/run/api/domainmapping/domainmapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import (
"sync"
"time"

"google.golang.org/api/run/v1"
api_region "github.com/JulienBreux/run-cli/internal/run/api/region"
model "github.com/JulienBreux/run-cli/internal/run/model/domainmapping"
"github.com/JulienBreux/run-cli/internal/run/model/common/condition"
model "github.com/JulienBreux/run-cli/internal/run/model/domainmapping"
"google.golang.org/api/run/v1"
)

var apiClient Client = &GCPClient{}
Expand All @@ -50,25 +50,33 @@ func List(project, region string) ([]model.DomainMapping, error) {
}

func listAllRegions(project string) ([]model.DomainMapping, error) {
var (
mu sync.Mutex
domainMappings []model.DomainMapping
wg sync.WaitGroup
)
regions := api_region.List()
results := make([][]model.DomainMapping, len(regions))
var wg sync.WaitGroup

for _, region := range api_region.List() {
for i, region := range regions {
wg.Add(1)
go func(r string) {
go func(idx int, r string) {
defer wg.Done()
if dms, err := List(project, r); err == nil {
mu.Lock()
domainMappings = append(domainMappings, dms...)
mu.Unlock()
results[idx] = dms
}
}(region)
}(i, region)
}

wg.Wait()

// Pre-allocate the final flat slice based on the exact total size of all results
var total int
for _, r := range results {
total += len(r)
}

domainMappings := make([]model.DomainMapping, 0, total)
for _, r := range results {
domainMappings = append(domainMappings, r...)
}

return domainMappings, nil
}

Expand All @@ -95,12 +103,12 @@ func mapDomainMapping(resp *run.DomainMapping, project, region string) model.Dom
})
}
}

routeName := ""
if resp.Spec != nil {
routeName = resp.Spec.RouteName
}

createTime := time.Time{}
name := ""
creator := ""
Expand All @@ -127,4 +135,4 @@ func mapDomainMapping(resp *run.DomainMapping, project, region string) model.Dom
CreateTime: createTime,
Conditions: conditions,
}
}
}
8 changes: 4 additions & 4 deletions internal/run/api/domainmapping/domainmapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func TestList_AllRegions(t *testing.T) {

dms, err := List("p", "all")
assert.NoError(t, err)

// Since api_region.List() returns many regions, we just want to ensure we called List for them and aggregated results.
// We mocked return for "us-central1".
found := false
Expand Down Expand Up @@ -212,7 +212,7 @@ func TestGCPClient_ListDomainMappings(t *testing.T) {
ListFunc: func(parent string, pageToken string) (*run.ListDomainMappingsResponse, error) {
if pageToken == "" {
return &run.ListDomainMappingsResponse{
Items: []*run.DomainMapping{{Metadata: &run.ObjectMeta{Name: "dm1"}}},
Items: []*run.DomainMapping{{Metadata: &run.ObjectMeta{Name: "dm1"}}},
Metadata: &run.ListMeta{Continue: "next-page"},
}, nil
}
Expand Down Expand Up @@ -245,7 +245,7 @@ func TestGCPClient_ListDomainMappings(t *testing.T) {
})

t.Run("ClientCreationError", func(t *testing.T) {
// Reset auth mock for this test
// Reset auth mock for this test
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
}
Expand All @@ -259,7 +259,7 @@ func TestGCPClient_ListDomainMappings(t *testing.T) {
})

t.Run("ListError", func(t *testing.T) {
// Reset auth mock for this test
// Reset auth mock for this test
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
}
Expand Down
8 changes: 4 additions & 4 deletions internal/run/api/job/execution/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func mapExecution(resp *runpb.Execution, region string) model.Execution {
// We can try to find the "Completed" or "Succeeded" condition or check terminal condition logic if exposed directly.
// runpb.Execution doesn't have a direct TerminalCondition field like Job, but it has Conditions.
// We usually look for "Completed" type.

// Helper to find latest relevant condition or just map all of them.
var conditions []*condition.Condition
for _, c := range resp.Conditions {
Expand All @@ -66,7 +66,7 @@ func mapExecution(resp *runpb.Execution, region string) model.Execution {
LastTransitionTime: c.LastTransitionTime.AsTime(),
}
conditions = append(conditions, cond)

// Heuristic: If condition is "Completed", treat as terminal status for summary
if c.Type == "Completed" {
terminalCondition = cond
Expand Down Expand Up @@ -151,10 +151,10 @@ func (c *GCPClient) ListExecutions(ctx context.Context, project, region, jobName
// BUT, executions are children of Jobs? No, they are children of Location.
// The resource name is projects/*/locations/*/jobs/*/executions/ -> v1
// v2: projects/*/locations/*/jobs/*/executions

// Wait, `runpb.ListExecutionsRequest` expects Parent = `projects/{project}/locations/{location}/jobs/{job}` OR `projects/{project}/locations/{location}`.
// If we can pass the job as parent, we get filtered list!

parent := jobName
if !strings.HasPrefix(jobName, "projects/") {
parent = fmt.Sprintf("projects/%s/locations/%s/jobs/%s", project, region, jobName)
Expand Down
30 changes: 19 additions & 11 deletions internal/run/api/job/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,35 @@ func mapJob(resp *runpb.Job, region string) model.Job {
}

func listAllRegions(project string) ([]model.Job, error) {
var (
mu sync.Mutex
jobs []model.Job
wg sync.WaitGroup
)
regions := api_region.List()
results := make([][]model.Job, len(regions))
var wg sync.WaitGroup

for _, region := range api_region.List() {
for i, region := range regions {
wg.Add(1)
go func(r string) {
go func(idx int, r string) {
defer wg.Done()
// Call List recursively for each region
// We ignore errors here to allow partial success (e.g. if one region is down or disabled)
if j, err := List(project, r); err == nil {
mu.Lock()
jobs = append(jobs, j...)
mu.Unlock()
results[idx] = j
}
}(region)
}(i, region)
}

wg.Wait()

// Pre-allocate the final flat slice based on the exact total size of all results
var total int
for _, r := range results {
total += len(r)
}

jobs := make([]model.Job, 0, total)
for _, r := range results {
jobs = append(jobs, r...)
}

return jobs, nil
}

Expand Down
30 changes: 15 additions & 15 deletions internal/run/api/job/job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ func TestMapJob(t *testing.T) {
assert.Equal(t, resp.Name, result.Name)
assert.Equal(t, "user@example.com", result.Creator)
assert.Equal(t, "us-central1", result.Region)

// Execution
assert.NotNil(t, result.LatestCreatedExecution)
assert.Equal(t, resp.LatestCreatedExecution.Name, result.LatestCreatedExecution.Name)
assert.Equal(t, now.Unix(), result.LatestCreatedExecution.CreateTime.Unix())

// Condition
assert.NotNil(t, result.TerminalCondition)
assert.Equal(t, "CONDITION_SUCCEEDED", result.TerminalCondition.State)
Expand Down Expand Up @@ -186,7 +186,7 @@ func TestList_AllRegions(t *testing.T) {

jobs, err := List("p", api_region.ALL)
assert.NoError(t, err)

found := false
for _, j := range jobs {
if j.Name == "job-us" && j.Region == "us-central1" {
Expand Down Expand Up @@ -295,7 +295,7 @@ func TestGCPClient_ListJobs(t *testing.T) {
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to find default credentials")
})

t.Run("Client Creation Error", func(t *testing.T) {
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
Expand Down Expand Up @@ -328,7 +328,7 @@ func TestGCPClient_ListJobs(t *testing.T) {
assert.Error(t, err)
assert.Contains(t, err.Error(), "iter error")
})

t.Run("Iterator Auth Error", func(t *testing.T) {
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
Expand Down Expand Up @@ -357,7 +357,7 @@ func TestGCPClient_RunJob(t *testing.T) {
client.FindDefaultCredentials = origFindCreds
createJobsClient = origCreateClient
}()

client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
}
Expand All @@ -375,13 +375,13 @@ func TestGCPClient_RunJob(t *testing.T) {
CloseFunc: func() error { return nil },
}, nil
}

client := &GCPClient{}
exec, err := client.RunJob(context.Background(), "job1")
assert.NoError(t, err)
assert.Equal(t, "exec-1", exec.Name)
})

t.Run("Run Error", func(t *testing.T) {
createJobsClient = func(ctx context.Context, opts ...option.ClientOption) (JobsClientWrapper, error) {
return &MockJobsClientWrapper{
Expand All @@ -391,18 +391,18 @@ func TestGCPClient_RunJob(t *testing.T) {
CloseFunc: func() error { return nil },
}, nil
}

client := &GCPClient{}
_, err := client.RunJob(context.Background(), "job1")
assert.Error(t, err)
assert.Contains(t, err.Error(), "run failed")
})

t.Run("Client Creation Error", func(t *testing.T) {
createJobsClient = func(ctx context.Context, opts ...option.ClientOption) (JobsClientWrapper, error) {
return nil, errors.New("client creation error")
}

client := &GCPClient{}
_, err := client.RunJob(context.Background(), "job1")
assert.Error(t, err)
Expand All @@ -412,21 +412,21 @@ func TestGCPClient_RunJob(t *testing.T) {

func TestWrappers_Delegation(t *testing.T) {
// Expect panics because nil clients are used

t.Run("GCPJobsClientWrapper", func(t *testing.T) {
w := &GCPJobsClientWrapper{client: nil}
assert.Panics(t, func() { _ = w.ListJobs(context.Background(), nil) })
assert.Panics(t, func() { _, _ = w.RunJob(context.Background(), nil) })
assert.Panics(t, func() { _ = w.Close() })
})

t.Run("GCPJobIteratorWrapper", func(t *testing.T) {
it := &GCPJobIteratorWrapper{it: nil}
assert.Panics(t, func() { _, _ = it.Next() })
})

t.Run("GCPRunJobOperationWrapper", func(t *testing.T) {
op := &GCPRunJobOperationWrapper{op: nil}
assert.Panics(t, func() { _, _ = op.Wait(context.Background()) })
})
}
}
12 changes: 6 additions & 6 deletions internal/run/api/log/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func TestGCPClient(t *testing.T) {
entry, err := it.Next()
assert.NoError(t, err)
assert.Equal(t, "log1", entry.Payload)

assert.NoError(t, client.Close())
})

Expand All @@ -293,7 +293,7 @@ func TestGCPClient(t *testing.T) {
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return nil, errors.New("auth failed")
}

_, err := NewGCPClient(context.Background(), "project")
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to find default credentials")
Expand All @@ -307,7 +307,7 @@ func TestGCPClient(t *testing.T) {
createLogAdminClient = func(ctx context.Context, projectID string, opts ...option.ClientOption) (LogAdminClientWrapper, error) {
return nil, errors.New("creation failed")
}

_, err := NewGCPClient(context.Background(), "project")
assert.Error(t, err)
assert.Contains(t, err.Error(), "creation failed")
Expand Down Expand Up @@ -355,15 +355,15 @@ func TestGCPClient(t *testing.T) {

func TestWrappers_Delegation(t *testing.T) {
// Expect panics because nil clients are used

t.Run("RealLogAdminClient", func(t *testing.T) {
w := &RealLogAdminClient{client: nil}
assert.Panics(t, func() { _ = w.Entries(context.Background()) })
assert.Panics(t, func() { _ = w.Close() })
})

t.Run("GCPEntryIterator", func(t *testing.T) {
it := &GCPEntryIterator{it: nil}
assert.Panics(t, func() { _, _ = it.Next() })
})
}
}
2 changes: 1 addition & 1 deletion internal/run/api/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ var apiClient Client = &GCPClient{}
func List() ([]model.Project, error) {
ctx := context.Background()
return apiClient.ListProjects(ctx)
}
}
Loading
Loading