github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/cmd/reqtool.go (about)

     1  /*
     2   * Copyright 2018-2019 The NATS Authors
     3   * Licensed under the Apache License, Version 2.0 (the "License");
     4   * you may not use this file except in compliance with the License.
     5   * You may obtain a copy of the License at
     6   *
     7   * http://www.apache.org/licenses/LICENSE-2.0
     8   *
     9   * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15  
    16  package cmd
    17  
    18  import (
    19  	"fmt"
    20  	"os"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/nats-io/nsc/v2/cmd/store"
    25  
    26  	nats "github.com/nats-io/nats.go"
    27  
    28  	"github.com/spf13/cobra"
    29  )
    30  
    31  func createToolReqCmd() *cobra.Command {
    32  	var params ReqParams
    33  	var cmd = &cobra.Command{
    34  		Use:     "req",
    35  		Short:   "Send a request to a subject on a NATS account",
    36  		Example: "ngs tool req <subject> <opt_payload>",
    37  		Args:    cobra.MinimumNArgs(1),
    38  		RunE: func(cmd *cobra.Command, args []string) error {
    39  			return RunAction(cmd, args, &params)
    40  		},
    41  	}
    42  	params.BindFlags(cmd)
    43  	cmd.Flags().BoolVarP(&encryptFlag, "encrypt", "E", false, "encrypt payload")
    44  	cmd.Flags().MarkHidden("encrypt")
    45  	cmd.Flags().MarkHidden("decrypt")
    46  
    47  	return cmd
    48  }
    49  
    50  func init() {
    51  	toolCmd.AddCommand(createToolReqCmd())
    52  	hidden := createToolReqCmd()
    53  	hidden.Hidden = true
    54  	hidden.Example = "ngs tool req <subject> <opt_payload>"
    55  	GetRootCmd().AddCommand(hidden)
    56  }
    57  
    58  type ReqParams struct {
    59  	AccountUserContextParams
    60  	credsPath string
    61  	natsURLs  []string
    62  }
    63  
    64  func (p *ReqParams) SetDefaults(ctx ActionCtx) error {
    65  	return p.AccountUserContextParams.SetDefaults(ctx)
    66  }
    67  
    68  func (p *ReqParams) PreInteractive(ctx ActionCtx) error {
    69  	return p.AccountUserContextParams.Edit(ctx)
    70  }
    71  
    72  func (p *ReqParams) Load(ctx ActionCtx) error {
    73  	p.credsPath = ctx.StoreCtx().KeyStore.CalcUserCredsPath(p.AccountContextParams.Name, p.UserContextParams.Name)
    74  	if natsURLFlag != "" {
    75  		p.natsURLs = []string{natsURLFlag}
    76  		return nil
    77  	}
    78  
    79  	oc, err := ctx.StoreCtx().Store.ReadOperatorClaim()
    80  	if err != nil {
    81  		return err
    82  	}
    83  	p.natsURLs = oc.OperatorServiceURLs
    84  	return nil
    85  }
    86  
    87  func (p *ReqParams) PostInteractive(ctx ActionCtx) error {
    88  	return nil
    89  }
    90  
    91  func (p *ReqParams) Validate(ctx ActionCtx) error {
    92  	if err := p.AccountUserContextParams.Validate(ctx); err != nil {
    93  		return err
    94  	}
    95  
    96  	if encryptFlag {
    97  		_, err := ctx.StoreCtx().KeyStore.GetSeed(ctx.StoreCtx().Account.PublicKey)
    98  		if err != nil {
    99  			return fmt.Errorf("unable to get the account private key to encrypt/decrypt the payload: %v", err)
   100  		}
   101  	}
   102  
   103  	if p.credsPath == "" {
   104  		return fmt.Errorf("a creds file for account %q/%q was not found", p.AccountContextParams.Name, p.UserContextParams.Name)
   105  	}
   106  	_, err := os.Stat(p.credsPath)
   107  	if os.IsNotExist(err) {
   108  		return err
   109  	}
   110  	if len(p.natsURLs) == 0 {
   111  		return fmt.Errorf("operator %q doesn't have operator_service_urls set", ctx.StoreCtx().Operator.Name)
   112  	}
   113  	return nil
   114  }
   115  
   116  func (p *ReqParams) Run(ctx ActionCtx) (store.Status, error) {
   117  	nc, err := nats.Connect(strings.Join(p.natsURLs, ", "),
   118  		createDefaultToolOptions("nsc_req", ctx, nats.UserCredentials(p.credsPath))...)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	defer nc.Close()
   123  
   124  	subj := ctx.Args()[0]
   125  	payload := ""
   126  	if len(ctx.Args()) > 1 {
   127  		payload = ctx.Args()[1]
   128  	}
   129  
   130  	var seed string
   131  	if encryptFlag {
   132  		// cannot fail if we are here
   133  		seed, _ = ctx.StoreCtx().KeyStore.GetSeed(ctx.StoreCtx().Account.PublicKey)
   134  	}
   135  
   136  	out := []byte(payload)
   137  	if encryptFlag {
   138  		out, err = EncryptKV(seed, out)
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  	}
   143  
   144  	if encryptFlag {
   145  		ctx.CurrentCmd().Printf("published encrypted request: [%s] : '%s'\n", subj, payload)
   146  	} else {
   147  		ctx.CurrentCmd().Printf("published request: [%s] : '%s'\n", subj, payload)
   148  	}
   149  	msg, err := nc.Request(subj, out, 5*time.Second)
   150  	if err == nats.ErrTimeout {
   151  		ctx.CurrentCmd().Println("request timed out")
   152  		return nil, nil
   153  	}
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	if encryptFlag {
   159  		msg = maybeDecryptMessage(seed, msg)
   160  	}
   161  	ctx.CurrentCmd().Printf("received reply: [%v] : '%s'\n", msg.Subject, string(msg.Data))
   162  	return nil, nil
   163  }