k8s.io/kubernetes@v1.29.3/test/integration/apiserver/discovery/service.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  package discovery
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"net/url"
    26  	"strconv"
    27  	"sync"
    28  	"time"
    29  
    30  	"k8s.io/apimachinery/pkg/util/wait"
    31  
    32  	"k8s.io/client-go/kubernetes"
    33  
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  
    36  	corev1apply "k8s.io/client-go/applyconfigurations/core/v1"
    37  )
    38  
    39  type FakeService interface {
    40  	Run(ctx context.Context) error
    41  	Port() *int32
    42  	Name() string
    43  }
    44  
    45  // Creates and registers an in-process Service capable of communicating with the
    46  // kubernetes integration test apiserver
    47  type fakeService struct {
    48  	name    string
    49  	client  kubernetes.Interface
    50  	handler http.Handler
    51  
    52  	lock       sync.RWMutex
    53  	activePort *int32
    54  }
    55  
    56  func NewFakeService(name string, client kubernetes.Interface, handler http.Handler) *fakeService {
    57  	return &fakeService{
    58  		name:    name,
    59  		client:  client,
    60  		handler: handler,
    61  	}
    62  }
    63  
    64  func (f *fakeService) Run(ctx context.Context) error {
    65  	aggregatedServer := httptest.NewUnstartedServer(f.handler)
    66  	aggregatedServer.StartTLS()
    67  	defer aggregatedServer.Close()
    68  
    69  	serverURL, err := url.Parse(aggregatedServer.URL)
    70  	if err != nil {
    71  		// This should never occur
    72  		panic(err)
    73  	}
    74  
    75  	serverPort, err := strconv.Atoi(serverURL.Port())
    76  	if err != nil {
    77  		// This should never occur
    78  		panic(err)
    79  	}
    80  
    81  	port := int32(serverPort)
    82  
    83  	// Install service into the cluster
    84  	service, err := f.client.CoreV1().Services("default").Apply(
    85  		ctx,
    86  		corev1apply.Service(f.name, "default").
    87  			WithSpec(corev1apply.ServiceSpec().
    88  				WithPorts(
    89  					corev1apply.ServicePort().
    90  						WithPort(port)).
    91  				WithType("ExternalName").
    92  				WithExternalName("localhost")),
    93  		metav1.ApplyOptions{
    94  			FieldManager: "test-manager",
    95  		},
    96  	)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	f.lock.Lock()
   102  	f.activePort = &port
   103  	f.lock.Unlock()
   104  
   105  	<-ctx.Done()
   106  
   107  	f.lock.Lock()
   108  	f.activePort = nil
   109  	f.lock.Unlock()
   110  
   111  	// Uninstall service from the cluser
   112  	err = f.client.CoreV1().Services("default").Delete(ctx, service.Name, metav1.DeleteOptions{})
   113  	if errors.Is(err, context.Canceled) {
   114  		err = nil
   115  	}
   116  	return err
   117  }
   118  
   119  func (f *fakeService) WaitForReady(ctx context.Context) error {
   120  	err := wait.PollUntilContextTimeout(ctx, 200*time.Millisecond, time.Second, false, func(ctx context.Context) (done bool, err error) {
   121  		return f.Port() != nil, nil
   122  	})
   123  
   124  	if errors.Is(err, context.Canceled) {
   125  		err = nil
   126  	} else if err != nil {
   127  		err = fmt.Errorf("service should have come alive in a reasonable amount of time: %w", err)
   128  	}
   129  
   130  	return err
   131  }
   132  
   133  func (f *fakeService) Port() *int32 {
   134  	// Returns the port of the server if it is running or nil
   135  	// if it is not running
   136  	f.lock.RLock()
   137  	defer f.lock.RUnlock()
   138  	return f.activePort
   139  }
   140  
   141  func (f *fakeService) Name() string {
   142  	return f.name
   143  }