Merge pull request #818 from aaron-prindle/heapster-addon
Added heapster to set of addonspull/834/head
commit
41998bdad0
|
@ -130,6 +130,12 @@ var settings = []Setting{
|
|||
validations: []setFn{IsValidAddon},
|
||||
callbacks: []setFn{EnableOrDisableAddon},
|
||||
},
|
||||
{
|
||||
name: "heapster",
|
||||
set: SetBool,
|
||||
validations: []setFn{IsValidAddon},
|
||||
callbacks: []setFn{EnableOrDisableAddon},
|
||||
},
|
||||
}
|
||||
|
||||
var ConfigCmd = &cobra.Command{
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright 2016 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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/minikube/pkg/minikube/assets"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
namespace string
|
||||
https bool
|
||||
addonsURLMode bool
|
||||
addonsURLFormat string
|
||||
addonsURLTemplate *template.Template
|
||||
)
|
||||
|
||||
const defaultAddonsFormatTemplate = "http://{{.IP}}:{{.Port}}"
|
||||
|
||||
var addonsOpenCmd = &cobra.Command{
|
||||
Use: "open ADDON_NAME",
|
||||
Short: "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list ",
|
||||
Long: "Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list ",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
t, err := template.New("addonsURL").Parse(addonsURLFormat)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "The value passed to --format is invalid:\n\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
addonsURLTemplate = t
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: minikube addons open ADDON_NAME")
|
||||
os.Exit(1)
|
||||
}
|
||||
addonName := args[0]
|
||||
api := libmachine.NewClient(constants.Minipath, constants.MakeMiniPath("certs"))
|
||||
defer api.Close()
|
||||
|
||||
cluster.EnsureMinikubeRunningOrExit(api, 1)
|
||||
addon, ok := assets.Addons[addonName] // validate addon input
|
||||
if !ok {
|
||||
fmt.Fprintln(os.Stderr, fmt.Sprintf(`addon '%s' is not a valid addon packaged with minikube.
|
||||
To see the list of available addons run:
|
||||
minikube addons list`, addonName))
|
||||
os.Exit(1)
|
||||
}
|
||||
ok, err := addon.IsEnabled()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if !ok {
|
||||
fmt.Fprintln(os.Stderr, fmt.Sprintf(`addon '%s' is currently not enabled.
|
||||
To enable this addon run:
|
||||
minikube addons enable %s`, addonName, addonName))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
namespace := "kube-system"
|
||||
key := "kubernetes.io/minikube-addons-endpoint"
|
||||
if err := util.RetryAfter(20,
|
||||
func() error {
|
||||
_, err := cluster.GetServiceListByLabel(namespace, key, addonName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, 6*time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not find endpoint for addon %s: %s\n", addonName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
serviceList, err := cluster.GetServiceListByLabel(namespace, key, addonName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error getting service with namespace :%s and labels %s:%s: %s", namespace, key, addonName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for i := range serviceList.Items {
|
||||
service := serviceList.Items[i].ObjectMeta.Name
|
||||
cluster.WaitAndMaybeOpenService(api, namespace, service, addonsURLTemplate, addonsURLMode, https)
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
AddonsCmd.AddCommand(addonsOpenCmd)
|
||||
addonsOpenCmd.Flags().BoolVar(&addonsURLMode, "url", false, "Display the kubernetes addons URL in the CLI instead of opening it in the default browser")
|
||||
addonsOpenCmd.Flags().BoolVar(&https, "https", false, "Open the addons URL with https instead of http")
|
||||
|
||||
addonsOpenCmd.PersistentFlags().StringVar(&addonsURLFormat, "format", defaultAddonsFormatTemplate, "Format to output addons URL in. This format will be applied to each url individually and they will be printed one at a time.")
|
||||
AddonsCmd.AddCommand(addonsOpenCmd)
|
||||
}
|
|
@ -49,7 +49,7 @@ var dashboardCmd = &cobra.Command{
|
|||
namespace := "kube-system"
|
||||
service := "kubernetes-dashboard"
|
||||
|
||||
if err := commonutil.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
|
||||
if err := commonutil.RetryAfter(20, func() error { return cluster.CheckService(namespace, service) }, 6*time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s\n", service, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
|
@ -19,18 +19,13 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/pkg/browser"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
kubeapi "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/minikube/pkg/minikube/cluster"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -46,7 +41,7 @@ var serviceCmd = &cobra.Command{
|
|||
Use: "service [flags] SERVICE",
|
||||
Short: "Gets the kubernetes URL(s) for the specified service in your local cluster",
|
||||
Long: `Gets the kubernetes URL(s) for the specified service in your local cluster. In the case of multiple URLs they will be printed one at a time`,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
t, err := template.New("serviceURL").Parse(serviceURLFormat)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "The value passed to --format is invalid:\n\n", err)
|
||||
|
@ -71,28 +66,7 @@ var serviceCmd = &cobra.Command{
|
|||
service, namespace))
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := util.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s\n", service, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
urls, err := cluster.GetServiceURLsForService(api, namespace, service, serviceURLTemplate)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "Check that minikube is running and that you have specified the correct namespace (-n flag).")
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, url := range urls {
|
||||
if https {
|
||||
url = strings.Replace(url, "http", "https", 1)
|
||||
}
|
||||
if serviceURLMode || !strings.HasPrefix(url, "http") {
|
||||
fmt.Fprintln(os.Stdout, url)
|
||||
} else {
|
||||
fmt.Fprintln(os.Stdout, "Opening kubernetes service "+namespace+"/"+service+" in default browser...")
|
||||
browser.OpenURL(url)
|
||||
}
|
||||
}
|
||||
cluster.WaitAndMaybeOpenService(api, namespace, service, serviceURLTemplate, serviceURLMode, https)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -119,37 +93,3 @@ func validateService(namespace string, service string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckService waits for the specified service to be ready by returning an error until the service is up
|
||||
// The check is done by polling the endpoint associated with the service and when the endpoint exists, returning no error->service-online
|
||||
func CheckService(namespace string, service string) error {
|
||||
client, err := cluster.GetKubernetesClient()
|
||||
if err != nil {
|
||||
return &util.RetriableError{Err: err}
|
||||
}
|
||||
endpoints := client.Endpoints(namespace)
|
||||
if err != nil {
|
||||
return &util.RetriableError{Err: err}
|
||||
}
|
||||
endpoint, err := endpoints.Get(service)
|
||||
if err != nil {
|
||||
return &util.RetriableError{Err: err}
|
||||
}
|
||||
return CheckEndpointReady(endpoint)
|
||||
}
|
||||
|
||||
const notReadyMsg = "Waiting, endpoint for service is not ready yet...\n"
|
||||
|
||||
func CheckEndpointReady(endpoint *kubeapi.Endpoints) error {
|
||||
if len(endpoint.Subsets) == 0 {
|
||||
fmt.Fprintf(os.Stderr, notReadyMsg)
|
||||
return &util.RetriableError{Err: errors.New("Endpoint for service is not ready yet")}
|
||||
}
|
||||
for _, subset := range endpoint.Subsets {
|
||||
if len(subset.Addresses) == 0 {
|
||||
fmt.Fprintf(os.Stderr, notReadyMsg)
|
||||
return &util.RetriableError{Err: errors.New("No endpoints for service are ready yet")}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kubeApi "k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
func TestCheckEndpointReady(t *testing.T) {
|
||||
endpointNoSubsets := &kubeApi.Endpoints{}
|
||||
if err := CheckEndpointReady(endpointNoSubsets); err == nil {
|
||||
t.Fatalf("Endpoint had no subsets but CheckEndpointReady did not return an error")
|
||||
}
|
||||
|
||||
endpointNotReady := &kubeApi.Endpoints{
|
||||
Subsets: []kubeApi.EndpointSubset{
|
||||
{Addresses: []kubeApi.EndpointAddress{},
|
||||
NotReadyAddresses: []kubeApi.EndpointAddress{
|
||||
{IP: "1.1.1.1"},
|
||||
{IP: "2.2.2.2"},
|
||||
{IP: "3.3.3.3"},
|
||||
}}}}
|
||||
if err := CheckEndpointReady(endpointNotReady); err == nil {
|
||||
t.Fatalf("Endpoint had no Addresses but CheckEndpointReady did not return an error")
|
||||
}
|
||||
|
||||
endpointReady := &kubeApi.Endpoints{
|
||||
Subsets: []kubeApi.EndpointSubset{
|
||||
{Addresses: []kubeApi.EndpointAddress{
|
||||
{IP: "1.1.1.1"},
|
||||
{IP: "2.2.2.2"},
|
||||
},
|
||||
NotReadyAddresses: []kubeApi.EndpointAddress{},
|
||||
}},
|
||||
}
|
||||
if err := CheckEndpointReady(endpointReady); err != nil {
|
||||
t.Fatalf("Endpoint was ready with at least one Address, but CheckEndpointReady returned an error")
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ metadata:
|
|||
labels:
|
||||
component: kube-addon-manager
|
||||
version: v5.1
|
||||
kubernetes.io/minikube-addons: addon-manager
|
||||
spec:
|
||||
hostNetwork: true
|
||||
containers:
|
||||
|
|
|
@ -21,6 +21,7 @@ metadata:
|
|||
app: kubernetes-dashboard
|
||||
version: v1.4.2
|
||||
kubernetes.io/cluster-service: "true"
|
||||
kubernetes.io/minikube-addons: dashboard
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
|
|
|
@ -21,6 +21,8 @@ metadata:
|
|||
labels:
|
||||
app: kubernetes-dashboard
|
||||
kubernetes.io/cluster-service: "true"
|
||||
kubernetes.io/minikube-addons: dashboard
|
||||
kubernetes.io/minikube-addons-endpoint: dashboard
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/cluster-service: 'true'
|
||||
kubernetes.io/name: monitoring-grafana
|
||||
kubernetes.io/minikube-addons: heapster
|
||||
kubernetes.io/minikube-addons-endpoint: heapster
|
||||
name: monitoring-grafana
|
||||
namespace: kube-system
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 3000
|
||||
selector:
|
||||
kubernetes.io/cluster-service: "true"
|
||||
name: influxGrafana
|
|
@ -0,0 +1,38 @@
|
|||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/cluster-service: 'true'
|
||||
k8s-app: heapster
|
||||
name: heapster
|
||||
kubernetes.io/minikube-addons: heapster
|
||||
name: heapster
|
||||
namespace: kube-system
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
kubernetes.io/cluster-service: "true"
|
||||
k8s-app: heapster
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/cluster-service: 'true'
|
||||
k8s-app: heapster
|
||||
spec:
|
||||
containers:
|
||||
- name: heapster
|
||||
image: gcr.io/google_containers/heapster:v1.2.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /heapster
|
||||
- --source=kubernetes
|
||||
- --sink=influxdb:http://monitoring-influxdb:8086
|
||||
- --metric_resolution=60s
|
||||
volumeMounts:
|
||||
- name: ssl-certs
|
||||
mountPath: /etc/ssl/certs
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: ssl-certs
|
||||
hostPath:
|
||||
path: /etc/ssl/certs
|
|
@ -0,0 +1,16 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/cluster-service: 'true'
|
||||
kubernetes.io/name: heapster
|
||||
kubernetes.io/minikube-addons: heapster
|
||||
name: heapster
|
||||
namespace: kube-system
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8082
|
||||
selector:
|
||||
kubernetes.io/cluster-service: "true"
|
||||
k8s-app: heapster
|
|
@ -0,0 +1,67 @@
|
|||
# Copyright 2016 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.
|
||||
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/cluster-service: 'true'
|
||||
name: influxGrafana
|
||||
kubernetes.io/minikube-addons: heapster
|
||||
name: influxdb-grafana
|
||||
namespace: kube-system
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
kubernetes.io/cluster-service: "true"
|
||||
name: influxGrafana
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/cluster-service: 'true'
|
||||
name: influxGrafana
|
||||
spec:
|
||||
containers:
|
||||
- name: influxdb
|
||||
image: kubernetes/heapster_influxdb:v0.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
volumeMounts:
|
||||
- mountPath: /data
|
||||
name: influxdb-storage
|
||||
- name: grafana
|
||||
image: gcr.io/google_containers/heapster_grafana:v2.6.0-2
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: INFLUXDB_SERVICE_URL
|
||||
value: http://localhost:8086
|
||||
# The following env variables are required to make Grafana accessible via
|
||||
# the kubernetes api-server proxy. On production clusters, we recommend
|
||||
# removing these env variables, setup auth for grafana, and expose the grafana
|
||||
# service using a LoadBalancer or a public IP.
|
||||
- name: GF_AUTH_BASIC_ENABLED
|
||||
value: "false"
|
||||
- name: GF_AUTH_ANONYMOUS_ENABLED
|
||||
value: "true"
|
||||
- name: GF_AUTH_ANONYMOUS_ORG_ROLE
|
||||
value: Admin
|
||||
- name: GF_SERVER_ROOT_URL
|
||||
value: /
|
||||
volumeMounts:
|
||||
- mountPath: /var
|
||||
name: grafana-storage
|
||||
volumes:
|
||||
- name: influxdb-storage
|
||||
emptyDir: {}
|
||||
- name: grafana-storage
|
||||
emptyDir: {}
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/cluster-service: 'true'
|
||||
kubernetes.io/name: monitoring-influxdb
|
||||
kubernetes.io/minikube-addons: heapster
|
||||
name: monitoring-influxdb
|
||||
namespace: kube-system
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 8083
|
||||
targetPort: 8083
|
||||
- name: api
|
||||
port: 8086
|
||||
targetPort: 8086
|
||||
selector:
|
||||
kubernetes.io/cluster-service: "true"
|
||||
name: influxGrafana
|
|
@ -315,6 +315,68 @@ _minikube_addons_list()
|
|||
noun_aliases=()
|
||||
}
|
||||
|
||||
_minikube_addons_open()
|
||||
{
|
||||
last_command="minikube_addons_open"
|
||||
commands=()
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
local_nonpersistent_flags=()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--format=")
|
||||
flags+=("--https")
|
||||
local_nonpersistent_flags+=("--https")
|
||||
flags+=("--url")
|
||||
local_nonpersistent_flags+=("--url")
|
||||
flags+=("--alsologtostderr")
|
||||
flags+=("--log_backtrace_at=")
|
||||
flags+=("--log_dir=")
|
||||
flags+=("--logtostderr")
|
||||
flags+=("--show-libmachine-logs")
|
||||
flags+=("--stderrthreshold=")
|
||||
flags+=("--v=")
|
||||
two_word_flags+=("-v")
|
||||
flags+=("--vmodule=")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
noun_aliases=()
|
||||
}
|
||||
|
||||
_minikube_addons_open()
|
||||
{
|
||||
last_command="minikube_addons_open"
|
||||
commands=()
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
local_nonpersistent_flags=()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--format=")
|
||||
flags+=("--https")
|
||||
local_nonpersistent_flags+=("--https")
|
||||
flags+=("--url")
|
||||
local_nonpersistent_flags+=("--url")
|
||||
flags+=("--alsologtostderr")
|
||||
flags+=("--log_backtrace_at=")
|
||||
flags+=("--log_dir=")
|
||||
flags+=("--logtostderr")
|
||||
flags+=("--show-libmachine-logs")
|
||||
flags+=("--stderrthreshold=")
|
||||
flags+=("--v=")
|
||||
two_word_flags+=("-v")
|
||||
flags+=("--vmodule=")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
noun_aliases=()
|
||||
}
|
||||
|
||||
_minikube_addons()
|
||||
{
|
||||
last_command="minikube_addons"
|
||||
|
@ -322,6 +384,8 @@ _minikube_addons()
|
|||
commands+=("disable")
|
||||
commands+=("enable")
|
||||
commands+=("list")
|
||||
commands+=("open")
|
||||
commands+=("open")
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
|
|
|
@ -37,4 +37,6 @@ For the list of accessible variables for the template, see the struct values her
|
|||
* [minikube addons disable](minikube_addons_disable.md) - Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list
|
||||
* [minikube addons enable](minikube_addons_enable.md) - Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list
|
||||
* [minikube addons list](minikube_addons_list.md) - Lists all available minikube addons as well as there current status (enabled/disabled)
|
||||
* [minikube addons open](minikube_addons_open.md) - Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list
|
||||
* [minikube addons open](minikube_addons_open.md) - Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
## minikube addons open
|
||||
|
||||
Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Opens the addon w/ADDON_NAME within minikube (example: minikube addons open dashboard). For a list of available addons use: minikube addons list
|
||||
|
||||
```
|
||||
minikube addons open ADDON_NAME
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--format string Format to output addons URL in. This format will be applied to each url individually and they will be printed one at a time. (default "http://{{.IP}}:{{.Port}}")
|
||||
--https Open the addons URL with https instead of http
|
||||
--url Display the kubernetes addons URL in the CLI instead of opening it in the default browser
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--alsologtostderr log to standard error as well as files
|
||||
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
|
||||
--log_dir string If non-empty, write log files in this directory (default "")
|
||||
--logtostderr log to standard error instead of files
|
||||
--show-libmachine-logs Whether or not to show logs from libmachine.
|
||||
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
|
||||
-v, --v Level log level for V logs
|
||||
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
* [minikube addons](minikube_addons.md) - Modify minikube's kubernetes addons
|
||||
|
|
@ -25,6 +25,7 @@ Configurable fields:
|
|||
* dashboard
|
||||
* addon-manager
|
||||
* kube-dns
|
||||
* heapster
|
||||
|
||||
```
|
||||
minikube config SUBCOMMAND [flags]
|
||||
|
|
|
@ -86,6 +86,33 @@ var Addons = map[string]*Addon{
|
|||
"kube-dns-svc.yaml",
|
||||
"0640"),
|
||||
}, true, "kube-dns"),
|
||||
"heapster": NewAddon([]*MemoryAsset{
|
||||
NewMemoryAsset(
|
||||
"deploy/addons/influxGrafana-rc.yaml",
|
||||
constants.AddonsPath,
|
||||
"influxGrafana-rc.yaml",
|
||||
"0640"),
|
||||
NewMemoryAsset(
|
||||
"deploy/addons/grafana-svc.yaml",
|
||||
constants.AddonsPath,
|
||||
"grafana-svc.yaml",
|
||||
"0640"),
|
||||
NewMemoryAsset(
|
||||
"deploy/addons/influxdb-svc.yaml",
|
||||
constants.AddonsPath,
|
||||
"influxdb-svc.yaml",
|
||||
"0640"),
|
||||
NewMemoryAsset(
|
||||
"deploy/addons/heapster-rc.yaml",
|
||||
constants.AddonsPath,
|
||||
"heapster-rc.yaml",
|
||||
"0640"),
|
||||
NewMemoryAsset(
|
||||
"deploy/addons/heapster-svc.yaml",
|
||||
constants.AddonsPath,
|
||||
"heapster-svc.yaml",
|
||||
"0640"),
|
||||
}, false, "heapster"),
|
||||
}
|
||||
|
||||
func AddMinikubeAddonsDirToAssets(assetList *[]CopyableFile) {
|
||||
|
|
|
@ -39,10 +39,12 @@ import (
|
|||
"github.com/docker/machine/libmachine/state"
|
||||
"github.com/golang/glog"
|
||||
download "github.com/jimmidyson/go-download"
|
||||
"github.com/pkg/browser"
|
||||
"github.com/pkg/errors"
|
||||
kubeapi "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
|
||||
"k8s.io/minikube/pkg/minikube/assets"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
|
@ -683,3 +685,85 @@ func GetServiceURLs(api libmachine.API, namespace string, t *template.Template)
|
|||
|
||||
return serviceURLs, nil
|
||||
}
|
||||
|
||||
// CheckService waits for the specified service to be ready by returning an error until the service is up
|
||||
// The check is done by polling the endpoint associated with the service and when the endpoint exists, returning no error->service-online
|
||||
func CheckService(namespace string, service string) error {
|
||||
client, err := GetKubernetesClient()
|
||||
if err != nil {
|
||||
return &util.RetriableError{Err: err}
|
||||
}
|
||||
endpoints := client.Endpoints(namespace)
|
||||
if err != nil {
|
||||
return &util.RetriableError{Err: err}
|
||||
}
|
||||
endpoint, err := endpoints.Get(service)
|
||||
if err != nil {
|
||||
return &util.RetriableError{Err: err}
|
||||
}
|
||||
return checkEndpointReady(endpoint)
|
||||
}
|
||||
|
||||
func checkEndpointReady(endpoint *kubeapi.Endpoints) error {
|
||||
const notReadyMsg = "Waiting, endpoint for service is not ready yet...\n"
|
||||
if len(endpoint.Subsets) == 0 {
|
||||
fmt.Fprintf(os.Stderr, notReadyMsg)
|
||||
return &util.RetriableError{Err: errors.New("Endpoint for service is not ready yet")}
|
||||
}
|
||||
for _, subset := range endpoint.Subsets {
|
||||
if len(subset.Addresses) == 0 {
|
||||
fmt.Fprintf(os.Stderr, notReadyMsg)
|
||||
return &util.RetriableError{Err: errors.New("No endpoints for service are ready yet")}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func WaitAndMaybeOpenService(api libmachine.API, namespace string, service string, urlTemplate *template.Template, urlMode bool, https bool) {
|
||||
if err := util.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s\n", service, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
urls, err := GetServiceURLsForService(api, namespace, service, urlTemplate)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "Check that minikube is running and that you have specified the correct namespace (-n flag).")
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, url := range urls {
|
||||
if https {
|
||||
url = strings.Replace(url, "http", "https", 1)
|
||||
}
|
||||
if urlMode || !strings.HasPrefix(url, "http") {
|
||||
fmt.Fprintln(os.Stdout, url)
|
||||
} else {
|
||||
fmt.Fprintln(os.Stdout, "Opening kubernetes service "+namespace+"/"+service+" in default browser...")
|
||||
browser.OpenURL(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetServiceListByLabel(namespace string, key string, value string) (*kubeapi.ServiceList, error) {
|
||||
client, err := GetKubernetesClient()
|
||||
if err != nil {
|
||||
return &kubeapi.ServiceList{}, &util.RetriableError{Err: err}
|
||||
}
|
||||
services := client.Services(namespace)
|
||||
if err != nil {
|
||||
return &kubeapi.ServiceList{}, &util.RetriableError{Err: err}
|
||||
}
|
||||
return getServiceListFromServicesByLabel(services, key, value)
|
||||
}
|
||||
|
||||
func getServiceListFromServicesByLabel(services unversioned.ServiceInterface, key string, value string) (*kubeapi.ServiceList, error) {
|
||||
selector := labels.SelectorFromSet(labels.Set(map[string]string{key: value}))
|
||||
serviceList, err := services.List(kubeapi.ListOptions{LabelSelector: selector})
|
||||
if err != nil {
|
||||
return &kubeapi.ServiceList{}, &util.RetriableError{Err: err}
|
||||
}
|
||||
if len(serviceList.Items) == 0 {
|
||||
return &kubeapi.ServiceList{}, &util.RetriableError{Err: err}
|
||||
}
|
||||
return serviceList, nil
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
"k8s.io/minikube/pkg/minikube/assets"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/minikube/tests"
|
||||
|
@ -779,3 +781,97 @@ func TestUpdateCustomAddons(t *testing.T) {
|
|||
t.Fatalf("Custom addon not copied. Expected transfers to contain custom addon with content: %s. It was: %s", testContent2, transferred)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckEndpointReady(t *testing.T) {
|
||||
endpointNoSubsets := &api.Endpoints{}
|
||||
if err := checkEndpointReady(endpointNoSubsets); err == nil {
|
||||
t.Fatalf("Endpoint had no subsets but checkEndpointReady did not return an error")
|
||||
}
|
||||
|
||||
endpointNotReady := &api.Endpoints{
|
||||
Subsets: []api.EndpointSubset{
|
||||
{Addresses: []api.EndpointAddress{},
|
||||
NotReadyAddresses: []api.EndpointAddress{
|
||||
{IP: "1.1.1.1"},
|
||||
{IP: "2.2.2.2"},
|
||||
{IP: "3.3.3.3"},
|
||||
}}}}
|
||||
if err := checkEndpointReady(endpointNotReady); err == nil {
|
||||
t.Fatalf("Endpoint had no Addresses but checkEndpointReady did not return an error")
|
||||
}
|
||||
|
||||
endpointReady := &api.Endpoints{
|
||||
Subsets: []api.EndpointSubset{
|
||||
{Addresses: []api.EndpointAddress{
|
||||
{IP: "1.1.1.1"},
|
||||
{IP: "2.2.2.2"},
|
||||
},
|
||||
NotReadyAddresses: []api.EndpointAddress{},
|
||||
}},
|
||||
}
|
||||
if err := checkEndpointReady(endpointReady); err != nil {
|
||||
t.Fatalf("Endpoint was ready with at least one Address, but checkEndpointReady returned an error")
|
||||
}
|
||||
}
|
||||
|
||||
type ServiceInterfaceMock struct {
|
||||
ServiceList *api.ServiceList
|
||||
}
|
||||
|
||||
func (s ServiceInterfaceMock) List(opts api.ListOptions) (*api.ServiceList, error) {
|
||||
serviceList := &api.ServiceList{
|
||||
Items: []api.Service{},
|
||||
}
|
||||
keyValArr := strings.Split(opts.LabelSelector.String(), "=")
|
||||
for _, service := range s.ServiceList.Items {
|
||||
if service.Spec.Selector[keyValArr[0]] == keyValArr[1] {
|
||||
serviceList.Items = append(serviceList.Items, service)
|
||||
}
|
||||
}
|
||||
return serviceList, nil
|
||||
}
|
||||
func (s ServiceInterfaceMock) Get(name string) (*api.Service, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (s ServiceInterfaceMock) Create(*api.Service) (*api.Service, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (s ServiceInterfaceMock) Update(*api.Service) (*api.Service, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (s ServiceInterfaceMock) UpdateStatus(*api.Service) (*api.Service, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (s ServiceInterfaceMock) Delete(name string) error {
|
||||
return nil
|
||||
}
|
||||
func (s ServiceInterfaceMock) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (s ServiceInterfaceMock) ProxyGet(scheme, name, port, path string, params map[string]string) restclient.ResponseWrapper {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestGetServiceListFromServicesByLabel(t *testing.T) {
|
||||
serviceList := &api.ServiceList{
|
||||
Items: []api.Service{
|
||||
{
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
serviceIface := ServiceInterfaceMock{
|
||||
ServiceList: serviceList,
|
||||
}
|
||||
if _, err := getServiceListFromServicesByLabel(serviceIface, "shouldError", "shouldError"); err == nil {
|
||||
t.Fatalf("Service had no label match but getServiceListFromServicesByLabel did not return an error")
|
||||
}
|
||||
|
||||
if _, err := getServiceListFromServicesByLabel(serviceIface, "foo", "bar"); err != nil {
|
||||
t.Fatalf("Endpoint was ready with at least one Address, but getServiceListFromServicesByLabel returned an error")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue