...

Source file src/sigs.k8s.io/cli-utils/pkg/printers/json/formatter.go

Documentation: sigs.k8s.io/cli-utils/pkg/printers/json

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package json
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"time"
    10  
    11  	"k8s.io/cli-runtime/pkg/genericclioptions"
    12  	"sigs.k8s.io/cli-utils/pkg/apply/event"
    13  	"sigs.k8s.io/cli-utils/pkg/common"
    14  	"sigs.k8s.io/cli-utils/pkg/object"
    15  	"sigs.k8s.io/cli-utils/pkg/object/validation"
    16  	"sigs.k8s.io/cli-utils/pkg/print/list"
    17  	"sigs.k8s.io/cli-utils/pkg/print/stats"
    18  )
    19  
    20  func NewFormatter(ioStreams genericclioptions.IOStreams,
    21  	_ common.DryRunStrategy) list.Formatter {
    22  	return &formatter{
    23  		ioStreams: ioStreams,
    24  		now:       time.Now,
    25  	}
    26  }
    27  
    28  type formatter struct {
    29  	ioStreams genericclioptions.IOStreams
    30  	now       func() time.Time
    31  }
    32  
    33  func (jf *formatter) FormatValidationEvent(ve event.ValidationEvent) error {
    34  	// unwrap validation errors
    35  	err := ve.Error
    36  	if vErr, ok := err.(*validation.Error); ok {
    37  		err = vErr.Unwrap()
    38  	}
    39  	if len(ve.Identifiers) == 0 {
    40  		// no objects, invalid event
    41  		return fmt.Errorf("invalid validation event: no identifiers: %w", err)
    42  	}
    43  	objects := make([]interface{}, len(ve.Identifiers))
    44  	for i, id := range ve.Identifiers {
    45  		objects[i] = jf.baseResourceEvent(id)
    46  	}
    47  	return jf.printEvent("validation", map[string]interface{}{
    48  		"objects": objects,
    49  		"error":   err.Error(),
    50  	})
    51  }
    52  
    53  func (jf *formatter) FormatApplyEvent(e event.ApplyEvent) error {
    54  	eventInfo := jf.baseResourceEvent(e.Identifier)
    55  	if e.Error != nil {
    56  		eventInfo["error"] = e.Error.Error()
    57  	}
    58  	eventInfo["status"] = e.Status.String()
    59  	return jf.printEvent("apply", eventInfo)
    60  }
    61  
    62  func (jf *formatter) FormatStatusEvent(se event.StatusEvent) error {
    63  	return jf.printResourceStatus(se)
    64  }
    65  
    66  func (jf *formatter) printResourceStatus(se event.StatusEvent) error {
    67  	eventInfo := jf.baseResourceEvent(se.Identifier)
    68  	eventInfo["status"] = se.PollResourceInfo.Status.String()
    69  	eventInfo["message"] = se.PollResourceInfo.Message
    70  	return jf.printEvent("status", eventInfo)
    71  }
    72  
    73  func (jf *formatter) FormatPruneEvent(e event.PruneEvent) error {
    74  	eventInfo := jf.baseResourceEvent(e.Identifier)
    75  	if e.Error != nil {
    76  		eventInfo["error"] = e.Error.Error()
    77  	}
    78  	eventInfo["status"] = e.Status.String()
    79  	return jf.printEvent("prune", eventInfo)
    80  }
    81  
    82  func (jf *formatter) FormatDeleteEvent(e event.DeleteEvent) error {
    83  	eventInfo := jf.baseResourceEvent(e.Identifier)
    84  	if e.Error != nil {
    85  		eventInfo["error"] = e.Error.Error()
    86  	}
    87  	eventInfo["status"] = e.Status.String()
    88  	return jf.printEvent("delete", eventInfo)
    89  }
    90  
    91  func (jf *formatter) FormatWaitEvent(e event.WaitEvent) error {
    92  	eventInfo := jf.baseResourceEvent(e.Identifier)
    93  	eventInfo["status"] = e.Status.String()
    94  	return jf.printEvent("wait", eventInfo)
    95  }
    96  
    97  func (jf *formatter) FormatErrorEvent(e event.ErrorEvent) error {
    98  	return jf.printEvent("error", map[string]interface{}{
    99  		"error": e.Err.Error(),
   100  	})
   101  }
   102  
   103  func (jf *formatter) FormatActionGroupEvent(
   104  	age event.ActionGroupEvent,
   105  	ags []event.ActionGroup,
   106  	s stats.Stats,
   107  	_ list.Collector,
   108  ) error {
   109  	content := map[string]interface{}{
   110  		"action": age.Action.String(),
   111  		"status": age.Status.String(),
   112  	}
   113  
   114  	switch age.Action {
   115  	case event.ApplyAction:
   116  		if age.Status == event.Finished {
   117  			as := s.ApplyStats
   118  			content["count"] = as.Sum()
   119  			content["successful"] = as.Successful
   120  			content["skipped"] = as.Skipped
   121  			content["failed"] = as.Failed
   122  		}
   123  	case event.PruneAction:
   124  		if age.Status == event.Finished {
   125  			ps := s.PruneStats
   126  			content["count"] = ps.Sum()
   127  			content["successful"] = ps.Successful
   128  			content["skipped"] = ps.Skipped
   129  			content["failed"] = ps.Failed
   130  		}
   131  	case event.DeleteAction:
   132  		if age.Status == event.Finished {
   133  			ds := s.DeleteStats
   134  			content["count"] = ds.Sum()
   135  			content["successful"] = ds.Successful
   136  			content["skipped"] = ds.Skipped
   137  			content["failed"] = ds.Failed
   138  		}
   139  	case event.WaitAction:
   140  		if age.Status == event.Finished {
   141  			ws := s.WaitStats
   142  			content["count"] = ws.Sum()
   143  			content["successful"] = ws.Successful
   144  			content["skipped"] = ws.Skipped
   145  			content["failed"] = ws.Failed
   146  			content["timeout"] = ws.Timeout
   147  		}
   148  	case event.InventoryAction:
   149  		// no extra content
   150  	default:
   151  		return fmt.Errorf("invalid action group action: %+v", age)
   152  	}
   153  
   154  	return jf.printEvent("group", content)
   155  }
   156  
   157  func (jf *formatter) FormatSummary(s stats.Stats) error {
   158  	if s.ApplyStats != (stats.ApplyStats{}) {
   159  		as := s.ApplyStats
   160  		err := jf.printEvent("summary", map[string]interface{}{
   161  			"action":     event.ApplyAction.String(),
   162  			"count":      as.Sum(),
   163  			"successful": as.Successful,
   164  			"skipped":    as.Skipped,
   165  			"failed":     as.Failed,
   166  		})
   167  		if err != nil {
   168  			return err
   169  		}
   170  	}
   171  	if s.PruneStats != (stats.PruneStats{}) {
   172  		ps := s.PruneStats
   173  		err := jf.printEvent("summary", map[string]interface{}{
   174  			"action":     event.PruneAction.String(),
   175  			"count":      ps.Sum(),
   176  			"successful": ps.Successful,
   177  			"skipped":    ps.Skipped,
   178  			"failed":     ps.Failed,
   179  		})
   180  		if err != nil {
   181  			return err
   182  		}
   183  	}
   184  	if s.DeleteStats != (stats.DeleteStats{}) {
   185  		ds := s.DeleteStats
   186  		err := jf.printEvent("summary", map[string]interface{}{
   187  			"action":     event.DeleteAction.String(),
   188  			"count":      ds.Sum(),
   189  			"successful": ds.Successful,
   190  			"skipped":    ds.Skipped,
   191  			"failed":     ds.Failed,
   192  		})
   193  		if err != nil {
   194  			return err
   195  		}
   196  	}
   197  	if s.WaitStats != (stats.WaitStats{}) {
   198  		ws := s.WaitStats
   199  		err := jf.printEvent("summary", map[string]interface{}{
   200  			"action":     event.WaitAction.String(),
   201  			"count":      ws.Sum(),
   202  			"successful": ws.Successful,
   203  			"skipped":    ws.Skipped,
   204  			"failed":     ws.Failed,
   205  			"timeout":    ws.Timeout,
   206  		})
   207  		if err != nil {
   208  			return err
   209  		}
   210  	}
   211  	return nil
   212  }
   213  
   214  func (jf *formatter) baseResourceEvent(identifier object.ObjMetadata) map[string]interface{} {
   215  	return map[string]interface{}{
   216  		"group":     identifier.GroupKind.Group,
   217  		"kind":      identifier.GroupKind.Kind,
   218  		"namespace": identifier.Namespace,
   219  		"name":      identifier.Name,
   220  	}
   221  }
   222  
   223  func (jf *formatter) printEvent(t string, content map[string]interface{}) error {
   224  	m := make(map[string]interface{})
   225  	m["timestamp"] = jf.now().UTC().Format(time.RFC3339)
   226  	m["type"] = t
   227  	for key, val := range content {
   228  		m[key] = val
   229  	}
   230  	b, err := json.Marshal(m)
   231  	if err != nil {
   232  		return err
   233  	}
   234  	_, err = fmt.Fprint(jf.ioStreams.Out, string(b)+"\n")
   235  	return err
   236  }
   237  

View as plain text