github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/agent/mcorpc/mcorpc.go (about)

     1  // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  // Package mcorpc provides a compatibility layer between Choria and
     6  // legacy MCollective SimpleRPC Agents
     7  //
     8  // Agents can be written in the Go language, compiled into the binaries
     9  // and be interacted with from the ruby MCollective client.
    10  //
    11  // It's planned to provide a backward compatible interface so that old
    12  // ruby agents, authorization and auditing will be usable inside the
    13  // Choria daemon via a shell-out mechanism
    14  package mcorpc
    15  
    16  import (
    17  	"encoding/json"
    18  	"fmt"
    19  	"time"
    20  
    21  	"github.com/choria-io/go-choria/config"
    22  	"github.com/choria-io/go-choria/protocol"
    23  	"github.com/choria-io/go-choria/srvcache"
    24  	"github.com/choria-io/go-choria/validator"
    25  )
    26  
    27  // ChoriaFramework provides access to the choria framework
    28  type ChoriaFramework interface {
    29  	Configuration() *config.Config
    30  	FacterDomain() (string, error)
    31  	FacterCmd() string
    32  	MiddlewareServers() (srvcache.Servers, error)
    33  	NewTransportFromJSON(data []byte) (protocol.TransportMessage, error)
    34  	ProvisionMode() bool
    35  	UniqueID() string
    36  	Certname() string
    37  }
    38  
    39  // StatusCode is a reply status as defined by MCollective SimpleRPC - integers 0 to 5
    40  //
    41  // See the constants OK, RPCAborted, UnknownRPCAction, MissingRPCData, InvalidRPCData and UnknownRPCError
    42  type StatusCode uint8
    43  
    44  const (
    45  	// OK is the reply status when all worked
    46  	OK = StatusCode(iota)
    47  
    48  	// Aborted is status for when the action could not run, most failures in an action should set this
    49  	Aborted
    50  
    51  	// UnknownAction is the status for unknown actions requested
    52  	UnknownAction
    53  
    54  	// MissingData is the status for missing input data
    55  	MissingData
    56  
    57  	// InvalidData is the status for invalid input data
    58  	InvalidData
    59  
    60  	// UnknownError is the status general failures in agents should set when things go bad
    61  	UnknownError
    62  )
    63  
    64  // Reply is the reply data as stipulated by MCollective RPC system.  The Data
    65  // has to be something that can be turned into JSON using the normal Marshal system
    66  type Reply struct {
    67  	Action          string     `json:"action"`
    68  	Statuscode      StatusCode `json:"statuscode"`
    69  	Statusmsg       string     `json:"statusmsg"`
    70  	Data            any        `json:"data"`
    71  	DisableResponse bool       `json:"-"`
    72  }
    73  
    74  // Request is a request as defined by the MCollective RPC system.
    75  // The input data is stored in Data as JSON text unprocessed, the
    76  // system at this level has no idea what is in there.  In your Agent
    77  // you can choose to use the ParseRequestData function to translate
    78  // this for you or just do whatever JSON parsing you like
    79  type Request struct {
    80  	Agent            string           `json:"agent"`
    81  	Action           string           `json:"action"`
    82  	Data             json.RawMessage  `json:"data"`
    83  	RequestID        string           `json:"requestid"`
    84  	SenderID         string           `json:"senderid"`
    85  	CallerID         string           `json:"callerid"`
    86  	Collective       string           `json:"collective"`
    87  	TTL              int              `json:"ttl"`
    88  	Time             time.Time        `json:"time"`
    89  	Filter           *protocol.Filter `json:"-"`
    90  	CallerPublicData string           `json:"-"`
    91  	SignerPublicData string           `json:"-"`
    92  }
    93  
    94  // ParseRequestData parses the request parameters received from the client into a target structure
    95  //
    96  // Validation is supported, the example below does a `shellsafe` check on the data prior to returning
    97  // it, should the check fail appropriate errors will be set on the reply data
    98  //
    99  // Example used in a action:
   100  //
   101  //	  var rparams struct {
   102  //	     Package string `json:"package" validate:"shellsafe"`
   103  //	  }
   104  //
   105  //	  if !mcorpc.ParseRequestData(&rparams, req, reply) {
   106  //	    // the function already set appropriate errors on reply
   107  //		   return
   108  //	  }
   109  //
   110  //	  // do stuff with rparams.Package
   111  func ParseRequestData(target any, request *Request, reply *Reply) bool {
   112  	err := json.Unmarshal(request.Data, target)
   113  	if err != nil {
   114  		reply.Statuscode = InvalidData
   115  		reply.Statusmsg = fmt.Sprintf("Could not parse request data for %s#%s: %s", request.Agent, request.Action, err)
   116  		return false
   117  	}
   118  
   119  	ok, err := validator.ValidateStruct(target)
   120  	if !ok {
   121  		reply.Statuscode = InvalidData
   122  		reply.Statusmsg = fmt.Sprintf("Validation failed: %s", err)
   123  		return false
   124  	}
   125  
   126  	return true
   127  }