mirror of
				https://github.com/fluxcd/flux2.git
				synced 2025-10-31 00:06:55 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			153 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2020 The Flux authors
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/spf13/cobra"
 | |
| 	"k8s.io/apimachinery/pkg/types"
 | |
| 	"k8s.io/apimachinery/pkg/util/validation"
 | |
| 	"k8s.io/apimachinery/pkg/util/wait"
 | |
| 	"sigs.k8s.io/controller-runtime/pkg/client"
 | |
| 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 | |
| 
 | |
| 	"github.com/fluxcd/flux2/internal/utils"
 | |
| )
 | |
| 
 | |
| var createCmd = &cobra.Command{
 | |
| 	Use:   "create",
 | |
| 	Short: "Create or update sources and resources",
 | |
| 	Long:  "The create sub-commands generate sources and resources.",
 | |
| }
 | |
| 
 | |
| type createFlags struct {
 | |
| 	interval time.Duration
 | |
| 	export   bool
 | |
| 	labels   []string
 | |
| }
 | |
| 
 | |
| var createArgs createFlags
 | |
| 
 | |
| func init() {
 | |
| 	createCmd.PersistentFlags().DurationVarP(&createArgs.interval, "interval", "", time.Minute, "source sync interval")
 | |
| 	createCmd.PersistentFlags().BoolVar(&createArgs.export, "export", false, "export in YAML format to stdout")
 | |
| 	createCmd.PersistentFlags().StringSliceVar(&createArgs.labels, "label", nil,
 | |
| 		"set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)")
 | |
| 	rootCmd.AddCommand(createCmd)
 | |
| }
 | |
| 
 | |
| // upsertable is an interface for values that can be used in `upsert`.
 | |
| type upsertable interface {
 | |
| 	adapter
 | |
| 	named
 | |
| }
 | |
| 
 | |
| // upsert updates or inserts an object. Instead of providing the
 | |
| // object itself, you provide a named (as in Name and Namespace)
 | |
| // template value, and a mutate function which sets the values you
 | |
| // want to update. The mutate function is nullary -- you mutate a
 | |
| // value in the closure, e.g., by doing this:
 | |
| //
 | |
| //     var existing Value
 | |
| //     existing.Name = name
 | |
| //     existing.Namespace = ns
 | |
| //     upsert(ctx, client, valueAdapter{&value}, func() error {
 | |
| //       value.Spec = onePreparedEarlier
 | |
| //     })
 | |
| func (names apiType) upsert(ctx context.Context, kubeClient client.Client, object upsertable, mutate func() error) (types.NamespacedName, error) {
 | |
| 	nsname := types.NamespacedName{
 | |
| 		Namespace: object.GetNamespace(),
 | |
| 		Name:      object.GetName(),
 | |
| 	}
 | |
| 
 | |
| 	op, err := controllerutil.CreateOrUpdate(ctx, kubeClient, object.asClientObject(), mutate)
 | |
| 	if err != nil {
 | |
| 		return nsname, err
 | |
| 	}
 | |
| 
 | |
| 	switch op {
 | |
| 	case controllerutil.OperationResultCreated:
 | |
| 		logger.Successf("%s created", names.kind)
 | |
| 	case controllerutil.OperationResultUpdated:
 | |
| 		logger.Successf("%s updated", names.kind)
 | |
| 	}
 | |
| 	return nsname, nil
 | |
| }
 | |
| 
 | |
| type upsertWaitable interface {
 | |
| 	upsertable
 | |
| 	statusable
 | |
| }
 | |
| 
 | |
| // upsertAndWait encodes the pattern of creating or updating a
 | |
| // resource, then waiting for it to reconcile. See the note on
 | |
| // `upsert` for how to work with the `mutate` argument.
 | |
| func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) error {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) // NB globals
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	logger.Generatef("generating %s", names.kind)
 | |
| 	logger.Actionf("applying %s", names.kind)
 | |
| 
 | |
| 	namespacedName, err := imageRepositoryType.upsert(ctx, kubeClient, object, mutate)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	logger.Waitingf("waiting for %s reconciliation", names.kind)
 | |
| 	if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
 | |
| 		isReady(ctx, kubeClient, namespacedName, object)); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	logger.Successf("%s reconciliation completed", names.kind)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func parseLabels() (map[string]string, error) {
 | |
| 	result := make(map[string]string)
 | |
| 	for _, label := range createArgs.labels {
 | |
| 		// validate key value pair
 | |
| 		parts := strings.Split(label, "=")
 | |
| 		if len(parts) != 2 {
 | |
| 			return nil, fmt.Errorf("invalid label format '%s', must be key=value", label)
 | |
| 		}
 | |
| 
 | |
| 		// validate label name
 | |
| 		if errors := validation.IsQualifiedName(parts[0]); len(errors) > 0 {
 | |
| 			return nil, fmt.Errorf("invalid label '%s': %v", parts[0], errors)
 | |
| 		}
 | |
| 
 | |
| 		// validate label value
 | |
| 		if errors := validation.IsValidLabelValue(parts[1]); len(errors) > 0 {
 | |
| 			return nil, fmt.Errorf("invalid label value '%s': %v", parts[1], errors)
 | |
| 		}
 | |
| 
 | |
| 		result[parts[0]] = parts[1]
 | |
| 	}
 | |
| 
 | |
| 	return result, nil
 | |
| }
 | 
