kic on mac: add service cmd support
parent
3cf2c9bbe3
commit
f390d4bf30
|
|
@ -20,8 +20,13 @@ import (
|
|||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/browser"
|
||||
|
|
@ -32,9 +37,11 @@ import (
|
|||
"k8s.io/minikube/pkg/minikube/config"
|
||||
pkg_config "k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/exit"
|
||||
"k8s.io/minikube/pkg/minikube/localpath"
|
||||
"k8s.io/minikube/pkg/minikube/machine"
|
||||
"k8s.io/minikube/pkg/minikube/out"
|
||||
"k8s.io/minikube/pkg/minikube/service"
|
||||
"k8s.io/minikube/pkg/minikube/tunnel/kic"
|
||||
)
|
||||
|
||||
const defaultServiceFormatTemplate = "http://{{.IP}}:{{.Port}}"
|
||||
|
|
@ -85,34 +92,17 @@ var serviceCmd = &cobra.Command{
|
|||
exit.WithError("Error getting config", err)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && cfg.Driver == oci.Docker {
|
||||
startKicServiceTunnel(svc, cfg.Name)
|
||||
return
|
||||
}
|
||||
|
||||
urls, err := service.WaitForService(api, namespace, svc, serviceURLTemplate, serviceURLMode, https, wait, interval)
|
||||
if err != nil {
|
||||
exit.WithError("Error opening service", err)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && cfg.Driver == oci.Docker {
|
||||
out.FailureT("Opening service in browser is not implemented yet for docker driver on Mac.\nThe following issue is tracking the in progress work:\nhttps://github.com/kubernetes/minikube/issues/6778")
|
||||
exit.WithCodeT(exit.Unavailable, "Not yet implemented for docker driver on MacOS.")
|
||||
}
|
||||
|
||||
for _, u := range urls {
|
||||
_, err := url.Parse(u)
|
||||
if err != nil {
|
||||
glog.Warningf("failed to parse url %q: %v (will not open)", u, err)
|
||||
out.String(fmt.Sprintf("%s\n", u))
|
||||
continue
|
||||
}
|
||||
|
||||
if serviceURLMode {
|
||||
out.String(fmt.Sprintf("%s\n", u))
|
||||
continue
|
||||
}
|
||||
|
||||
out.T(out.Celebrate, "Opening service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": svc})
|
||||
if err := browser.OpenURL(u); err != nil {
|
||||
exit.WithError(fmt.Sprintf("open url failed: %s", u), err)
|
||||
}
|
||||
}
|
||||
openURLs(svc, urls)
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -126,3 +116,65 @@ func init() {
|
|||
serviceCmd.PersistentFlags().StringVar(&serviceURLFormat, "format", defaultServiceFormatTemplate, "Format to output service URL in. This format will be applied to each url individually and they will be printed one at a time.")
|
||||
|
||||
}
|
||||
|
||||
func startKicServiceTunnel(svc, configName string) {
|
||||
ctrlC := make(chan os.Signal, 1)
|
||||
signal.Notify(ctrlC, os.Interrupt)
|
||||
|
||||
clientset, err := service.K8s.GetClientset(1 * time.Second)
|
||||
if err != nil {
|
||||
exit.WithError("error creating clientset", err)
|
||||
}
|
||||
|
||||
port, err := oci.HostPortBinding(oci.Docker, configName, 22)
|
||||
if err != nil {
|
||||
exit.WithError("error getting ssh port", err)
|
||||
}
|
||||
sshPort := strconv.Itoa(port)
|
||||
sshKey := filepath.Join(localpath.MiniPath(), "machines", configName, "id_rsa")
|
||||
|
||||
serviceTunnel := kic.NewServiceTunnel(sshPort, sshKey, clientset.CoreV1())
|
||||
urls, err := serviceTunnel.Start(svc, namespace)
|
||||
if err != nil {
|
||||
exit.WithError("error starting tunnel", err)
|
||||
}
|
||||
|
||||
// wait for tunnel to come up
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
data := [][]string{{namespace, svc, "", strings.Join(urls, "\n")}}
|
||||
service.PrintServiceList(os.Stdout, data)
|
||||
|
||||
openURLs(svc, urls)
|
||||
|
||||
<-ctrlC
|
||||
|
||||
err = serviceTunnel.Stop()
|
||||
if err != nil {
|
||||
exit.WithError("error stopping tunnel", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func openURLs(svc string, urls []string) {
|
||||
for _, u := range urls {
|
||||
_, err := url.Parse(u)
|
||||
if err != nil {
|
||||
glog.Warningf("failed to parse url %q: %v (will not open)", u, err)
|
||||
out.String(fmt.Sprintf("%s\n", u))
|
||||
continue
|
||||
}
|
||||
|
||||
if serviceURLMode {
|
||||
out.String(fmt.Sprintf("%s\n", u))
|
||||
continue
|
||||
}
|
||||
|
||||
out.T(out.Celebrate, "Opening service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": svc})
|
||||
if err := browser.OpenURL(u); err != nil {
|
||||
exit.WithError(fmt.Sprintf("open url failed: %s", u), err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 kic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
typed_core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
// ServiceTunnel ...
|
||||
type ServiceTunnel struct {
|
||||
sshPort string
|
||||
sshKey string
|
||||
v1Core typed_core.CoreV1Interface
|
||||
sshConn *sshConn
|
||||
}
|
||||
|
||||
// NewServiceTunnel ...
|
||||
func NewServiceTunnel(sshPort, sshKey string, v1Core typed_core.CoreV1Interface) *ServiceTunnel {
|
||||
return &ServiceTunnel{
|
||||
sshPort: sshPort,
|
||||
sshKey: sshKey,
|
||||
v1Core: v1Core,
|
||||
}
|
||||
}
|
||||
|
||||
// Start ...
|
||||
func (t *ServiceTunnel) Start(svcName, namespace string) ([]string, error) {
|
||||
svc, err := t.v1Core.Services(namespace).Get(svcName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
glog.Errorf("error listing services: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.sshConn = createSSHConn(svcName, t.sshPort, t.sshKey, svc)
|
||||
|
||||
go func() {
|
||||
err = t.sshConn.startAndWait()
|
||||
if err != nil {
|
||||
glog.Errorf("error starting ssh tunnel: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
urls := make([]string, 0, len(svc.Spec.Ports))
|
||||
for _, port := range svc.Spec.Ports {
|
||||
urls = append(urls, fmt.Sprintf("http://127.0.0.1:%d", port.Port))
|
||||
}
|
||||
|
||||
return urls, nil
|
||||
}
|
||||
|
||||
// Stop ...
|
||||
func (t *ServiceTunnel) Stop() error {
|
||||
err := t.sshConn.stop()
|
||||
if err != nil {
|
||||
glog.Errorf("error stopping ssh tunnel: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ type sshConn struct {
|
|||
cmd *exec.Cmd
|
||||
}
|
||||
|
||||
func createSSHConn(name, sshPort, sshKey string, svc v1.Service) *sshConn {
|
||||
func createSSHConn(name, sshPort, sshKey string, svc *v1.Service) *sshConn {
|
||||
// extract sshArgs
|
||||
sshArgs := []string{
|
||||
// TODO: document the options here
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ func (t *SSHTunnel) startConnection(svc v1.Service) {
|
|||
}
|
||||
|
||||
// create new ssh conn
|
||||
newSSHConn := createSSHConn(uniqName, t.sshPort, t.sshKey, svc)
|
||||
newSSHConn := createSSHConn(uniqName, t.sshPort, t.sshKey, &svc)
|
||||
t.conns[newSSHConn.name] = newSSHConn
|
||||
|
||||
go func() {
|
||||
|
|
@ -147,3 +147,33 @@ func sshConnUniqName(service v1.Service) string {
|
|||
|
||||
return strings.Join(n, "")
|
||||
}
|
||||
|
||||
// StartServiceTunnel ...
|
||||
func (t *SSHTunnel) StartServiceTunnel(svcName string) error {
|
||||
svc, err := t.v1Core.Services("default").Get(svcName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
glog.Errorf("error listing services: %v", err)
|
||||
}
|
||||
|
||||
newSSHConn := createSSHConn(svcName, t.sshPort, t.sshKey, svc)
|
||||
|
||||
go func() {
|
||||
err := newSSHConn.startAndWait()
|
||||
if err != nil {
|
||||
glog.Errorf("error starting ssh tunnel: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
//err = t.LoadBalancerEmulator.PatchServiceIP(t.v1Core.RESTClient(), svc, "127.0.0.1")
|
||||
//if err != nil {
|
||||
// glog.Errorf("error patching service: %v", err)
|
||||
//}
|
||||
|
||||
<-t.ctx.Done()
|
||||
_, err = t.LoadBalancerEmulator.Cleanup()
|
||||
if err != nil {
|
||||
glog.Errorf("error cleaning up: %v", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue