k8s.io/kubernetes@v1.29.3/test/e2e_node/plugins/gcp-credential-provider/provider.go (about)

     1  /*
     2  Copyright 2022 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  // Originally copied from pkg/credentialproviders/gcp
    18  package main
    19  
    20  import (
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"net/http"
    26  
    27  	credentialproviderv1 "k8s.io/kubelet/pkg/apis/credentialprovider/v1"
    28  )
    29  
    30  const (
    31  	maxReadLength = 10 * 1 << 20 // 10MB
    32  )
    33  
    34  var containerRegistryUrls = []string{"container.cloud.google.com", "gcr.io", "*.gcr.io", "*.pkg.dev"}
    35  
    36  // HTTPError wraps a non-StatusOK error code as an error.
    37  type HTTPError struct {
    38  	StatusCode int
    39  	URL        string
    40  }
    41  
    42  var _ error = &HTTPError{}
    43  
    44  // Error implements error
    45  func (h *HTTPError) Error() string {
    46  	return fmt.Sprintf("http status code: %d while fetching url %s",
    47  		h.StatusCode, h.URL)
    48  }
    49  
    50  // TokenBlob is used to decode the JSON blob containing an access token
    51  // that is returned by GCE metadata.
    52  type TokenBlob struct {
    53  	AccessToken string `json:"access_token"`
    54  }
    55  
    56  type provider struct {
    57  	client        *http.Client
    58  	tokenEndpoint string
    59  }
    60  
    61  func (p *provider) Provide(image string) (map[string]credentialproviderv1.AuthConfig, error) {
    62  	cfg := map[string]credentialproviderv1.AuthConfig{}
    63  
    64  	tokenJSONBlob, err := readURL(p.tokenEndpoint, p.client)
    65  	if err != nil {
    66  		return cfg, err
    67  	}
    68  
    69  	var parsedBlob TokenBlob
    70  	if err := json.Unmarshal(tokenJSONBlob, &parsedBlob); err != nil {
    71  		return cfg, err
    72  	}
    73  
    74  	authConfig := credentialproviderv1.AuthConfig{
    75  		Username: "_token",
    76  		Password: parsedBlob.AccessToken,
    77  	}
    78  
    79  	// Add our entry for each of the supported container registry URLs
    80  	for _, k := range containerRegistryUrls {
    81  		cfg[k] = authConfig
    82  	}
    83  	return cfg, nil
    84  }
    85  
    86  func readURL(url string, client *http.Client) (body []byte, err error) {
    87  	req, err := http.NewRequest("GET", url, nil)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	req.Header = http.Header{
    93  		"Metadata-Flavor": []string{"Google"},
    94  	}
    95  
    96  	resp, err := client.Do(req)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	defer resp.Body.Close()
   101  
   102  	if resp.StatusCode != http.StatusOK {
   103  		return nil, &HTTPError{
   104  			StatusCode: resp.StatusCode,
   105  			URL:        url,
   106  		}
   107  	}
   108  
   109  	limitedReader := &io.LimitedReader{R: resp.Body, N: maxReadLength}
   110  	contents, err := io.ReadAll(limitedReader)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	if limitedReader.N <= 0 {
   116  		return nil, errors.New("the read limit is reached")
   117  	}
   118  
   119  	return contents, nil
   120  }