github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/license-update.go (about)

     1  // Copyright (c) 2015-2023 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"math/rand"
    24  	"time"
    25  
    26  	"github.com/minio/minio/internal/logger"
    27  	"github.com/tidwall/gjson"
    28  )
    29  
    30  const (
    31  	licUpdateCycle = 24 * time.Hour * 30
    32  	licRenewPath   = "/api/cluster/renew-license"
    33  )
    34  
    35  // initlicenseUpdateJob start the periodic license update job in the background.
    36  func initLicenseUpdateJob(ctx context.Context, objAPI ObjectLayer) {
    37  	go func() {
    38  		r := rand.New(rand.NewSource(time.Now().UnixNano()))
    39  		// Leader node (that successfully acquires the lock inside licenceUpdaterLoop)
    40  		// will keep performing the license update. If the leader goes down for some
    41  		// reason, the lock will be released and another node will acquire it and
    42  		// take over because of this loop.
    43  		for {
    44  			licenceUpdaterLoop(ctx, objAPI)
    45  
    46  			// license update stopped for some reason.
    47  			// sleep for some time and try again.
    48  			duration := time.Duration(r.Float64() * float64(time.Hour))
    49  			if duration < time.Second {
    50  				// Make sure to sleep at least a second to avoid high CPU ticks.
    51  				duration = time.Second
    52  			}
    53  			time.Sleep(duration)
    54  		}
    55  	}()
    56  }
    57  
    58  func licenceUpdaterLoop(ctx context.Context, objAPI ObjectLayer) {
    59  	ctx, cancel := globalLeaderLock.GetLock(ctx)
    60  	defer cancel()
    61  
    62  	licenseUpdateTimer := time.NewTimer(licUpdateCycle)
    63  	defer licenseUpdateTimer.Stop()
    64  
    65  	for {
    66  		select {
    67  		case <-ctx.Done():
    68  			return
    69  		case <-licenseUpdateTimer.C:
    70  
    71  			if globalSubnetConfig.Registered() {
    72  				performLicenseUpdate(ctx, objAPI)
    73  			}
    74  
    75  			// Reset the timer for next cycle.
    76  			licenseUpdateTimer.Reset(licUpdateCycle)
    77  		}
    78  	}
    79  }
    80  
    81  func performLicenseUpdate(ctx context.Context, objectAPI ObjectLayer) {
    82  	// the subnet license renewal api renews the license only
    83  	// if required e.g. when it is expiring soon
    84  	url := globalSubnetConfig.BaseURL + licRenewPath
    85  
    86  	resp, err := globalSubnetConfig.Post(url, nil)
    87  	if err != nil {
    88  		logger.LogIf(ctx, fmt.Errorf("error from %s: %w", url, err))
    89  		return
    90  	}
    91  
    92  	r := gjson.Parse(resp).Get("license_v2")
    93  	if r.Index == 0 {
    94  		logger.LogIf(ctx, fmt.Errorf("license not found in response from %s", url))
    95  		return
    96  	}
    97  
    98  	lic := r.String()
    99  	if lic == globalSubnetConfig.License {
   100  		// license hasn't changed.
   101  		return
   102  	}
   103  
   104  	kv := "subnet license=" + lic
   105  	result, err := setConfigKV(ctx, objectAPI, []byte(kv))
   106  	if err != nil {
   107  		logger.LogIf(ctx, fmt.Errorf("error setting subnet license config: %w", err))
   108  		return
   109  	}
   110  
   111  	if result.Dynamic {
   112  		if err := applyDynamicConfigForSubSys(GlobalContext, objectAPI, result.Cfg, result.SubSys); err != nil {
   113  			logger.LogIf(ctx, fmt.Errorf("error applying subnet dynamic config: %w", err))
   114  			return
   115  		}
   116  		globalNotificationSys.SignalConfigReload(result.SubSys)
   117  	}
   118  }