sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/cmd/cm2kc/main.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"sort"
    24  
    25  	flag "github.com/spf13/pflag"
    26  	clientcmdapi "k8s.io/client-go/tools/clientcmd/api/v1"
    27  	"sigs.k8s.io/yaml"
    28  
    29  	"sigs.k8s.io/prow/pkg/kube"
    30  )
    31  
    32  const (
    33  	// defaultInput is the default input source.
    34  	defaultInput = "/dev/stdin"
    35  	// defaultOutput is the default output source.
    36  	defaultOutput = "/dev/stdout"
    37  )
    38  
    39  // Cluster represents the information necessary to talk to a Kubernetes master endpoint.
    40  type Cluster struct {
    41  	// The IP address of the cluster's master endpoint.
    42  	Endpoint string `json:"endpoint"`
    43  	// Base64-encoded public cert used by clients to authenticate to the cluster endpoint.
    44  	ClientCertificate []byte `json:"clientCertificate"`
    45  	// Base64-encoded private key used by clients..
    46  	ClientKey []byte `json:"clientKey"`
    47  	// Base64-encoded public certificate that is the root of trust for the cluster.
    48  	ClusterCACertificate []byte `json:"clusterCaCertificate"`
    49  }
    50  
    51  // options are the available command-line flags.
    52  type options struct {
    53  	input  string
    54  	output string
    55  }
    56  
    57  // parseFlags parses the command-line flags.
    58  func (o *options) parseFlags() {
    59  	flag.StringVarP(&o.input, "input", "i", defaultInput, "Input cluster map file.")
    60  	flag.StringVarP(&o.output, "output", "o", defaultOutput, "Output kubeconfig file.")
    61  
    62  	flag.Parse()
    63  }
    64  
    65  // printErrAndExit prints an error message to stderr and exits with a status code.
    66  func printErrAndExit(err error, code int) {
    67  	_, _ = fmt.Fprintln(os.Stderr, err.Error())
    68  	os.Exit(code)
    69  }
    70  
    71  // unmarshalClusterMap reads a map[string]Cluster in yaml bytes.
    72  func unmarshalClusterMap(data []byte) (map[string]Cluster, error) {
    73  	var raw map[string]Cluster
    74  	if err := yaml.Unmarshal(data, &raw); err != nil {
    75  		// If we failed to unmarshal the multicluster format try the single Cluster format.
    76  		var singleConfig Cluster
    77  		if err := yaml.Unmarshal(data, &singleConfig); err != nil {
    78  			return nil, err
    79  		}
    80  		raw = map[string]Cluster{kube.DefaultClusterAlias: singleConfig}
    81  	}
    82  	return raw, nil
    83  }
    84  
    85  // createKubeConfigFromClusterMap creates a standard kube config from a cluster map.
    86  func createKubeConfigFromClusterMap(cm map[string]Cluster) ([]byte, error) {
    87  	config := clientcmdapi.Config{
    88  		APIVersion:     "v1",
    89  		Kind:           "Config",
    90  		Clusters:       []clientcmdapi.NamedCluster{},
    91  		AuthInfos:      []clientcmdapi.NamedAuthInfo{},
    92  		Contexts:       []clientcmdapi.NamedContext{},
    93  		CurrentContext: kube.DefaultClusterAlias,
    94  	}
    95  
    96  	names := make([]string, 0, len(cm))
    97  
    98  	for k := range cm {
    99  		names = append(names, k)
   100  	}
   101  
   102  	sort.Strings(names)
   103  
   104  	for _, name := range names {
   105  		config.Clusters = append(config.Clusters, clientcmdapi.NamedCluster{
   106  			Name: name,
   107  			Cluster: clientcmdapi.Cluster{
   108  				Server:                   cm[name].Endpoint,
   109  				CertificateAuthorityData: cm[name].ClusterCACertificate,
   110  			},
   111  		})
   112  		config.AuthInfos = append(config.AuthInfos, clientcmdapi.NamedAuthInfo{
   113  			Name: name,
   114  			AuthInfo: clientcmdapi.AuthInfo{
   115  				ClientCertificateData: cm[name].ClientCertificate,
   116  				ClientKeyData:         cm[name].ClientKey,
   117  			},
   118  		})
   119  		config.Contexts = append(config.Contexts, clientcmdapi.NamedContext{
   120  			Name: name,
   121  			Context: clientcmdapi.Context{
   122  				Cluster:  name,
   123  				AuthInfo: name,
   124  			},
   125  		})
   126  	}
   127  
   128  	return yaml.Marshal(config)
   129  }
   130  
   131  // main entry point.
   132  func main() {
   133  	var o options
   134  
   135  	o.parseFlags()
   136  
   137  	in, err := os.ReadFile(o.input)
   138  	if err != nil {
   139  		printErrAndExit(err, 1)
   140  	}
   141  
   142  	cm, err := unmarshalClusterMap(in)
   143  	if err != nil {
   144  		printErrAndExit(err, 1)
   145  	}
   146  
   147  	kc, err := createKubeConfigFromClusterMap(cm)
   148  	if err != nil {
   149  		printErrAndExit(err, 1)
   150  	}
   151  
   152  	dir := filepath.Dir(o.output)
   153  	if err = os.MkdirAll(dir, os.ModePerm); err != nil {
   154  		printErrAndExit(err, 1)
   155  	}
   156  
   157  	if err = os.WriteFile(o.output, kc, 0644); err != nil {
   158  		printErrAndExit(err, 1)
   159  	}
   160  }