From fa717f546f446ab06cbedb7dee3da23aee504533 Mon Sep 17 00:00:00 2001 From: Sundaram Kumar Jha Date: Fri, 22 Nov 2024 19:33:44 +0530 Subject: [PATCH] added test comment (#124) * added test comment * resolved warnings * updated go version * formated ...go fmt ./... --- .github/workflows/build.yaml | 2 +- main.go | 3 +- program/aws/aws.go | 6 +- program/aws/config.go | 4 +- program/aws/program.go | 2 +- program/aws/stats.go | 2 +- program/aws/version.go | 2 +- program/azure/azure.go | 210 ++++++++++----------- program/azure/config.go | 2 +- program/azure/program.go | 232 +++++++++++------------ program/gcp/config.go | 2 +- program/gcp/gcp.go | 352 +++++++++++++++++------------------ program/gcp/program.go | 228 +++++++++++------------ program/gcp/stats.go | 2 +- 14 files changed, 525 insertions(+), 524 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8371791..2a4a755 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -5,7 +5,7 @@ on: name: Build env: - GO_VERSION: 1.19 + GO_VERSION: 1.21 jobs: build: diff --git a/main.go b/main.go index c45bab4..625287c 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "github.com/rs/zerolog/log" ) +// main function func main() { if len(os.Args) < 2 { fmt.Println("Please provide (aws, gcp or azure) as the first argument.") @@ -50,7 +51,7 @@ func main() { log.Err(err).Msg("Program failed for AWS") os.Exit(1) } - + case "azure": ctx, err = optionsAZURE.Parse(os.Args[2:]) if err != nil { diff --git a/program/aws/aws.go b/program/aws/aws.go index cdec206..83356cc 100644 --- a/program/aws/aws.go +++ b/program/aws/aws.go @@ -125,7 +125,7 @@ func (program *Options) getUniqueSessions() <-chan *sessionInfo { for _, region := range program.Regions { wg.Add(1) - go func(profile, region, account string) { + go func(profile, region, account string, info *sessionInfo) { defer wg.Done() if region != info.region { log := log.With().Str("profile", info.profile).Str("region", region).Logger() @@ -143,7 +143,7 @@ func (program *Options) getUniqueSessions() <-chan *sessionInfo { log.Error().Err(err).Msg("Failed to create session") } } - }(info.profile, region, info.account) + }(info.profile, region, info.account, info) } } } @@ -202,4 +202,4 @@ func NewSession(profile, region string, log zerolog.Logger) (*sessionInfo, error } else { return nil, err } -} \ No newline at end of file +} diff --git a/program/aws/config.go b/program/aws/config.go index 07ca3dd..13eea5b 100644 --- a/program/aws/config.go +++ b/program/aws/config.go @@ -96,7 +96,7 @@ func (program *Options) WriteConfig(config *api.Config) error { } if err := os.Rename(program.KubeConfig, bakFile); err == nil { - if e2 := os.Rename(newFile, program.KubeConfig); err == nil { + if e2 := os.Rename(newFile, program.KubeConfig); e2 == nil { return nil } else { // There was an error renaming the new file, so restore the bak file @@ -109,4 +109,4 @@ func (program *Options) WriteConfig(config *api.Config) error { } else { return err } -} \ No newline at end of file +} diff --git a/program/aws/program.go b/program/aws/program.go index 7cf31ff..fbbfcaf 100644 --- a/program/aws/program.go +++ b/program/aws/program.go @@ -142,4 +142,4 @@ func isTerminal(file *os.File) bool { } else { return (fileInfo.Mode() & os.ModeCharDevice) != 0 } -} \ No newline at end of file +} diff --git a/program/aws/stats.go b/program/aws/stats.go index 7e76bf0..698ebcb 100644 --- a/program/aws/stats.go +++ b/program/aws/stats.go @@ -21,4 +21,4 @@ func (s *Stats) Log() { Msg("Statistics") } -var stats Stats \ No newline at end of file +var stats Stats diff --git a/program/aws/version.go b/program/aws/version.go index 18490f7..96595b5 100644 --- a/program/aws/version.go +++ b/program/aws/version.go @@ -14,4 +14,4 @@ func (v *VersionCmd) Run(program *Options) error { _ = program _, _ = fmt.Println(Version) return nil -} \ No newline at end of file +} diff --git a/program/azure/azure.go b/program/azure/azure.go index d6f7ce4..6fc1984 100644 --- a/program/azure/azure.go +++ b/program/azure/azure.go @@ -60,116 +60,116 @@ func (program *Options) getSubscriptions() <-chan string { } func (program *Options) getClustersFrom(s *azureSessionInfo, clusters chan<- AzureClusterInfo) { - var wg sync.WaitGroup - defer wg.Wait() - - client := s.client - ctx := context.Background() - uniqueClusters := make(map[string]struct{}) - - pager := client.NewListPager(nil) - - for pager.More() { - page, err := pager.NextPage(ctx) - if err != nil { - s.log.Error().Err(err).Msg("Error listing AKS clusters") - return - } - - for _, c := range page.Value { - clusterKey := fmt.Sprintf("%s-%s", *c.Name, *c.Location) - if _, exists := uniqueClusters[clusterKey]; !exists { - uniqueClusters[clusterKey] = struct{}{} - stats.Clusters.Add(1) - - wg.Add(1) - go func(c *armcontainerservice.ManagedCluster) { - defer wg.Done() - - s.log.Debug(). - Str("cluster_name", *c.Name). - Str("location", *c.Location). - Msg("Found unique AKS cluster") - - clusters <- AzureClusterInfo{ - ManagedCluster: c, - log: s.log.With().Str("cluster_name", *c.Name).Str("location", *c.Location).Logger(), - session: s, - } - }(c) - } - } - } + var wg sync.WaitGroup + defer wg.Wait() + + client := s.client + ctx := context.Background() + uniqueClusters := make(map[string]struct{}) + + pager := client.NewListPager(nil) + + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + s.log.Error().Err(err).Msg("Error listing AKS clusters") + return + } + + for _, c := range page.Value { + clusterKey := fmt.Sprintf("%s-%s", *c.Name, *c.Location) + if _, exists := uniqueClusters[clusterKey]; !exists { + uniqueClusters[clusterKey] = struct{}{} + stats.Clusters.Add(1) + + wg.Add(1) + go func(c *armcontainerservice.ManagedCluster) { + defer wg.Done() + + s.log.Debug(). + Str("cluster_name", *c.Name). + Str("location", *c.Location). + Msg("Found unique AKS cluster") + + clusters <- AzureClusterInfo{ + ManagedCluster: c, + log: s.log.With().Str("cluster_name", *c.Name).Str("location", *c.Location).Logger(), + session: s, + } + }(c) + } + } + } } func (program *Options) getUniqueAzureSessions() <-chan *azureSessionInfo { - sessions := make(chan *azureSessionInfo) - - go func() { - wg := sync.WaitGroup{} - defer close(sessions) - defer wg.Wait() - - subscriptions := make(map[string]bool) - for info := range program.getSubscriptionSessions() { - if _, found := subscriptions[info.subscription]; found { - info.log.Debug().Msg("Subscription is duplicate") - continue - } - - stats.UniqueSubscriptions.Add(1) - info.log.Debug().Msg("Subscription is good for use") - subscriptions[info.subscription] = true - sessions <- info - - for _, location := range program.Locations { - if location != info.location { - stats.Locations.Add(1) - wg.Add(1) - go func(subscription, location string) { - defer wg.Done() - log := log.With().Str("subscription", subscription).Str("location", location).Logger() - log.Debug().Msg("Creating regional session") - - if s, err := program.newAzureSession(subscription, location); err == nil { - sessions <- s - } else { - log.Error().Err(err).Msg("Failed to create Azure session") - } - }(info.subscription, location) - } - } - } - }() - - return sessions + sessions := make(chan *azureSessionInfo) + + go func() { + wg := sync.WaitGroup{} + defer close(sessions) + defer wg.Wait() + + subscriptions := make(map[string]bool) + for info := range program.getSubscriptionSessions() { + if _, found := subscriptions[info.subscription]; found { + info.log.Debug().Msg("Subscription is duplicate") + continue + } + + stats.UniqueSubscriptions.Add(1) + info.log.Debug().Msg("Subscription is good for use") + subscriptions[info.subscription] = true + sessions <- info + + for _, location := range program.Locations { + if location != info.location { + stats.Locations.Add(1) + wg.Add(1) + go func(subscription, location string) { + defer wg.Done() + log := log.With().Str("subscription", subscription).Str("location", location).Logger() + log.Debug().Msg("Creating regional session") + + if s, err := program.newAzureSession(subscription, location); err == nil { + sessions <- s + } else { + log.Error().Err(err).Msg("Failed to create Azure session") + } + }(info.subscription, location) + } + } + } + }() + + return sessions } func (program *Options) getSubscriptionSessions() <-chan *azureSessionInfo { - sessions := make(chan *azureSessionInfo) - wg := sync.WaitGroup{} - - go func() { - defer close(sessions) - defer wg.Wait() - - subscriptions := program.getSubscriptions() - - for s := range subscriptions { - stats.Subscriptions.Add(1) - log := log.With().Str("subscription", s).Str("location", program.Locations[0]).Logger() - wg.Add(1) - go func(s string) { - defer wg.Done() - if session, err := NewAzureSession(s, program.Locations[0], log); err == nil { - stats.UsableSubscriptions.Add(1) - sessions <- session - } - }(s) - } - }() - - return sessions + sessions := make(chan *azureSessionInfo) + wg := sync.WaitGroup{} + + go func() { + defer close(sessions) + defer wg.Wait() + + subscriptions := program.getSubscriptions() + + for s := range subscriptions { + stats.Subscriptions.Add(1) + log := log.With().Str("subscription", s).Str("location", program.Locations[0]).Logger() + wg.Add(1) + go func(s string) { + defer wg.Done() + if session, err := NewAzureSession(s, program.Locations[0], log); err == nil { + stats.UsableSubscriptions.Add(1) + sessions <- session + } + }(s) + } + }() + + return sessions } func (program *Options) newAzureSession(subscription, location string) (*azureSessionInfo, error) { @@ -219,4 +219,4 @@ func NewAzureSession(subscription, location string, log zerolog.Logger) (*azureS client: client, log: log, }, nil -} \ No newline at end of file +} diff --git a/program/azure/config.go b/program/azure/config.go index 01ecbbf..ddbba35 100644 --- a/program/azure/config.go +++ b/program/azure/config.go @@ -9,7 +9,7 @@ import ( ) func captureConfig(c AzureClusterInfo, resourceGroup string, i *api.Config) error { - certificateData := []byte(*c.ManagedCluster.Properties.NetworkProfile.ServiceCidr) + certificateData := []byte(*c.ManagedCluster.Properties.NetworkProfile.ServiceCidr) cluster := api.Cluster{ Server: "https://" + *c.ManagedCluster.Properties.Fqdn, diff --git a/program/azure/program.go b/program/azure/program.go index 5bcd01e..d58510d 100644 --- a/program/azure/program.go +++ b/program/azure/program.go @@ -16,136 +16,136 @@ import ( ) type Options struct { - Version bool `help:"Show program version"` + Version bool `help:"Show program version"` - KubeConfig string `group:"Input" short:"k" help:"Kubeconfig file" type:"path" default:"~/.kube/config"` - Subscriptions []string `group:"Input" help:"List of Azure subscriptions to check"` - SubscriptionFile string `group:"Input" help:"File containing list of Azure subscriptions" type:"path"` - Locations []string `group:"Input" help:"List of Azure locations to check" env:"AZURE_LOCATIONS" default:"eastus,westus,centralus,northeurope,westeurope"` - ResourceGroups []string `group:"Input" help:"List of Azure resource groups to check"` + KubeConfig string `group:"Input" short:"k" help:"Kubeconfig file" type:"path" default:"~/.kube/config"` + Subscriptions []string `group:"Input" help:"List of Azure subscriptions to check"` + SubscriptionFile string `group:"Input" help:"File containing list of Azure subscriptions" type:"path"` + Locations []string `group:"Input" help:"List of Azure locations to check" env:"AZURE_LOCATIONS" default:"eastus,westus,centralus,northeurope,westeurope"` + ResourceGroups []string `group:"Input" help:"List of Azure resource groups to check"` - Debug bool `group:"Info" help:"Show debugging information"` - OutputFormat string `group:"Info" enum:"auto,jsonl,terminal" default:"auto" help:"How to show program output (auto|terminal|jsonl)"` - Quiet bool `group:"Info" help:"Be less verbose than usual"` + Debug bool `group:"Info" help:"Show debugging information"` + OutputFormat string `group:"Info" enum:"auto,jsonl,terminal" default:"auto" help:"How to show program output (auto|terminal|jsonl)"` + Quiet bool `group:"Info" help:"Be less verbose than usual"` } func (program *Options) Parse(args []string) (*kong.Context, error) { - parser, err := kong.New(program, - kong.ShortUsageOnError(), - kong.Description("Download kubeconfigs in bulk by examining AKS clusters across multiple subscriptions and locations"), - ) - if err != nil { - fmt.Println(err) - return nil, err - } - return parser.Parse(args) + parser, err := kong.New(program, + kong.ShortUsageOnError(), + kong.Description("Download kubeconfigs in bulk by examining AKS clusters across multiple subscriptions and locations"), + ) + if err != nil { + fmt.Println(err) + return nil, err + } + return parser.Parse(args) } func (program *Options) Run(options *Options) error { - config, err := program.ReadConfig() - if err != nil { - log.Error().Err(err).Msg("Failed to read kubeconfig file") - return err - } - - clusters := make(chan AzureClusterInfo) - wg := sync.WaitGroup{} - - for sess := range program.getUniqueAzureSessions() { - wg.Add(1) - go func(sess *azureSessionInfo) { - defer wg.Done() - program.getClustersFrom(sess, clusters) - }(sess) - } - - go func() { - wg.Wait() - close(clusters) - }() - - for c := range clusters { - resourceGroup := "" - if id := *c.ManagedCluster.ID; id != "" { - parts := strings.Split(id, "/") - for i, part := range parts { - if part == "resourceGroups" && i+1 < len(parts) { - resourceGroup = parts[i+1] - break - } - } - } - - if err := captureConfig(c, resourceGroup, config); err != nil { - stats.Errors.Add(1) - log.Error().Err(err).Msg("Error capturing cluster configuration") - } - } - - if err := program.WriteConfig(config); err != nil { - stats.Errors.Add(1) - log.Error(). - Err(err). - Str("file", program.KubeConfig). - Msg("Error saving kubeconfig") - } - - stats.Log() - if stats.Errors.Load() > 0 { - return errors.New("Errors encountered during run") - } - return nil + config, err := program.ReadConfig() + if err != nil { + log.Error().Err(err).Msg("Failed to read kubeconfig file") + return err + } + + clusters := make(chan AzureClusterInfo) + wg := sync.WaitGroup{} + + for sess := range program.getUniqueAzureSessions() { + wg.Add(1) + go func(sess *azureSessionInfo) { + defer wg.Done() + program.getClustersFrom(sess, clusters) + }(sess) + } + + go func() { + wg.Wait() + close(clusters) + }() + + for c := range clusters { + resourceGroup := "" + if id := *c.ManagedCluster.ID; id != "" { + parts := strings.Split(id, "/") + for i, part := range parts { + if part == "resourceGroups" && i+1 < len(parts) { + resourceGroup = parts[i+1] + break + } + } + } + + if err := captureConfig(c, resourceGroup, config); err != nil { + stats.Errors.Add(1) + log.Error().Err(err).Msg("Error capturing cluster configuration") + } + } + + if err := program.WriteConfig(config); err != nil { + stats.Errors.Add(1) + log.Error(). + Err(err). + Str("file", program.KubeConfig). + Msg("Error saving kubeconfig") + } + + stats.Log() + if stats.Errors.Load() > 0 { + return errors.New("Errors encountered during run") + } + return nil } func (program *Options) AfterApply() error { - program.initLogging() - if len(program.Locations) < 1 { - return errors.New("Must specify at least one location") - } - if len(program.Subscriptions) < 1 && program.SubscriptionFile == "" { - return errors.New("Must specify either subscriptions or subscription file") - } - return nil + program.initLogging() + if len(program.Locations) < 1 { + return errors.New("Must specify at least one location") + } + if len(program.Subscriptions) < 1 && program.SubscriptionFile == "" { + return errors.New("Must specify either subscriptions or subscription file") + } + return nil } func (program *Options) initLogging() { - if program.Version { - fmt.Println(Version) - os.Exit(0) - } - - switch { - case program.Debug: - zerolog.SetGlobalLevel(zerolog.DebugLevel) - case program.Quiet: - zerolog.SetGlobalLevel(zerolog.WarnLevel) - default: - zerolog.SetGlobalLevel(zerolog.InfoLevel) - } - - var out io.Writer = os.Stdout - if os.Getenv("TERM") == "" && runtime.GOOS == "windows" { - out = colorable.NewColorableStdout() - } - - if program.OutputFormat == "terminal" || - (program.OutputFormat == "auto" && isTerminal(os.Stdout)) { - log.Logger = log.Output(zerolog.ConsoleWriter{Out: out}) - } else { - log.Logger = log.Output(out) - } - - log.Logger.Debug(). - Str("version", Version). - Str("program", os.Args[0]). - Msg("Starting") + if program.Version { + fmt.Println(Version) + os.Exit(0) + } + + switch { + case program.Debug: + zerolog.SetGlobalLevel(zerolog.DebugLevel) + case program.Quiet: + zerolog.SetGlobalLevel(zerolog.WarnLevel) + default: + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } + + var out io.Writer = os.Stdout + if os.Getenv("TERM") == "" && runtime.GOOS == "windows" { + out = colorable.NewColorableStdout() + } + + if program.OutputFormat == "terminal" || + (program.OutputFormat == "auto" && isTerminal(os.Stdout)) { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: out}) + } else { + log.Logger = log.Output(out) + } + + log.Logger.Debug(). + Str("version", Version). + Str("program", os.Args[0]). + Msg("Starting") } func isTerminal(file *os.File) bool { - if fileInfo, err := file.Stat(); err != nil { - log.Err(err).Msg("Error running stat") - return false - } else { - return (fileInfo.Mode() & os.ModeCharDevice) != 0 - } -} \ No newline at end of file + if fileInfo, err := file.Stat(); err != nil { + log.Err(err).Msg("Error running stat") + return false + } else { + return (fileInfo.Mode() & os.ModeCharDevice) != 0 + } +} diff --git a/program/gcp/config.go b/program/gcp/config.go index b1608a3..3f244f9 100644 --- a/program/gcp/config.go +++ b/program/gcp/config.go @@ -98,4 +98,4 @@ func (program *Options) WriteConfig(config *api.Config) error { } else { return err } -} \ No newline at end of file +} diff --git a/program/gcp/gcp.go b/program/gcp/gcp.go index 3994780..df040cd 100644 --- a/program/gcp/gcp.go +++ b/program/gcp/gcp.go @@ -15,205 +15,205 @@ import ( ) type gcpSessionInfo struct { - project string - zone string - session *container.ClusterManagerClient - log zerolog.Logger + project string + zone string + session *container.ClusterManagerClient + log zerolog.Logger } type GCPClusterInfo struct { - *containerpb.Cluster - log zerolog.Logger - session *gcpSessionInfo + *containerpb.Cluster + log zerolog.Logger + session *gcpSessionInfo } func (program *Options) getProjects() <-chan string { - output := make(chan string) - - if len(program.Projects) < 1 { - go func() { - defer close(output) - if f, err := os.Open(program.ProjectFile); err == nil { - scanner := bufio.NewScanner(f) - scanner.Split(bufio.ScanLines) - - for scanner.Scan() { - s := strings.TrimSpace(scanner.Text()) - if s != "" { - output <- s - } - } - - } else { - log.Error().Str("file", program.ProjectFile).Err(err).Msg("Failed to open project file") - } - }() - } else { - go func() { - defer close(output) - for _, p := range program.Projects { - output <- p - } - }() - } - - return output + output := make(chan string) + + if len(program.Projects) < 1 { + go func() { + defer close(output) + if f, err := os.Open(program.ProjectFile); err == nil { + scanner := bufio.NewScanner(f) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + s := strings.TrimSpace(scanner.Text()) + if s != "" { + output <- s + } + } + + } else { + log.Error().Str("file", program.ProjectFile).Err(err).Msg("Failed to open project file") + } + }() + } else { + go func() { + defer close(output) + for _, p := range program.Projects { + output <- p + } + }() + } + + return output } func (program *Options) getClustersFrom(s *gcpSessionInfo, clusters chan<- GCPClusterInfo) { - wg := sync.WaitGroup{} - defer wg.Wait() - - req := &containerpb.ListClustersRequest{ - Parent: "projects/" + s.project + "/locations/" + s.zone, - } - - s.log.Debug().Str("request", req.Parent).Msg("Requesting cluster listing") - - out, err := s.session.ListClusters(context.Background(), req) - if err != nil { - s.log.Error().Err(err).Msg("Error listing GKE clusters") - return - } - - s.log.Debug().Int("number_of_clusters", len(out.Clusters)).Msg("GKE clusters found") - - if len(out.Clusters) > 0 { - stats.Clusters.Add(int32(len(out.Clusters))) - } - - if len(out.Clusters) == 0 { - s.log.Warn().Msg("No GKE clusters found in the specified project and zone") - } - - for _, c := range out.Clusters { - wg.Add(1) - go func(c *containerpb.Cluster) { - defer wg.Done() - s.log.Debug().Str("cluster_name", c.Name).Msg("Found GKE cluster") - clusters <- GCPClusterInfo{ - Cluster: c, - log: s.log.With().Str("cluster_name", c.Name).Logger(), - session: s, - } - }(c) - } + wg := sync.WaitGroup{} + defer wg.Wait() + + req := &containerpb.ListClustersRequest{ + Parent: "projects/" + s.project + "/locations/" + s.zone, + } + + s.log.Debug().Str("request", req.Parent).Msg("Requesting cluster listing") + + out, err := s.session.ListClusters(context.Background(), req) + if err != nil { + s.log.Error().Err(err).Msg("Error listing GKE clusters") + return + } + + s.log.Debug().Int("number_of_clusters", len(out.Clusters)).Msg("GKE clusters found") + + if len(out.Clusters) > 0 { + stats.Clusters.Add(int32(len(out.Clusters))) + } + + if len(out.Clusters) == 0 { + s.log.Warn().Msg("No GKE clusters found in the specified project and zone") + } + + for _, c := range out.Clusters { + wg.Add(1) + go func(c *containerpb.Cluster) { + defer wg.Done() + s.log.Debug().Str("cluster_name", c.Name).Msg("Found GKE cluster") + clusters <- GCPClusterInfo{ + Cluster: c, + log: s.log.With().Str("cluster_name", c.Name).Logger(), + session: s, + } + }(c) + } } func (program *Options) getUniqueGCPSessions() <-chan *gcpSessionInfo { - sessions := make(chan *gcpSessionInfo) - - go func() { - wg := sync.WaitGroup{} - defer close(sessions) - defer wg.Wait() - - projects := make(map[string]bool) - for info := range program.getProjectSessions() { - if _, found := projects[info.project]; found { - info.log.Debug().Msg("Project is duplicate") - continue - } - - info.log.Debug().Msg("Project is good for use") - projects[info.project] = true - stats.UniqueProjects.Add(1) - sessions <- info - - if strings.Count(info.zone, "-") == 2 { - for _, zone := range program.Zones { - if zone != info.zone { - wg.Add(1) - go func(project, zone string) { - defer wg.Done() - log := log.With().Str("project", project).Str("zone", zone).Logger() - log.Debug().Msg("Creating regional session") - - if s, err := program.newGCPSession(project, zone); err == nil { - sessions <- s - } else { - log.Error().Err(err).Msg("Failed to create GCP session") - } - }(info.project, zone) - } - } - } - } - }() - - return sessions + sessions := make(chan *gcpSessionInfo) + + go func() { + wg := sync.WaitGroup{} + defer close(sessions) + defer wg.Wait() + + projects := make(map[string]bool) + for info := range program.getProjectSessions() { + if _, found := projects[info.project]; found { + info.log.Debug().Msg("Project is duplicate") + continue + } + + info.log.Debug().Msg("Project is good for use") + projects[info.project] = true + stats.UniqueProjects.Add(1) + sessions <- info + + if strings.Count(info.zone, "-") == 2 { + for _, zone := range program.Zones { + if zone != info.zone { + wg.Add(1) + go func(project, zone string) { + defer wg.Done() + log := log.With().Str("project", project).Str("zone", zone).Logger() + log.Debug().Msg("Creating regional session") + + if s, err := program.newGCPSession(project, zone); err == nil { + sessions <- s + } else { + log.Error().Err(err).Msg("Failed to create GCP session") + } + }(info.project, zone) + } + } + } + } + }() + + return sessions } func (program *Options) getProjectSessions() <-chan *gcpSessionInfo { - sessions := make(chan *gcpSessionInfo) - wg := sync.WaitGroup{} + sessions := make(chan *gcpSessionInfo) + wg := sync.WaitGroup{} - go func() { - defer close(sessions) - defer wg.Wait() + go func() { + defer close(sessions) + defer wg.Wait() - projects := program.getProjects() + projects := program.getProjects() - for p := range projects { - log := log.With().Str("project", p).Str("zone", program.Zones[0]).Logger() - wg.Add(1) - go func(p string) { - defer wg.Done() - if s, err := NewGCPSession(p, program.Zones[0], log); err == nil { - sessions <- s - } - }(p) - } - }() + for p := range projects { + log := log.With().Str("project", p).Str("zone", program.Zones[0]).Logger() + wg.Add(1) + go func(p string) { + defer wg.Done() + if s, err := NewGCPSession(p, program.Zones[0], log); err == nil { + sessions <- s + } + }(p) + } + }() - return sessions + return sessions } func (program *Options) newGCPSession(project, zone string) (*gcpSessionInfo, error) { - opts := []option.ClientOption{} - if program.CredentialsFile != "" { - opts = append(opts, option.WithCredentialsFile(program.CredentialsFile)) - } - - sess, err := container.NewClusterManagerClient(context.Background(), opts...) - if err != nil { - return nil, err - } - - stats.Projects.Add(1) - stats.UsableProjects.Add(1) - - logger := log.With().Str("project", project).Str("zone", zone).Logger() - logger.Debug().Msg("GCP project session created") - - return &gcpSessionInfo{ - project: project, - zone: zone, - session: sess, - log: logger, - }, nil + opts := []option.ClientOption{} + if program.CredentialsFile != "" { + opts = append(opts, option.WithCredentialsFile(program.CredentialsFile)) + } + + sess, err := container.NewClusterManagerClient(context.Background(), opts...) + if err != nil { + return nil, err + } + + stats.Projects.Add(1) + stats.UsableProjects.Add(1) + + logger := log.With().Str("project", project).Str("zone", zone).Logger() + logger.Debug().Msg("GCP project session created") + + return &gcpSessionInfo{ + project: project, + zone: zone, + session: sess, + log: logger, + }, nil } func NewGCPSession(project, zone string, log zerolog.Logger) (*gcpSessionInfo, error) { - opts := []option.ClientOption{} - if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") != "" { - opts = append(opts, option.WithCredentialsFile(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"))) - } - - sess, err := container.NewClusterManagerClient(context.Background(), opts...) - if err != nil { - log.Error().Err(err).Msg("Failed to create GCP ClusterManagerClient") - return nil, err - } - - log.Debug().Msg("GCP project session created successfully") - - return &gcpSessionInfo{ - project: project, - zone: zone, - session: sess, - log: log, - }, nil - -} \ No newline at end of file + opts := []option.ClientOption{} + if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") != "" { + opts = append(opts, option.WithCredentialsFile(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"))) + } + + sess, err := container.NewClusterManagerClient(context.Background(), opts...) + if err != nil { + log.Error().Err(err).Msg("Failed to create GCP ClusterManagerClient") + return nil, err + } + + log.Debug().Msg("GCP project session created successfully") + + return &gcpSessionInfo{ + project: project, + zone: zone, + session: sess, + log: log, + }, nil + +} diff --git a/program/gcp/program.go b/program/gcp/program.go index 836456e..7f65855 100644 --- a/program/gcp/program.go +++ b/program/gcp/program.go @@ -1,138 +1,138 @@ package gcp import ( - "fmt" - "github.com/alecthomas/kong" - "github.com/mattn/go-colorable" - "github.com/pkg/errors" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "io" - "os" - "runtime" - "sync" + "fmt" + "github.com/alecthomas/kong" + "github.com/mattn/go-colorable" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "io" + "os" + "runtime" + "sync" ) type Options struct { - Version bool `help:"Show program version"` + Version bool `help:"Show program version"` - KubeConfig string `group:"Input" short:"k" help:"Kubeconfig file" type:"path" default:"~/.kube/config"` - CredentialsFile string `group:"Input" short:"c" help:"GCP Credentials File" type:"existingfile" default:"~/.config/gcloud/application_default_credentials.json"` - Projects []string `group:"Input" help:"List of GCP projects to check"` - ProjectFile string `group:"Input" help:"File containing list of GCP projects" type:"path"` - Zones []string `group:"Input" help:"List of GCP zones to check" env:"GCP_ZONES" default:"us-central1-a,us-east1-b,us-west1-a,europe-west1-b,asia-east1-a"` + KubeConfig string `group:"Input" short:"k" help:"Kubeconfig file" type:"path" default:"~/.kube/config"` + CredentialsFile string `group:"Input" short:"c" help:"GCP Credentials File" type:"existingfile" default:"~/.config/gcloud/application_default_credentials.json"` + Projects []string `group:"Input" help:"List of GCP projects to check"` + ProjectFile string `group:"Input" help:"File containing list of GCP projects" type:"path"` + Zones []string `group:"Input" help:"List of GCP zones to check" env:"GCP_ZONES" default:"us-central1-a,us-east1-b,us-west1-a,europe-west1-b,asia-east1-a"` - Debug bool `group:"Info" help:"Show debugging information"` - OutputFormat string `group:"Info" enum:"auto,jsonl,terminal" default:"auto" help:"How to show program output (auto|terminal|jsonl)"` - Quiet bool `group:"Info" help:"Be less verbose than usual"` + Debug bool `group:"Info" help:"Show debugging information"` + OutputFormat string `group:"Info" enum:"auto,jsonl,terminal" default:"auto" help:"How to show program output (auto|terminal|jsonl)"` + Quiet bool `group:"Info" help:"Be less verbose than usual"` } func (program *Options) Parse(args []string) (*kong.Context, error) { - parser, err := kong.New(program, - kong.ShortUsageOnError(), - kong.Description("Download kubeconfigs in bulk by examining GKE clusters across multiple projects and zones"), - ) - if err != nil { - fmt.Println(err) - return nil, err - } - return parser.Parse(args) + parser, err := kong.New(program, + kong.ShortUsageOnError(), + kong.Description("Download kubeconfigs in bulk by examining GKE clusters across multiple projects and zones"), + ) + if err != nil { + fmt.Println(err) + return nil, err + } + return parser.Parse(args) } func (program *Options) Run(options *Options) error { - config, err := program.ReadConfig() - if err != nil { - log.Error().Err(err).Msg("Failed to read kubeconfig file") - return err - } - - clusters := make(chan GCPClusterInfo) - wg := sync.WaitGroup{} - - for sess := range program.getUniqueGCPSessions() { - wg.Add(1) - go func(sess *gcpSessionInfo) { - defer wg.Done() - program.getClustersFrom(sess, clusters) - }(sess) - } - - go func() { - wg.Wait() - close(clusters) - }() - - for c := range clusters { - if err := captureConfig(c, config); err != nil { - stats.Errors.Add(1) - log.Error().Err(err).Msg("Error capturing cluster configuration") - } - } - - if err := program.WriteConfig(config); err != nil { - stats.Errors.Add(1) - log.Error(). - Err(err). - Str("file", program.KubeConfig). - Msg("Error saving kubeconfig") - } - - stats.Log() - if stats.Errors.Load() > 0 { - return errors.New("Errors encountered during run") - } - return nil + config, err := program.ReadConfig() + if err != nil { + log.Error().Err(err).Msg("Failed to read kubeconfig file") + return err + } + + clusters := make(chan GCPClusterInfo) + wg := sync.WaitGroup{} + + for sess := range program.getUniqueGCPSessions() { + wg.Add(1) + go func(sess *gcpSessionInfo) { + defer wg.Done() + program.getClustersFrom(sess, clusters) + }(sess) + } + + go func() { + wg.Wait() + close(clusters) + }() + + for c := range clusters { + if err := captureConfig(c, config); err != nil { + stats.Errors.Add(1) + log.Error().Err(err).Msg("Error capturing cluster configuration") + } + } + + if err := program.WriteConfig(config); err != nil { + stats.Errors.Add(1) + log.Error(). + Err(err). + Str("file", program.KubeConfig). + Msg("Error saving kubeconfig") + } + + stats.Log() + if stats.Errors.Load() > 0 { + return errors.New("Errors encountered during run") + } + return nil } func (program *Options) AfterApply() error { - program.initLogging() - if len(program.Zones) < 1 { - return errors.New("Must specify at least one zone") - } - if len(program.Projects) < 1 && program.ProjectFile == "" { - return errors.New("Must specify either projects or project file") - } - return nil + program.initLogging() + if len(program.Zones) < 1 { + return errors.New("Must specify at least one zone") + } + if len(program.Projects) < 1 && program.ProjectFile == "" { + return errors.New("Must specify either projects or project file") + } + return nil } func (program *Options) initLogging() { - if program.Version { - fmt.Println(Version) - os.Exit(0) - } - - switch { - case program.Debug: - zerolog.SetGlobalLevel(zerolog.DebugLevel) - case program.Quiet: - zerolog.SetGlobalLevel(zerolog.WarnLevel) - default: - zerolog.SetGlobalLevel(zerolog.InfoLevel) - } - - var out io.Writer = os.Stdout - if os.Getenv("TERM") == "" && runtime.GOOS == "windows" { - out = colorable.NewColorableStdout() - } - - if program.OutputFormat == "terminal" || - (program.OutputFormat == "auto" && isTerminal(os.Stdout)) { - log.Logger = log.Output(zerolog.ConsoleWriter{Out: out}) - } else { - log.Logger = log.Output(out) - } - - log.Logger.Debug(). - Str("version", Version). - Str("program", os.Args[0]). - Msg("Starting") + if program.Version { + fmt.Println(Version) + os.Exit(0) + } + + switch { + case program.Debug: + zerolog.SetGlobalLevel(zerolog.DebugLevel) + case program.Quiet: + zerolog.SetGlobalLevel(zerolog.WarnLevel) + default: + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } + + var out io.Writer = os.Stdout + if os.Getenv("TERM") == "" && runtime.GOOS == "windows" { + out = colorable.NewColorableStdout() + } + + if program.OutputFormat == "terminal" || + (program.OutputFormat == "auto" && isTerminal(os.Stdout)) { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: out}) + } else { + log.Logger = log.Output(out) + } + + log.Logger.Debug(). + Str("version", Version). + Str("program", os.Args[0]). + Msg("Starting") } func isTerminal(file *os.File) bool { - if fileInfo, err := file.Stat(); err != nil { - log.Err(err).Msg("Error running stat") - return false - } else { - return (fileInfo.Mode() & os.ModeCharDevice) != 0 - } + if fileInfo, err := file.Stat(); err != nil { + log.Err(err).Msg("Error running stat") + return false + } else { + return (fileInfo.Mode() & os.ModeCharDevice) != 0 + } } diff --git a/program/gcp/stats.go b/program/gcp/stats.go index 79c89c7..21a8a0d 100644 --- a/program/gcp/stats.go +++ b/program/gcp/stats.go @@ -20,4 +20,4 @@ func (s *Stats) Log() { Msg("Statistics") } -var stats Stats \ No newline at end of file +var stats Stats