talos/pkg/client/reply.go
Andrey Smirnov 682dd433ba refactor: move Talos client package to pkg/
As this implements Go client for Talos API, it makes sense to publish it
one the top level.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
2020-04-01 23:45:58 +03:00

132 lines
3.0 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package client
import (
"fmt"
"reflect"
"github.com/hashicorp/go-multierror"
)
// NodeError is RPC error from some node
type NodeError struct {
Node string
Err string
}
func (ne *NodeError) Error() string {
return fmt.Sprintf("%s: %s", ne.Node, ne.Err)
}
// FilterMessages removes error Messagess from resp and builds multierror.
//
//nolint: gocyclo
func FilterMessages(resp interface{}, err error) (interface{}, error) {
if resp == nil {
return nil, err
}
respStructPtr := reflect.ValueOf(resp)
if respStructPtr.Kind() != reflect.Ptr {
panic("response should be pointer to struct")
}
if respStructPtr.IsNil() {
return nil, err
}
respStruct := respStructPtr.Elem()
if respStruct.Kind() != reflect.Struct {
panic("response should be struct")
}
messagesField := respStruct.FieldByName("Messages")
if !messagesField.IsValid() {
panic("Messages field missing")
}
if messagesField.Kind() != reflect.Slice {
panic("Messages field should be a slice")
}
var multiErr *multierror.Error
for i := 0; i < messagesField.Len(); {
MessagesPtr := messagesField.Index(i)
if MessagesPtr.Kind() != reflect.Ptr {
panic("Messages slice should container pointers")
}
Messages := MessagesPtr.Elem()
if Messages.Kind() != reflect.Struct {
panic("Messages slice should container pointers to structs")
}
metadataField := Messages.FieldByName("Metadata")
if !metadataField.IsValid() {
panic("Messages metadata field missing")
}
if metadataField.Kind() != reflect.Ptr {
panic("Messages metadata field should be a pointer")
}
if metadataField.IsNil() {
// missing metadata, skip the field
i++
continue
}
metadata := metadataField.Elem()
if metadata.Kind() != reflect.Struct {
panic("Messages metadata should be struct")
}
errorField := metadata.FieldByName("Error")
if !errorField.IsValid() {
panic("metadata.Error field missing")
}
if errorField.Kind() != reflect.String {
panic("metadata.Error should be string")
}
if errorField.IsZero() {
// no error, leave it as is
i++
continue
}
hostnameField := metadata.FieldByName("Hostname")
if !hostnameField.IsValid() {
panic("metadata.Hostname field missing")
}
if hostnameField.Kind() != reflect.String {
panic("metadata.Hostname should be string")
}
// extract error
nodeError := &NodeError{
Node: hostnameField.String(),
Err: errorField.String(),
}
multiErr = multierror.Append(multiErr, nodeError)
// remove ith Messages
reflect.Copy(messagesField.Slice(i, messagesField.Len()), messagesField.Slice(i+1, messagesField.Len()))
messagesField.SetLen(messagesField.Len() - 1)
}
// if all the Messagess were error Messagess...
if multiErr != nil && messagesField.Len() == 0 {
resp = nil
}
return resp, multiErr.ErrorOrNil()
}