Add printers for events
It will allow users to see what's happening during phase execution. Change-Id: I31c4f98ccbd811ac88c7eabdf5efbe6ffaae6a41 Relates-To: #339
This commit is contained in:
parent
d649d9d450
commit
feadd70f34
@ -15,6 +15,7 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
applyevent "sigs.k8s.io/cli-utils/pkg/apply/event"
|
||||
@ -56,6 +57,87 @@ type Event struct {
|
||||
GenericContainerEvent GenericContainerEvent
|
||||
}
|
||||
|
||||
//GenericEvent generalized type for custom events
|
||||
type GenericEvent struct {
|
||||
Type string
|
||||
Operation string
|
||||
Message string
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
var mapTypeToEvent = map[Type]string{
|
||||
ClusterctlType: "ClusterctlEvent",
|
||||
IsogenType: "IsogenEvent",
|
||||
BootstrapType: "BootstrapEvent",
|
||||
GenericContainerType: "GenericContainerEvent",
|
||||
}
|
||||
|
||||
var unknownEventType = map[Type]string{
|
||||
ApplierType: "ApplierType",
|
||||
ErrorType: "ErrorType",
|
||||
StatusPollerType: "StatusPollerType",
|
||||
WaitType: "WaitType",
|
||||
}
|
||||
|
||||
var isogenOperationToString = map[IsogenOperation]string{
|
||||
IsogenStart: "IsogenStart",
|
||||
IsogenValidation: "IsogenValidation",
|
||||
IsogenEnd: "IsogenEnd",
|
||||
}
|
||||
|
||||
var clusterctlOperationToString = map[ClusterctlOperation]string{
|
||||
ClusterctlInitStart: "ClusterctlInitStart",
|
||||
ClusterctlInitEnd: "ClusterctlInitEnd",
|
||||
ClusterctlMoveStart: "ClusterctlMoveStart",
|
||||
ClusterctlMoveEnd: "ClusterctlMoveEnd",
|
||||
}
|
||||
|
||||
var bootstrapOperationToString = map[BootstrapOperation]string{
|
||||
BootstrapStart: "BootstrapStart",
|
||||
BootstrapDryRun: "BootstrapDryRun",
|
||||
BootstrapValidation: "BootstrapValidation",
|
||||
BootstrapRun: "BootstrapRun",
|
||||
BootstrapEnd: "BootstrapEnd",
|
||||
}
|
||||
|
||||
var genericContainerOperationToString = map[GenericContainerOperation]string{
|
||||
GenericContainerStart: "GenericContainerStart",
|
||||
GenericContainerStop: "GenericContainerStop",
|
||||
}
|
||||
|
||||
//Normalize cast Event to GenericEvent type
|
||||
func Normalize(e Event) GenericEvent {
|
||||
var eventType string
|
||||
if t, exists := mapTypeToEvent[e.Type]; exists {
|
||||
eventType = t
|
||||
} else {
|
||||
eventType = fmt.Sprintf("Unknown event type: %v", unknownEventType[e.Type])
|
||||
}
|
||||
|
||||
var operation, message string
|
||||
switch e.Type {
|
||||
case ClusterctlType:
|
||||
operation = clusterctlOperationToString[e.ClusterctlEvent.Operation]
|
||||
message = e.ClusterctlEvent.Message
|
||||
case IsogenType:
|
||||
operation = isogenOperationToString[e.IsogenEvent.Operation]
|
||||
message = e.IsogenEvent.Message
|
||||
case BootstrapType:
|
||||
operation = bootstrapOperationToString[e.BootstrapEvent.Operation]
|
||||
message = e.BootstrapEvent.Message
|
||||
case GenericContainerType:
|
||||
operation = genericContainerOperationToString[e.GenericContainerEvent.Operation]
|
||||
message = e.GenericContainerEvent.Message
|
||||
}
|
||||
|
||||
return GenericEvent{
|
||||
Type: eventType,
|
||||
Operation: operation,
|
||||
Message: message,
|
||||
Timestamp: e.Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEvent create new event with timestamp
|
||||
func NewEvent() Event {
|
||||
return Event{
|
||||
|
61
pkg/events/events_test.go
Normal file
61
pkg/events/events_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package events_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
)
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
sourceEvent events.Event
|
||||
expectedEvent events.GenericEvent
|
||||
}{
|
||||
{
|
||||
name: "Unknow event type",
|
||||
sourceEvent: events.NewEvent().WithErrorEvent(events.ErrorEvent{}),
|
||||
expectedEvent: events.GenericEvent{
|
||||
Type: "Unknown event type: ErrorType",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Clusterctl event type",
|
||||
sourceEvent: events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
|
||||
Operation: events.ClusterctlInitStart,
|
||||
Message: "Clusterctl init start",
|
||||
}),
|
||||
expectedEvent: events.GenericEvent{
|
||||
Type: "ClusterctlEvent",
|
||||
Message: "Clusterctl init start",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ge := events.Normalize(tt.sourceEvent)
|
||||
assert.Equal(t, tt.expectedEvent.Type, ge.Type)
|
||||
if tt.expectedEvent.Type != "Unknown event type: ErrorType" {
|
||||
assert.Equal(t, tt.expectedEvent.Message, ge.Message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
69
pkg/events/printers.go
Normal file
69
pkg/events/printers.go
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package events
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// YAMLPrinter format for event printer output
|
||||
YAMLPrinter = "yaml"
|
||||
// JSONPrinter format for event printer output
|
||||
JSONPrinter = "json"
|
||||
)
|
||||
|
||||
// NewGenericPrinter returns event printer
|
||||
func NewGenericPrinter(writer io.Writer, formatterType string) GenericPrinter {
|
||||
var formatter func(o interface{}) ([]byte, error)
|
||||
switch formatterType {
|
||||
case YAMLPrinter:
|
||||
formatter = yaml.Marshal
|
||||
case JSONPrinter:
|
||||
formatter = json.Marshal
|
||||
default:
|
||||
log.Fatal("Event printer received wrong type of event formatter")
|
||||
}
|
||||
return GenericPrinter{
|
||||
formatter: formatter,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
// GenericPrinter object represents event printer
|
||||
type GenericPrinter struct {
|
||||
formatter func(interface{}) ([]byte, error)
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// PrintEvent write event details
|
||||
func (p GenericPrinter) PrintEvent(ge GenericEvent) error {
|
||||
data, err := p.formatter(map[string]interface{}{
|
||||
"Type": ge.Type,
|
||||
"Operation": ge.Operation,
|
||||
"Message": ge.Message,
|
||||
"Timestamp": ge.Timestamp,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = p.writer.Write(data)
|
||||
return err
|
||||
}
|
69
pkg/events/printers_test.go
Normal file
69
pkg/events/printers_test.go
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package events_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
)
|
||||
|
||||
type fakeWriter struct {
|
||||
writeErr error
|
||||
}
|
||||
|
||||
var _ io.Writer = fakeWriter{}
|
||||
|
||||
func (f fakeWriter) Write(p []byte) (n int, err error) {
|
||||
return 0, f.writeErr
|
||||
}
|
||||
|
||||
func TestPrintEvent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
formatterType string
|
||||
errString string
|
||||
writer io.Writer
|
||||
}{
|
||||
{
|
||||
name: "Fail on formatter type",
|
||||
formatterType: events.YAMLPrinter,
|
||||
errString: "Error on write",
|
||||
writer: fakeWriter{
|
||||
writeErr: fmt.Errorf("Error on write"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := events.NewGenericPrinter(tt.writer, tt.formatterType)
|
||||
e := events.NewEvent().WithIsogenEvent(events.IsogenEvent{
|
||||
Operation: events.IsogenStart,
|
||||
Message: "starting ISO generation",
|
||||
})
|
||||
ge := events.Normalize(e)
|
||||
err := p.PrintEvent(ge)
|
||||
if tt.errString != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.errString)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -34,15 +34,19 @@ type EventProcessor interface {
|
||||
type DefaultProcessor struct {
|
||||
errors []error
|
||||
applierChan chan<- applyevent.Event
|
||||
genericPrinter GenericPrinter
|
||||
}
|
||||
|
||||
// NewDefaultProcessor returns instance of DefaultProcessor as interface Implementation
|
||||
func NewDefaultProcessor(streams genericclioptions.IOStreams) EventProcessor {
|
||||
applyCh := make(chan applyevent.Event)
|
||||
go printers.GetPrinter(printers.EventsPrinter, streams).Print(applyCh, common.DryRunNone)
|
||||
// printer for custom airshipctl events
|
||||
genericPrinter := NewGenericPrinter(log.Writer(), JSONPrinter)
|
||||
return &DefaultProcessor{
|
||||
errors: []error{},
|
||||
applierChan: applyCh,
|
||||
genericPrinter: genericPrinter,
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,19 +59,16 @@ func (p *DefaultProcessor) Process(ch <-chan Event) error {
|
||||
case ErrorType:
|
||||
log.Printf("Received error on event channel %v", e.ErrorEvent)
|
||||
p.errors = append(p.errors, e.ErrorEvent.Error)
|
||||
case ClusterctlType, IsogenType, GenericContainerType:
|
||||
// TODO each event needs to be interface that allows us to print it for example
|
||||
// Stringer interface or AsYAML for further processing.
|
||||
// For now we print the event object as is
|
||||
log.Printf("Received event: %v", e)
|
||||
case BootstrapType:
|
||||
log.Printf("%s", e.BootstrapEvent.Message)
|
||||
case StatusPollerType:
|
||||
log.Fatalf("Processing for status poller events are not yet implemented")
|
||||
case WaitType:
|
||||
log.Fatalf("Processing for wait events are not yet implemented")
|
||||
default:
|
||||
log.Fatalf("Unknown event type received: %d", e.Type)
|
||||
ge := Normalize(e)
|
||||
err := p.genericPrinter.PrintEvent(ge)
|
||||
if err != nil {
|
||||
p.errors = append(p.errors, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return checkErrors(p.errors)
|
||||
|
Loading…
Reference in New Issue
Block a user