github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/kubernetes/portforward/entry_manager.go (about)

     1  /*
     2  Copyright 2019 The Skaffold 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 portforward
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"sync"
    24  
    25  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
    26  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/event"
    27  	eventV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/v2"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
    30  )
    31  
    32  var (
    33  	portForwardEvent = func(entry *portForwardEntry) {
    34  		event.PortForwarded(
    35  			int32(entry.localPort),
    36  			entry.resource.Port,
    37  			entry.podName,
    38  			entry.containerName,
    39  			entry.resource.Namespace,
    40  			entry.portName,
    41  			string(entry.resource.Type),
    42  			entry.resource.Name,
    43  			entry.resource.Address)
    44  	}
    45  	portForwardEventV2 = func(entry *portForwardEntry) {
    46  		eventV2.PortForwarded(
    47  			int32(entry.localPort),
    48  			entry.resource.Port,
    49  			entry.podName,
    50  			entry.containerName,
    51  			entry.resource.Namespace,
    52  			entry.portName,
    53  			string(entry.resource.Type),
    54  			entry.resource.Name,
    55  			entry.resource.Address)
    56  	}
    57  )
    58  
    59  // EntryManager handles forwarding entries and keeping track of
    60  // forwarded ports and resources.
    61  type EntryManager struct {
    62  	entryForwarder EntryForwarder
    63  
    64  	// forwardedPorts serves as a synchronized set of ports we've forwarded.
    65  	forwardedPorts util.PortSet
    66  
    67  	// forwardedResources is a map of portForwardEntry key (string) -> portForwardEntry
    68  	forwardedResources sync.Map
    69  }
    70  
    71  // NewEntryManager returns a new port forward entry manager to keep track
    72  // of forwarded ports and resources
    73  func NewEntryManager(entryForwarder EntryForwarder) *EntryManager {
    74  	return &EntryManager{
    75  		entryForwarder: entryForwarder,
    76  	}
    77  }
    78  
    79  func (b *EntryManager) forwardPortForwardEntry(ctx context.Context, out io.Writer, entry *portForwardEntry) {
    80  	out, ctx = output.WithEventContext(ctx, out, constants.PortForward, fmt.Sprintf("%s/%s", entry.resource.Type, entry.resource.Name))
    81  
    82  	// Check if this resource has already been forwarded
    83  	if _, found := b.forwardedResources.LoadOrStore(entry.key(), entry); found {
    84  		return
    85  	}
    86  
    87  	if err := b.entryForwarder.Forward(ctx, entry); err == nil {
    88  		output.Green.Fprintln(
    89  			out,
    90  			fmt.Sprintf("Port forwarding %s/%s in namespace %s, remote port %s -> http://%s:%d",
    91  				entry.resource.Type,
    92  				entry.resource.Name,
    93  				entry.resource.Namespace,
    94  				entry.resource.Port.String(),
    95  				entry.resource.Address,
    96  				entry.localPort))
    97  	} else {
    98  		output.Red.Fprintln(out, err)
    99  	}
   100  	portForwardEvent(entry)
   101  	portForwardEventV2(entry)
   102  }
   103  
   104  // Start ensures the underlying entryForwarder is ready to forward.
   105  func (b *EntryManager) Start(out io.Writer) {
   106  	b.entryForwarder.Start(out)
   107  }
   108  
   109  // Stop terminates all kubectl port-forward commands.
   110  func (b *EntryManager) Stop() {
   111  	b.forwardedResources.Range(func(_, value interface{}) bool {
   112  		pfe := value.(*portForwardEntry)
   113  		b.Terminate(pfe)
   114  		return true
   115  	})
   116  }
   117  
   118  // Terminate terminates a single port forward entry
   119  func (b *EntryManager) Terminate(p *portForwardEntry) {
   120  	b.forwardedResources.Delete(p.key())
   121  	b.forwardedPorts.Delete(p.localPort)
   122  	b.entryForwarder.Terminate(p)
   123  }