github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/provtarget/builddefaults/default.go (about)

     1  // Copyright (c) 2021-2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package builddefaults
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"io"
    11  	"net"
    12  	"os"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/choria-io/go-choria/config"
    17  	"github.com/choria-io/go-choria/internal/util"
    18  	"github.com/choria-io/go-choria/srvcache"
    19  	"github.com/choria-io/tokens"
    20  	"github.com/sirupsen/logrus"
    21  
    22  	"github.com/choria-io/go-choria/backoff"
    23  	"github.com/choria-io/go-choria/build"
    24  )
    25  
    26  // Provider creates an instance of the provider
    27  func Provider() *Resolver {
    28  	return &Resolver{
    29  		bi: &build.Info{},
    30  	}
    31  }
    32  
    33  // Resolver resolve names against the compile time build properties
    34  type Resolver struct {
    35  	identity string
    36  	bi       *build.Info
    37  }
    38  
    39  // Name is te name of the resolver
    40  func (b *Resolver) Name() string {
    41  	return "Choria JWT Resolver"
    42  }
    43  
    44  // Configure overrides build settings using the contents of the JWT
    45  func (b *Resolver) Configure(ctx context.Context, cfg *config.Config, log *logrus.Entry) {
    46  	jwtf := b.bi.ProvisionJWTFile()
    47  	if jwtf == "" {
    48  		return
    49  	}
    50  
    51  	if !util.FileExist(jwtf) {
    52  		return
    53  	}
    54  
    55  	log.Infof("Setting build defaults to those found in %s", jwtf)
    56  
    57  	b.identity = cfg.Identity
    58  
    59  	jwtreader, err := os.Open(b.bi.ProvisionJWTFile())
    60  	if err != nil {
    61  		log.Errorf("Configuration of the provisioner settings based on JWT file %s failed: %s", jwtf, err)
    62  		return
    63  	}
    64  	defer jwtreader.Close()
    65  
    66  	_, err = SetBuildBasedOnJWT(jwtreader, b.bi)
    67  	if err != nil {
    68  		log.Errorf("Configuration of the provisioner settings based on JWT file %s failed: %s", jwtf, err)
    69  	}
    70  }
    71  
    72  // Targets are the build time configured provisioners
    73  func (b *Resolver) Targets(ctx context.Context, log *logrus.Entry) []string {
    74  	if b.bi.ProvisionBrokerURLs() != "" {
    75  		return strings.Split(b.bi.ProvisionBrokerURLs(), ",")
    76  	}
    77  
    78  	domain := b.bi.ProvisionBrokerSRVDomain()
    79  	if domain == "" {
    80  		log.Warnf("Neither provisioning broker url or provisioning SRV domain is set, cannot continue")
    81  		return []string{}
    82  	}
    83  
    84  	log.Infof("Performing provisioning broker resolution via SRV using domain %s", domain)
    85  
    86  	servers := srvcache.NewServers()
    87  	cache := srvcache.New(b.identity, 5*time.Second, net.LookupSRV, log)
    88  	var err error
    89  	try := 0
    90  
    91  	for {
    92  		try++
    93  
    94  		for _, q := range []string{"_choria-provisioner._tcp"} {
    95  			if ctx.Err() != nil {
    96  				return []string{}
    97  			}
    98  
    99  			record := q + "." + domain
   100  			log.Infof("Attempting SRV lookup on %s", record)
   101  
   102  			servers, err = cache.LookupSrvServers("", "", record, "nats")
   103  			if err != nil {
   104  				log.Warnf("Failed to resolve %s: %s", record, err)
   105  				continue
   106  			}
   107  
   108  			log.Infof("Found %d SRV records for %s", servers.Count(), record)
   109  			break
   110  		}
   111  
   112  		if servers.Count() > 0 {
   113  			break
   114  		}
   115  
   116  		log.Warnf("Resolving provisioning brokers via SRV lookups in domain %s failed on try %d, will keep trying", domain, try)
   117  
   118  		backoff.TwentySec.TrySleep(ctx, try)
   119  	}
   120  
   121  	return servers.Strings()
   122  }
   123  
   124  // SetBuildBasedOnJWT sets build settings based on contents of a JWT file
   125  func SetBuildBasedOnJWT(r io.Reader, bi *build.Info) (*tokens.ProvisioningClaims, error) {
   126  	jwt, err := io.ReadAll(r)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	claims, err := tokens.ParseProvisionTokenUnverified(string(jwt))
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	if claims.Token == "" {
   137  		return nil, fmt.Errorf("no auth token")
   138  	}
   139  
   140  	if claims.SRVDomain == "" && claims.URLs == "" {
   141  		return nil, fmt.Errorf("no srv domain or urls")
   142  	}
   143  
   144  	if claims.SRVDomain != "" && claims.URLs != "" {
   145  		return nil, fmt.Errorf("both srv domain and URLs supplied")
   146  	}
   147  
   148  	bi.SetProvisionBrokerURLs(claims.URLs)
   149  	bi.SetProvisionToken(claims.Token)
   150  	bi.SetProvisionBrokerSRVDomain(claims.SRVDomain)
   151  	bi.SetProvisionUsingVersion2(claims.ProtoV2)
   152  	bi.SetProvisionAllowServerUpdate(claims.AllowUpdate)
   153  
   154  	if claims.ProvDefault {
   155  		bi.EnableProvisionModeAsDefault()
   156  	} else {
   157  		bi.DisableProvisionModeAsDefault()
   158  	}
   159  
   160  	if claims.Secure {
   161  		bi.EnableProvisionModeSecurity()
   162  	} else {
   163  		bi.DisableProvisionModeSecurity()
   164  	}
   165  
   166  	if claims.ProvFacts != "" {
   167  		bi.SetProvisionFacts(claims.ProvFacts)
   168  	}
   169  
   170  	if claims.ProvRegData != "" {
   171  		bi.SetProvisionRegistrationData(claims.ProvRegData)
   172  	}
   173  
   174  	if claims.ProvNatsUser != "" {
   175  		bi.SetProvisioningBrokerUsername(claims.ProvNatsUser)
   176  	}
   177  
   178  	if claims.ProvNatsPass != "" {
   179  		bi.SetProvisioningBrokerPassword(claims.ProvNatsPass)
   180  	}
   181  
   182  	return claims, nil
   183  }