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

Add JSON status (fix #956) #958

Merged
merged 1 commit into from
Feb 10, 2023
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
59 changes: 35 additions & 24 deletions clustermesh/clustermesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -468,6 +469,7 @@ type Parameters struct {
ConfigOverwrites []string
Retries int
HelmValuesSecretName string
Output string
raphink marked this conversation as resolved.
Show resolved Hide resolved
}

func (p Parameters) validateParams() error {
Expand Down Expand Up @@ -629,16 +631,16 @@ func (k *K8sClusterMesh) Enable(ctx context.Context) error {
}

type accessInformation struct {
ServiceIPs []string
ServicePort int
ClusterID string
ClusterName string
CA []byte
ClientCert []byte
ClientKey []byte
ExternalWorkloadCert []byte
ExternalWorkloadKey []byte
Tunnel string
ServiceIPs []string `json:"service_ips,omitempty"`
ServicePort int `json:"service_port,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
ClusterName string `json:"cluster_name,omitempty"`
CA []byte `json:"ca,omitempty"`
ClientCert []byte `json:"client_cert,omitempty"`
ClientKey []byte `json:"client_key,omitempty"`
ExternalWorkloadCert []byte `json:"external_workload_cert,omitempty"`
ExternalWorkloadKey []byte `json:"external_workload_key,omitempty"`
Tunnel string `json:"tunnel,omitempty"`
}

func (ai *accessInformation) etcdConfiguration() string {
Expand Down Expand Up @@ -1042,9 +1044,9 @@ func (k *K8sClusterMesh) Disconnect(ctx context.Context) error {
}

type Status struct {
AccessInformation *accessInformation
Service *corev1.Service
Connectivity *ConnectivityStatus
AccessInformation *accessInformation `json:"access_information,omitempty"`
Service *corev1.Service `json:"service,omitempty"`
raphink marked this conversation as resolved.
Show resolved Hide resolved
Connectivity *ConnectivityStatus `json:"connectivity,omitempty"`
}

func (k *K8sClusterMesh) statusAccessInformation(ctx context.Context, log bool, getExternalWorkloadSecret bool) (*accessInformation, error) {
Expand Down Expand Up @@ -1104,23 +1106,23 @@ func (k *K8sClusterMesh) waitForDeployment(ctx context.Context) error {
}

type StatisticalStatus struct {
Min int64
Avg float64
Max int64
Min int64 `json:"min,omitempty"`
Avg float64 `json:"avg,omitempty"`
Max int64 `json:"max,omitempty"`
}

type ClusterStats struct {
Configured int
Connected int
Configured int `json:"configured,omitempty"`
Connected int `json:"connected,omitempty"`
}

type ConnectivityStatus struct {
GlobalServices StatisticalStatus
Connected StatisticalStatus
Clusters map[string]*ClusterStats
Total int64
NotReady int64
Errors status.ErrorCountMapMap
GlobalServices StatisticalStatus `json:"global_services,omitempty"`
Connected StatisticalStatus `json:"connected,omitempty"`
Clusters map[string]*ClusterStats `json:"clusters,omitempty"`
Total int64 `json:"total,omitempty"`
NotReady int64 `json:"not_ready,omitempty"`
Errors status.ErrorCountMapMap `json:"errors,omitempty"`
}

func (c *ConnectivityStatus) addError(pod, cluster string, err error) {
Expand Down Expand Up @@ -1296,6 +1298,15 @@ func (k *K8sClusterMesh) Status(ctx context.Context) (*Status, error) {

s.Connectivity, err = k.statusConnectivity(ctx)

if k.params.Output == status.OutputJSON {
jsonStatus, err := json.MarshalIndent(s, "", " ")
if err != nil {
return nil, fmt.Errorf("failed to marshal status to JSON")
}
fmt.Println(string(jsonStatus))
return s, nil
}

if s.Connectivity != nil {
if s.Connectivity.NotReady > 0 {
k.Log("⚠️ %d/%d nodes are not connected to all clusters [min:%d / avg:%.1f / max:%d]",
Expand Down
8 changes: 8 additions & 0 deletions internal/cli/cmd/clustermesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/cilium/cilium-cli/clustermesh"
"github.com/cilium/cilium-cli/defaults"
"github.com/cilium/cilium-cli/status"
)

func newCmdClusterMesh() *cobra.Command {
Expand Down Expand Up @@ -152,6 +153,12 @@ func newCmdClusterMeshStatus() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
params.Namespace = namespace

if params.Output == status.OutputJSON {
// Write status log messages to stderr to make sure they don't
// clutter JSON output.
params.Writer = os.Stderr
}

cm := clustermesh.NewK8sClusterMesh(k8sClient, params)
if _, err := cm.Status(context.Background()); err != nil {
fatalf("Unable to determine status: %s", err)
Expand All @@ -163,6 +170,7 @@ func newCmdClusterMeshStatus() *cobra.Command {
cmd.Flags().BoolVar(&params.Wait, "wait", false, "Wait until status is successful")
cmd.Flags().DurationVar(&params.WaitDuration, "wait-duration", 15*time.Minute, "Maximum time to wait")
cmd.Flags().BoolVar(&params.SkipServiceCheck, "skip-service-check", false, "Do not require service IP of remote cluster to be available")
cmd.Flags().StringVarP(&params.Output, "output", "o", status.OutputSummary, "Output format. One of: json, summary")

return cmd
}
Expand Down
18 changes: 16 additions & 2 deletions internal/cli/cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ package cmd

import (
"context"
"encoding/json"
"fmt"
"os"

"github.com/spf13/cobra"

Expand All @@ -29,11 +31,22 @@ func newCmdStatus() *cobra.Command {
}

s, err := collector.Status(context.Background())
// Report the most recent status even if an error occurred.
fmt.Print(s.Format())
raphink marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
// Report the most recent status even if an error occurred.
fmt.Fprint(os.Stderr, s.Format())
fatalf("Unable to determine status: %s", err)
}
if params.Output == status.OutputJSON {
jsonStatus, err := json.MarshalIndent(s, "", " ")
if err != nil {
// Report the most recent status even if an error occurred.
fmt.Fprint(os.Stderr, s.Format())
fatalf("Unable to marshal status to JSON: %s", err)
}
fmt.Println(string(jsonStatus))
} else {
fmt.Print(s.Format())
}
return err
},
}
Expand All @@ -43,6 +56,7 @@ func newCmdStatus() *cobra.Command {
cmd.Flags().IntVar(&params.WorkerCount,
"worker-count", status.DefaultWorkerCount,
"The number of workers to use")
cmd.Flags().StringVarP(&params.Output, "output", "o", status.OutputSummary, "Output format. One of: json, summary")

return cmd
}
3 changes: 3 additions & 0 deletions status/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type K8sStatusParameters struct {

// The number of workers to use.
WorkerCount int

// The output format
Output string
raphink marked this conversation as resolved.
Show resolved Hide resolved
}

type K8sStatusCollector struct {
Expand Down
23 changes: 14 additions & 9 deletions status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const (
Reset = "\033[0m"
)

const (
OutputJSON = "json"
OutputSummary = "summary"
)

// MapCount is a map to count number of occurrences of a string
type MapCount map[string]int

Expand All @@ -35,10 +40,10 @@ type MapMapCount map[string]MapCount
// PodStateCount counts the number of pods in the k8s cluster
type PodsCount struct {
// All is the number of all pods in the k8s cluster
All int
All int `json:"all"`

// ByCilium is the number of all the pods in the k8s cluster
ByCilium int
ByCilium int `json:"by_cilium"`
}

// PodStateCount counts the number of pods in a particular state
Expand Down Expand Up @@ -77,29 +82,29 @@ type ErrorCountMapMap map[string]ErrorCountMap
type Status struct {
// ImageCount is a map counting the number of images in use indexed by
// the image name
ImageCount MapMapCount
ImageCount MapMapCount `json:"image_count,omitempty"`

// PhaseCount is a map counting the number of pods in each phase
// (running, failing, scheduled, ...)
PhaseCount MapMapCount
PhaseCount MapMapCount `json:"phase_count,omitempty"`

// PodState counts the number of pods matching conditions such as
// desired, ready, available, and unavailable
PodState PodStateMap
PodState PodStateMap `json:"pod_state,omitempty"`

// PodsCount is the number of pods in the k8s cluster
// all pods, and pods managed by cilium
PodsCount PodsCount
PodsCount PodsCount `json:"pods_count,omitempty"`

CiliumStatus CiliumStatusMap
CiliumStatus CiliumStatusMap `json:"cilium_status,omitempty"`

// Errors is the aggregated errors and warnings of all pods of a
// particular deployment type
Errors ErrorCountMapMap
Errors ErrorCountMapMap `json:"errors,omitempty"`

// CollectionErrors is the errors that accumulated while collecting the
// status
CollectionErrors []error
CollectionErrors []error `json:"collection_errors,omitempty"`

mutex *sync.Mutex
}
Expand Down