mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor files
This commit is contained in:
56
vendor/k8s.io/kubernetes/test/e2e_node/services/BUILD
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/test/e2e_node/services/BUILD
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"apiserver.go",
|
||||
"etcd.go",
|
||||
"internal_services.go",
|
||||
"kubelet.go",
|
||||
"logs.go",
|
||||
"namespace_controller.go",
|
||||
"server.go",
|
||||
"services.go",
|
||||
"util.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/test/e2e_node/services",
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app:go_default_library",
|
||||
"//cmd/kube-apiserver/app/options:go_default_library",
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/controller/namespace:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//test/e2e/framework:go_default_library",
|
||||
"//test/e2e_node/builder:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/etcdserver:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/etcdserver/api/v2http:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/pkg/transport:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/pkg/types:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/kardianos/osext:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
91
vendor/k8s.io/kubernetes/test/e2e_node/services/apiserver.go
generated
vendored
Normal file
91
vendor/k8s.io/kubernetes/test/e2e_node/services/apiserver.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
apiserver "k8s.io/kubernetes/cmd/kube-apiserver/app"
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||
)
|
||||
|
||||
const (
|
||||
clusterIPRange = "10.0.0.1/24"
|
||||
apiserverClientURL = "http://localhost:8080"
|
||||
apiserverHealthCheckURL = apiserverClientURL + "/healthz"
|
||||
)
|
||||
|
||||
// APIServer is a server which manages apiserver.
|
||||
type APIServer struct{}
|
||||
|
||||
// NewAPIServer creates an apiserver.
|
||||
func NewAPIServer() *APIServer {
|
||||
return &APIServer{}
|
||||
}
|
||||
|
||||
// Start starts the apiserver, returns when apiserver is ready.
|
||||
func (a *APIServer) Start() error {
|
||||
config := options.NewServerRunOptions()
|
||||
config.Etcd.StorageConfig.ServerList = []string{getEtcdClientURL()}
|
||||
// TODO: Current setup of etcd in e2e-node tests doesn't support etcd v3
|
||||
// protocol. We should migrate it to use the same infrastructure as all
|
||||
// other tests (pkg/storage/etcd/testing).
|
||||
config.Etcd.StorageConfig.Type = "etcd2"
|
||||
_, ipnet, err := net.ParseCIDR(clusterIPRange)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ServiceClusterIPRange = *ipnet
|
||||
config.AllowPrivileged = true
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
err := apiserver.Run(config, stopCh)
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("run apiserver error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = readinessCheck("apiserver", []string{apiserverHealthCheckURL}, errCh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the apiserver. Currently, there is no way to stop the apiserver.
|
||||
// The function is here only for completion.
|
||||
func (a *APIServer) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const apiserverName = "apiserver"
|
||||
|
||||
func (a *APIServer) Name() string {
|
||||
return apiserverName
|
||||
}
|
||||
|
||||
func getAPIServerClientURL() string {
|
||||
return apiserverClientURL
|
||||
}
|
||||
|
||||
func getAPIServerHealthCheckURL() string {
|
||||
return apiserverHealthCheckURL
|
||||
}
|
160
vendor/k8s.io/kubernetes/test/e2e_node/services/etcd.go
generated
vendored
Normal file
160
vendor/k8s.io/kubernetes/test/e2e_node/services/etcd.go
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/etcdserver"
|
||||
"github.com/coreos/etcd/etcdserver/api/v2http"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
"github.com/coreos/etcd/pkg/types"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// TODO: These tests should not be leveraging v2http
|
||||
// TODO(random-liu): Add service interface to manage services with the same behaviour.
|
||||
|
||||
// All following configurations are got from etcd source code.
|
||||
// TODO(random-liu): Use embed.NewConfig after etcd3 is supported.
|
||||
const (
|
||||
etcdName = "etcd"
|
||||
clientURLStr = "http://localhost:4001" // clientURL has listener created and handles etcd API traffic
|
||||
peerURLStr = "http://localhost:7001" // peerURL does't have listener created, it is used to pass Etcd validation
|
||||
snapCount = etcdserver.DefaultSnapCount
|
||||
maxSnapFiles = 5
|
||||
maxWALFiles = 5
|
||||
tickMs = 100
|
||||
electionTicks = 10
|
||||
etcdHealthCheckURL = clientURLStr + "/v2/keys/" // Trailing slash is required,
|
||||
)
|
||||
|
||||
// EtcdServer is a server which manages etcd.
|
||||
type EtcdServer struct {
|
||||
*etcdserver.EtcdServer
|
||||
config *etcdserver.ServerConfig
|
||||
clientListen net.Listener
|
||||
}
|
||||
|
||||
// NewEtcd creates a new default etcd server using 'dataDir' for persistence.
|
||||
func NewEtcd(dataDir string) *EtcdServer {
|
||||
clientURLs, err := types.NewURLs([]string{clientURLStr})
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to parse client url %q: %v", clientURLStr, err)
|
||||
}
|
||||
peerURLs, err := types.NewURLs([]string{peerURLStr})
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to parse peer url %q: %v", peerURLStr, err)
|
||||
}
|
||||
|
||||
config := &etcdserver.ServerConfig{
|
||||
Name: etcdName,
|
||||
ClientURLs: clientURLs,
|
||||
PeerURLs: peerURLs,
|
||||
DataDir: dataDir,
|
||||
InitialPeerURLsMap: map[string]types.URLs{etcdName: peerURLs},
|
||||
NewCluster: true,
|
||||
SnapCount: snapCount,
|
||||
MaxSnapFiles: maxSnapFiles,
|
||||
MaxWALFiles: maxWALFiles,
|
||||
TickMs: tickMs,
|
||||
ElectionTicks: electionTicks,
|
||||
}
|
||||
|
||||
return &EtcdServer{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the etcd server and listening for client connections
|
||||
func (e *EtcdServer) Start() error {
|
||||
var err error
|
||||
e.EtcdServer, err = etcdserver.NewServer(e.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// create client listener, there should be only one url
|
||||
e.clientListen, err = createListener(e.config.ClientURLs[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start etcd
|
||||
e.EtcdServer.Start()
|
||||
|
||||
// setup client listener
|
||||
ch := v2http.NewClientHandler(e.EtcdServer, e.config.ReqTimeout())
|
||||
errCh := make(chan error)
|
||||
go func(l net.Listener) {
|
||||
defer close(errCh)
|
||||
srv := &http.Server{
|
||||
Handler: ch,
|
||||
ReadTimeout: 5 * time.Minute,
|
||||
}
|
||||
// Serve always returns a non-nil error.
|
||||
errCh <- srv.Serve(l)
|
||||
}(e.clientListen)
|
||||
|
||||
err = readinessCheck("etcd", []string{etcdHealthCheckURL}, errCh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop closes all connections and stops the Etcd server
|
||||
func (e *EtcdServer) Stop() error {
|
||||
if e.EtcdServer != nil {
|
||||
e.EtcdServer.Stop()
|
||||
}
|
||||
if e.clientListen != nil {
|
||||
err := e.clientListen.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name returns the server's unique name
|
||||
func (e *EtcdServer) Name() string {
|
||||
return etcdName
|
||||
}
|
||||
|
||||
func createListener(url url.URL) (net.Listener, error) {
|
||||
l, err := net.Listen("tcp", url.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err = transport.NewKeepAliveListener(l, url.Scheme, &tls.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func getEtcdClientURL() string {
|
||||
return clientURLStr
|
||||
}
|
||||
|
||||
func getEtcdHealthCheckURL() string {
|
||||
return etcdHealthCheckURL
|
||||
}
|
143
vendor/k8s.io/kubernetes/test/e2e_node/services/internal_services.go
generated
vendored
Normal file
143
vendor/k8s.io/kubernetes/test/e2e_node/services/internal_services.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// e2eService manages e2e services in current process.
|
||||
type e2eServices struct {
|
||||
rmDirs []string
|
||||
// statically linked e2e services
|
||||
etcdServer *EtcdServer
|
||||
apiServer *APIServer
|
||||
nsController *NamespaceController
|
||||
}
|
||||
|
||||
func newE2EServices() *e2eServices {
|
||||
return &e2eServices{}
|
||||
}
|
||||
|
||||
// run starts all e2e services and wait for the termination signal. Once receives the
|
||||
// termination signal, it will stop the e2e services gracefully.
|
||||
func (es *e2eServices) run() error {
|
||||
defer es.stop()
|
||||
if err := es.start(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Wait until receiving a termination signal.
|
||||
waitForTerminationSignal()
|
||||
return nil
|
||||
}
|
||||
|
||||
// start starts the tests embedded services or returns an error.
|
||||
func (es *e2eServices) start() error {
|
||||
glog.Info("Starting e2e services...")
|
||||
err := es.startEtcd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = es.startApiServer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = es.startNamespaceController()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
glog.Info("E2E services started.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// stop stops the embedded e2e services.
|
||||
func (es *e2eServices) stop() {
|
||||
glog.Info("Stopping e2e services...")
|
||||
// TODO(random-liu): Use a loop to stop all services after introducing
|
||||
// service interface.
|
||||
glog.Info("Stopping namespace controller")
|
||||
if es.nsController != nil {
|
||||
if err := es.nsController.Stop(); err != nil {
|
||||
glog.Errorf("Failed to stop %q: %v", es.nsController.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
glog.Info("Stopping API server")
|
||||
if es.apiServer != nil {
|
||||
if err := es.apiServer.Stop(); err != nil {
|
||||
glog.Errorf("Failed to stop %q: %v", es.apiServer.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
glog.Info("Stopping etcd")
|
||||
if es.etcdServer != nil {
|
||||
if err := es.etcdServer.Stop(); err != nil {
|
||||
glog.Errorf("Failed to stop %q: %v", es.etcdServer.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, d := range es.rmDirs {
|
||||
glog.Infof("Deleting directory %v", d)
|
||||
err := os.RemoveAll(d)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to delete directory %s.\n%v", d, err)
|
||||
}
|
||||
}
|
||||
|
||||
glog.Info("E2E services stopped.")
|
||||
}
|
||||
|
||||
// startEtcd starts the embedded etcd instance or returns an error.
|
||||
func (es *e2eServices) startEtcd() error {
|
||||
glog.Info("Starting etcd")
|
||||
// Create data directory in current working space.
|
||||
dataDir, err := ioutil.TempDir(".", "etcd")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Mark the dataDir as directories to remove.
|
||||
es.rmDirs = append(es.rmDirs, dataDir)
|
||||
es.etcdServer = NewEtcd(dataDir)
|
||||
return es.etcdServer.Start()
|
||||
}
|
||||
|
||||
// startApiServer starts the embedded API server or returns an error.
|
||||
func (es *e2eServices) startApiServer() error {
|
||||
glog.Info("Starting API server")
|
||||
es.apiServer = NewAPIServer()
|
||||
return es.apiServer.Start()
|
||||
}
|
||||
|
||||
// startNamespaceController starts the embedded namespace controller or returns an error.
|
||||
func (es *e2eServices) startNamespaceController() error {
|
||||
glog.Info("Starting namespace controller")
|
||||
es.nsController = NewNamespaceController(framework.TestContext.Host)
|
||||
return es.nsController.Start()
|
||||
}
|
||||
|
||||
// getServicesHealthCheckURLs returns the health check urls for the internal services.
|
||||
func getServicesHealthCheckURLs() []string {
|
||||
return []string{
|
||||
getEtcdHealthCheckURL(),
|
||||
getAPIServerHealthCheckURL(),
|
||||
}
|
||||
}
|
331
vendor/k8s.io/kubernetes/test/e2e_node/services/kubelet.go
generated
vendored
Normal file
331
vendor/k8s.io/kubernetes/test/e2e_node/services/kubelet.go
generated
vendored
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
"k8s.io/kubernetes/test/e2e_node/builder"
|
||||
)
|
||||
|
||||
// TODO(random-liu): Replace this with standard kubelet launcher.
|
||||
|
||||
// args is the type used to accumulate args from the flags with the same name.
|
||||
type args []string
|
||||
|
||||
// String function of flag.Value
|
||||
func (a *args) String() string {
|
||||
return fmt.Sprint(*a)
|
||||
}
|
||||
|
||||
// Set function of flag.Value
|
||||
func (a *args) Set(value string) error {
|
||||
// Someone else is calling flag.Parse after the flags are parsed in the
|
||||
// test framework. Use this to avoid the flag being parsed twice.
|
||||
// TODO(random-liu): Figure out who is parsing the flags.
|
||||
if flag.Parsed() {
|
||||
return nil
|
||||
}
|
||||
// Note that we assume all white space in flag string is separating fields
|
||||
na := strings.Fields(value)
|
||||
*a = append(*a, na...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// kubeletArgs is the override kubelet args specified by the test runner.
|
||||
var kubeletArgs args
|
||||
|
||||
func init() {
|
||||
flag.Var(&kubeletArgs, "kubelet-flags", "Kubelet flags passed to kubelet, this will override default kubelet flags in the test. Flags specified in multiple kubelet-flags will be concatenate.")
|
||||
}
|
||||
|
||||
// RunKubelet starts kubelet and waits for termination signal. Once receives the
|
||||
// termination signal, it will stop the kubelet gracefully.
|
||||
func RunKubelet() {
|
||||
var err error
|
||||
// Enable monitorParent to make sure kubelet will receive termination signal
|
||||
// when test process exits.
|
||||
e := NewE2EServices(true /* monitorParent */)
|
||||
defer e.Stop()
|
||||
e.kubelet, err = e.startKubelet()
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to start kubelet: %v", err)
|
||||
}
|
||||
// Wait until receiving a termination signal.
|
||||
waitForTerminationSignal()
|
||||
}
|
||||
|
||||
const (
|
||||
// Ports of different e2e services.
|
||||
kubeletPort = "10250"
|
||||
kubeletReadOnlyPort = "10255"
|
||||
KubeletRootDirectory = "/var/lib/kubelet"
|
||||
// Health check url of kubelet
|
||||
kubeletHealthCheckURL = "http://127.0.0.1:" + kubeletReadOnlyPort + "/healthz"
|
||||
)
|
||||
|
||||
// startKubelet starts the Kubelet in a separate process or returns an error
|
||||
// if the Kubelet fails to start.
|
||||
func (e *E2EServices) startKubelet() (*server, error) {
|
||||
glog.Info("Starting kubelet")
|
||||
|
||||
// set feature gates so we can check which features are enabled and pass the appropriate flags
|
||||
utilfeature.DefaultFeatureGate.Set(framework.TestContext.FeatureGates)
|
||||
|
||||
// Build kubeconfig
|
||||
kubeconfigPath, err := createKubeconfigCWD()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create pod manifest path
|
||||
manifestPath, err := createPodManifestDirectory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.rmDirs = append(e.rmDirs, manifestPath)
|
||||
err = createRootDirectory(KubeletRootDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var killCommand, restartCommand *exec.Cmd
|
||||
var isSystemd bool
|
||||
// Apply default kubelet flags.
|
||||
cmdArgs := []string{}
|
||||
if systemdRun, err := exec.LookPath("systemd-run"); err == nil {
|
||||
// On systemd services, detection of a service / unit works reliably while
|
||||
// detection of a process started from an ssh session does not work.
|
||||
// Since kubelet will typically be run as a service it also makes more
|
||||
// sense to test it that way
|
||||
isSystemd = true
|
||||
unitName := fmt.Sprintf("kubelet-%d.service", rand.Int31())
|
||||
cmdArgs = append(cmdArgs, systemdRun, "--unit="+unitName, "--slice=runtime.slice", "--remain-after-exit", builder.GetKubeletServerBin())
|
||||
killCommand = exec.Command("systemctl", "kill", unitName)
|
||||
restartCommand = exec.Command("systemctl", "restart", unitName)
|
||||
e.logs["kubelet.log"] = LogFileData{
|
||||
Name: "kubelet.log",
|
||||
JournalctlCommand: []string{"-u", unitName},
|
||||
}
|
||||
cmdArgs = append(cmdArgs,
|
||||
"--kubelet-cgroups=/kubelet.slice",
|
||||
"--cgroup-root=/",
|
||||
)
|
||||
} else {
|
||||
cmdArgs = append(cmdArgs, builder.GetKubeletServerBin())
|
||||
cmdArgs = append(cmdArgs,
|
||||
// TODO(random-liu): Get rid of this docker specific thing.
|
||||
"--runtime-cgroups=/docker-daemon",
|
||||
"--kubelet-cgroups=/kubelet",
|
||||
"--cgroup-root=/",
|
||||
"--system-cgroups=/system",
|
||||
)
|
||||
}
|
||||
cmdArgs = append(cmdArgs,
|
||||
"--kubeconfig", kubeconfigPath,
|
||||
"--address", "0.0.0.0",
|
||||
"--port", kubeletPort,
|
||||
"--read-only-port", kubeletReadOnlyPort,
|
||||
"--root-dir", KubeletRootDirectory,
|
||||
"--volume-stats-agg-period", "10s", // Aggregate volumes frequently so tests don't need to wait as long
|
||||
"--allow-privileged", "true",
|
||||
"--serialize-image-pulls", "false",
|
||||
"--pod-manifest-path", manifestPath,
|
||||
"--file-check-frequency", "10s", // Check file frequently so tests won't wait too long
|
||||
"--docker-disable-shared-pid=false",
|
||||
// Assign a fixed CIDR to the node because there is no node controller.
|
||||
//
|
||||
// Note: this MUST be in sync with with the IP in
|
||||
// - cluster/gce/config-test.sh and
|
||||
// - test/e2e_node/conformance/run_test.sh.
|
||||
"--pod-cidr", "10.100.0.0/24",
|
||||
"--eviction-pressure-transition-period", "30s",
|
||||
// Apply test framework feature gates by default. This could also be overridden
|
||||
// by kubelet-flags.
|
||||
"--feature-gates", framework.TestContext.FeatureGates,
|
||||
"--eviction-hard", "memory.available<250Mi,nodefs.available<10%,nodefs.inodesFree<5%", // The hard eviction thresholds.
|
||||
"--eviction-minimum-reclaim", "nodefs.available=5%,nodefs.inodesFree=5%", // The minimum reclaimed resources after eviction.
|
||||
"--v", LOG_VERBOSITY_LEVEL, "--logtostderr",
|
||||
)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
|
||||
// Enable dynamic config if the feature gate is enabled
|
||||
dynamicConfigDir, err := getDynamicConfigDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmdArgs = append(cmdArgs, "--dynamic-config-dir", dynamicConfigDir)
|
||||
}
|
||||
|
||||
// Enable kubenet by default.
|
||||
cniBinDir, err := getCNIBinDirectory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cniConfDir, err := getCNIConfDirectory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmdArgs = append(cmdArgs,
|
||||
"--network-plugin=kubenet",
|
||||
"--cni-bin-dir", cniBinDir,
|
||||
"--cni-conf-dir", cniConfDir)
|
||||
|
||||
// Keep hostname override for convenience.
|
||||
if framework.TestContext.NodeName != "" { // If node name is specified, set hostname override.
|
||||
cmdArgs = append(cmdArgs, "--hostname-override", framework.TestContext.NodeName)
|
||||
}
|
||||
|
||||
// Override the default kubelet flags.
|
||||
cmdArgs = append(cmdArgs, kubeletArgs...)
|
||||
|
||||
// Adjust the args if we are running kubelet with systemd.
|
||||
if isSystemd {
|
||||
adjustArgsForSystemd(cmdArgs)
|
||||
}
|
||||
|
||||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||
server := newServer(
|
||||
"kubelet",
|
||||
cmd,
|
||||
killCommand,
|
||||
restartCommand,
|
||||
[]string{kubeletHealthCheckURL},
|
||||
"kubelet.log",
|
||||
e.monitorParent,
|
||||
true /* restartOnExit */)
|
||||
return server, server.start()
|
||||
}
|
||||
|
||||
// createPodManifestDirectory creates pod manifest directory.
|
||||
func createPodManifestDirectory() (string, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get current working directory: %v", err)
|
||||
}
|
||||
path, err := ioutil.TempDir(cwd, "pod-manifest")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create static pod manifest directory: %v", err)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// createKubeconfig creates a kubeconfig file at the fully qualified `path`. The parent dirs must exist.
|
||||
func createKubeconfig(path string) error {
|
||||
kubeconfig := []byte(`apiVersion: v1
|
||||
kind: Config
|
||||
users:
|
||||
- name: kubelet
|
||||
clusters:
|
||||
- cluster:
|
||||
server: ` + getAPIServerClientURL() + `
|
||||
insecure-skip-tls-verify: true
|
||||
name: local
|
||||
contexts:
|
||||
- context:
|
||||
cluster: local
|
||||
user: kubelet
|
||||
name: local-context
|
||||
current-context: local-context`)
|
||||
|
||||
if err := ioutil.WriteFile(path, kubeconfig, 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createRootDirectory(path string) error {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return os.MkdirAll(path, os.FileMode(0755))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func kubeconfigCWDPath() (string, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get current working directory: %v", err)
|
||||
}
|
||||
return filepath.Join(cwd, "kubeconfig"), nil
|
||||
}
|
||||
|
||||
// like createKubeconfig, but creates kubeconfig at current-working-directory/kubeconfig
|
||||
// returns a fully-qualified path to the kubeconfig file
|
||||
func createKubeconfigCWD() (string, error) {
|
||||
kubeconfigPath, err := kubeconfigCWDPath()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = createKubeconfig(kubeconfigPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return kubeconfigPath, nil
|
||||
}
|
||||
|
||||
// getCNIBinDirectory returns CNI directory.
|
||||
func getCNIBinDirectory() (string, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(cwd, "cni", "bin"), nil
|
||||
}
|
||||
|
||||
// getCNIConfDirectory returns CNI Configuration directory.
|
||||
func getCNIConfDirectory() (string, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(cwd, "cni", "net.d"), nil
|
||||
}
|
||||
|
||||
// getDynamicConfigDir returns the directory for dynamic Kubelet configuration
|
||||
func getDynamicConfigDir() (string, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(cwd, "dynamic-kubelet-config"), nil
|
||||
}
|
||||
|
||||
// adjustArgsForSystemd escape special characters in kubelet arguments for systemd. Systemd
|
||||
// may try to do auto expansion without escaping.
|
||||
func adjustArgsForSystemd(args []string) {
|
||||
for i := range args {
|
||||
args[i] = strings.Replace(args[i], "%", "%%", -1)
|
||||
args[i] = strings.Replace(args[i], "$", "$$", -1)
|
||||
}
|
||||
}
|
100
vendor/k8s.io/kubernetes/test/e2e_node/services/logs.go
generated
vendored
Normal file
100
vendor/k8s.io/kubernetes/test/e2e_node/services/logs.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// LogFileData holds data about logfiles to fetch with a journalctl command or
|
||||
// file from a node's file system.
|
||||
type LogFileData struct {
|
||||
// Name of the log file.
|
||||
Name string `json:"name"`
|
||||
// Files are possible absolute paths of the log file.
|
||||
Files []string `json:"files"`
|
||||
// JournalctlCommand is the journalctl command to get log.
|
||||
JournalctlCommand []string `json:"journalctl"`
|
||||
}
|
||||
|
||||
// logFiles are the type used to collect all log files. The key is the expected
|
||||
// name of the log file after collected.
|
||||
type logFiles map[string]LogFileData
|
||||
|
||||
// String function of flag.Value
|
||||
func (l *logFiles) String() string {
|
||||
return fmt.Sprint(*l)
|
||||
}
|
||||
|
||||
// Set function of flag.Value
|
||||
func (l *logFiles) Set(value string) error {
|
||||
// Someone else is calling flag.Parse after the flags are parsed in the
|
||||
// test framework. Use this to avoid the flag being parsed twice.
|
||||
// TODO(random-liu): Figure out who is parsing the flags.
|
||||
if flag.Parsed() {
|
||||
return nil
|
||||
}
|
||||
var log LogFileData
|
||||
if err := json.Unmarshal([]byte(value), &log); err != nil {
|
||||
return err
|
||||
}
|
||||
// Note that we assume all white space in flag string is separating fields
|
||||
logs := *l
|
||||
logs[log.Name] = log
|
||||
return nil
|
||||
}
|
||||
|
||||
// extraLogs is the extra logs specified by the test runner.
|
||||
var extraLogs = make(logFiles)
|
||||
|
||||
func init() {
|
||||
flag.Var(&extraLogs, "extra-log", "Extra log to collect after test in the json format of LogFile.")
|
||||
}
|
||||
|
||||
// requiredLogs is the required logs to collect after the test.
|
||||
var requiredLogs = []LogFileData{
|
||||
{
|
||||
Name: "kern.log",
|
||||
Files: []string{"/var/log/kern.log"},
|
||||
JournalctlCommand: []string{"-k"},
|
||||
},
|
||||
{
|
||||
Name: "cloud-init.log",
|
||||
Files: []string{"/var/log/cloud-init.log"},
|
||||
JournalctlCommand: []string{"-u", "cloud*"},
|
||||
},
|
||||
// TODO(random-liu): Make docker.log non-required.
|
||||
{
|
||||
Name: "docker.log",
|
||||
Files: []string{"/var/log/docker.log", "/var/log/upstart/docker.log"},
|
||||
JournalctlCommand: []string{"-u", "docker"},
|
||||
},
|
||||
}
|
||||
|
||||
// getLogFiles get all logs to collect after the test.
|
||||
func getLogFiles() logFiles {
|
||||
logs := make(logFiles)
|
||||
for _, l := range requiredLogs {
|
||||
logs[l.Name] = l
|
||||
}
|
||||
for _, l := range extraLogs {
|
||||
logs[l.Name] = l
|
||||
}
|
||||
return logs
|
||||
}
|
83
vendor/k8s.io/kubernetes/test/e2e_node/services/namespace_controller.go
generated
vendored
Normal file
83
vendor/k8s.io/kubernetes/test/e2e_node/services/namespace_controller.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
namespacecontroller "k8s.io/kubernetes/pkg/controller/namespace"
|
||||
)
|
||||
|
||||
const (
|
||||
// ncName is the name of namespace controller
|
||||
ncName = "namespace-controller"
|
||||
// ncResyncPeriod is resync period of the namespace controller
|
||||
ncResyncPeriod = 5 * time.Minute
|
||||
// ncConcurrency is concurrency of the namespace controller
|
||||
ncConcurrency = 2
|
||||
)
|
||||
|
||||
// NamespaceController is a server which manages namespace controller.
|
||||
type NamespaceController struct {
|
||||
host string
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// NewNamespaceController creates a new namespace controller.
|
||||
func NewNamespaceController(host string) *NamespaceController {
|
||||
return &NamespaceController{host: host, stopCh: make(chan struct{})}
|
||||
}
|
||||
|
||||
// Start starts the namespace controller.
|
||||
func (n *NamespaceController) Start() error {
|
||||
// Use the default QPS
|
||||
config := restclient.AddUserAgent(&restclient.Config{Host: n.host}, ncName)
|
||||
client, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientPool := dynamic.NewClientPool(config, legacyscheme.Registry.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
discoverResourcesFn := client.Discovery().ServerPreferredNamespacedResources
|
||||
informerFactory := informers.NewSharedInformerFactory(client, ncResyncPeriod)
|
||||
nc := namespacecontroller.NewNamespaceController(
|
||||
client,
|
||||
clientPool,
|
||||
discoverResourcesFn,
|
||||
informerFactory.Core().V1().Namespaces(),
|
||||
ncResyncPeriod, v1.FinalizerKubernetes,
|
||||
)
|
||||
informerFactory.Start(n.stopCh)
|
||||
go nc.Run(ncConcurrency, n.stopCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the namespace controller.
|
||||
func (n *NamespaceController) Stop() error {
|
||||
close(n.stopCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name returns the name of namespace controller.
|
||||
func (n *NamespaceController) Name() string {
|
||||
return ncName
|
||||
}
|
361
vendor/k8s.io/kubernetes/test/e2e_node/services/server.go
generated
vendored
Normal file
361
vendor/k8s.io/kubernetes/test/e2e_node/services/server.go
generated
vendored
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
)
|
||||
|
||||
var serverStartTimeout = flag.Duration("server-start-timeout", time.Second*120, "Time to wait for each server to become healthy.")
|
||||
|
||||
// A server manages a separate server process started and killed with
|
||||
// commands.
|
||||
type server struct {
|
||||
// name is the name of the server, it is only used for logging.
|
||||
name string
|
||||
// startCommand is the command used to start the server
|
||||
startCommand *exec.Cmd
|
||||
// killCommand is the command used to stop the server. It is not required. If it
|
||||
// is not specified, `kill` will be used to stop the server.
|
||||
killCommand *exec.Cmd
|
||||
// restartCommand is the command used to restart the server. If provided, it will be used
|
||||
// instead of startCommand when restarting the server.
|
||||
restartCommand *exec.Cmd
|
||||
// healthCheckUrls is the urls used to check whether the server is ready.
|
||||
healthCheckUrls []string
|
||||
// outFilename is the name of the log file. The stdout and stderr of the server
|
||||
// will be redirected to this file.
|
||||
outFilename string
|
||||
// monitorParent determines whether the server should watch its parent process and exit
|
||||
// if its parent is gone.
|
||||
monitorParent bool
|
||||
// restartOnExit determines whether a restart loop is launched with the server
|
||||
restartOnExit bool
|
||||
// Writing to this channel, if it is not nil, stops the restart loop.
|
||||
// When tearing down a server, you should check for this channel and write to it if it exists.
|
||||
stopRestartingCh chan<- bool
|
||||
// Read from this to confirm that the restart loop has stopped.
|
||||
ackStopRestartingCh <-chan bool
|
||||
}
|
||||
|
||||
// newServer returns a new server with the given name, commands, health check
|
||||
// URLs, etc.
|
||||
func newServer(name string, start, kill, restart *exec.Cmd, urls []string, outputFileName string, monitorParent, restartOnExit bool) *server {
|
||||
return &server{
|
||||
name: name,
|
||||
startCommand: start,
|
||||
killCommand: kill,
|
||||
restartCommand: restart,
|
||||
healthCheckUrls: urls,
|
||||
outFilename: outputFileName,
|
||||
monitorParent: monitorParent,
|
||||
restartOnExit: restartOnExit,
|
||||
}
|
||||
}
|
||||
|
||||
// commandToString format command to string.
|
||||
func commandToString(c *exec.Cmd) string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(append([]string{c.Path}, c.Args[1:]...), " ")
|
||||
}
|
||||
|
||||
func (s *server) String() string {
|
||||
return fmt.Sprintf("server %q start-command: `%s`, kill-command: `%s`, restart-command: `%s`, health-check: %v, output-file: %q", s.name,
|
||||
commandToString(s.startCommand), commandToString(s.killCommand), commandToString(s.restartCommand), s.healthCheckUrls, s.outFilename)
|
||||
}
|
||||
|
||||
// readinessCheck checks whether services are ready via the supplied health
|
||||
// check URLs. Once there is an error in errCh, the function will stop waiting
|
||||
// and return the error.
|
||||
// TODO(random-liu): Move this to util
|
||||
func readinessCheck(name string, urls []string, errCh <-chan error) error {
|
||||
glog.Infof("Running readiness check for service %q", name)
|
||||
endTime := time.Now().Add(*serverStartTimeout)
|
||||
blockCh := make(chan error)
|
||||
defer close(blockCh)
|
||||
for endTime.After(time.Now()) {
|
||||
select {
|
||||
// We *always* want to run the health check if there is no error on the channel.
|
||||
// With systemd, reads from errCh report nil because cmd.Run() waits
|
||||
// on systemd-run, rather than the service process. systemd-run quickly
|
||||
// exits with status 0, causing the channel to be closed with no error. In
|
||||
// this case, you want to wait for the health check to complete, rather
|
||||
// than returning from readinessCheck as soon as the channel is closed.
|
||||
case err, ok := <-errCh:
|
||||
if ok { // The channel is not closed, this is a real error
|
||||
if err != nil { // If there is an error, return it
|
||||
return err
|
||||
}
|
||||
// If not, keep checking readiness.
|
||||
} else { // The channel is closed, this is only a zero value.
|
||||
// Replace the errCh with blockCh to avoid busy loop,
|
||||
// and keep checking readiness.
|
||||
errCh = blockCh
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
ready := true
|
||||
for _, url := range urls {
|
||||
resp, err := http.Head(url)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
ready = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if ready {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("e2e service %q readiness check timeout %v", name, *serverStartTimeout)
|
||||
}
|
||||
|
||||
// start starts the server by running its commands, monitors it with a health
|
||||
// check, and ensures that it is restarted if applicable.
|
||||
//
|
||||
// Note: restartOnExit == true requires len(s.healthCheckUrls) > 0 to work properly.
|
||||
func (s *server) start() error {
|
||||
glog.Infof("Starting server %q with command %q", s.name, commandToString(s.startCommand))
|
||||
errCh := make(chan error)
|
||||
|
||||
// Set up restart channels if the server is configured for restart on exit.
|
||||
var stopRestartingCh, ackStopRestartingCh chan bool
|
||||
if s.restartOnExit {
|
||||
if len(s.healthCheckUrls) == 0 {
|
||||
return fmt.Errorf("Tried to start %s which has s.restartOnExit == true, but no health check urls provided.", s)
|
||||
}
|
||||
|
||||
stopRestartingCh = make(chan bool)
|
||||
ackStopRestartingCh = make(chan bool)
|
||||
|
||||
s.stopRestartingCh = stopRestartingCh
|
||||
s.ackStopRestartingCh = ackStopRestartingCh
|
||||
}
|
||||
|
||||
// This goroutine actually runs the start command for the server.
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
|
||||
// Create the output filename
|
||||
outPath := path.Join(framework.TestContext.ReportDir, s.outFilename)
|
||||
outfile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("failed to create file %q for `%s` %v.", outPath, s, err)
|
||||
return
|
||||
} else {
|
||||
glog.Infof("Output file for server %q: %v", s.name, outfile.Name())
|
||||
}
|
||||
defer outfile.Close()
|
||||
defer outfile.Sync()
|
||||
|
||||
// Set the command to write the output file
|
||||
s.startCommand.Stdout = outfile
|
||||
s.startCommand.Stderr = outfile
|
||||
|
||||
// If monitorParent is set, set Pdeathsig when starting the server.
|
||||
if s.monitorParent {
|
||||
// Death of this test process should kill the server as well.
|
||||
attrs := &syscall.SysProcAttr{}
|
||||
// Hack to set linux-only field without build tags.
|
||||
deathSigField := reflect.ValueOf(attrs).Elem().FieldByName("Pdeathsig")
|
||||
if deathSigField.IsValid() {
|
||||
deathSigField.Set(reflect.ValueOf(syscall.SIGTERM))
|
||||
} else {
|
||||
errCh <- fmt.Errorf("failed to set Pdeathsig field (non-linux build)")
|
||||
return
|
||||
}
|
||||
s.startCommand.SysProcAttr = attrs
|
||||
}
|
||||
|
||||
// Start the command
|
||||
err = s.startCommand.Start()
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("failed to run %s: %v", s, err)
|
||||
return
|
||||
}
|
||||
if !s.restartOnExit {
|
||||
glog.Infof("Waiting for server %q start command to complete", s.name)
|
||||
// If we aren't planning on restarting, ok to Wait() here to release resources.
|
||||
// Otherwise, we Wait() in the restart loop.
|
||||
err = s.startCommand.Wait()
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("failed to run start command for server %q: %v", s.name, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
usedStartCmd := true
|
||||
for {
|
||||
glog.Infof("Running health check for service %q", s.name)
|
||||
// Wait for an initial health check to pass, so that we are sure the server started.
|
||||
err := readinessCheck(s.name, s.healthCheckUrls, nil)
|
||||
if err != nil {
|
||||
if usedStartCmd {
|
||||
glog.Infof("Waiting for server %q start command to complete after initial health check failed", s.name)
|
||||
s.startCommand.Wait() // Release resources if necessary.
|
||||
}
|
||||
// This should not happen, immediately stop the e2eService process.
|
||||
glog.Fatalf("Restart loop readinessCheck failed for %s", s)
|
||||
} else {
|
||||
glog.Infof("Initial health check passed for service %q", s.name)
|
||||
}
|
||||
|
||||
// Initial health check passed, wait until a health check fails again.
|
||||
stillAlive:
|
||||
for {
|
||||
select {
|
||||
case <-stopRestartingCh:
|
||||
ackStopRestartingCh <- true
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
for _, url := range s.healthCheckUrls {
|
||||
resp, err := http.Head(url)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
break stillAlive
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if usedStartCmd {
|
||||
s.startCommand.Wait() // Release resources from last cmd
|
||||
usedStartCmd = false
|
||||
}
|
||||
if s.restartCommand != nil {
|
||||
// Always make a fresh copy of restartCommand before
|
||||
// running, we may have to restart multiple times
|
||||
s.restartCommand = &exec.Cmd{
|
||||
Path: s.restartCommand.Path,
|
||||
Args: s.restartCommand.Args,
|
||||
Env: s.restartCommand.Env,
|
||||
Dir: s.restartCommand.Dir,
|
||||
Stdin: s.restartCommand.Stdin,
|
||||
Stdout: s.restartCommand.Stdout,
|
||||
Stderr: s.restartCommand.Stderr,
|
||||
ExtraFiles: s.restartCommand.ExtraFiles,
|
||||
SysProcAttr: s.restartCommand.SysProcAttr,
|
||||
}
|
||||
// Run and wait for exit. This command is assumed to have
|
||||
// short duration, e.g. systemctl restart
|
||||
glog.Infof("Restarting server %q with restart command", s.name)
|
||||
err = s.restartCommand.Run()
|
||||
if err != nil {
|
||||
// This should not happen, immediately stop the e2eService process.
|
||||
glog.Fatalf("Restarting server %s with restartCommand failed. Error: %v.", s, err)
|
||||
}
|
||||
} else {
|
||||
s.startCommand = &exec.Cmd{
|
||||
Path: s.startCommand.Path,
|
||||
Args: s.startCommand.Args,
|
||||
Env: s.startCommand.Env,
|
||||
Dir: s.startCommand.Dir,
|
||||
Stdin: s.startCommand.Stdin,
|
||||
Stdout: s.startCommand.Stdout,
|
||||
Stderr: s.startCommand.Stderr,
|
||||
ExtraFiles: s.startCommand.ExtraFiles,
|
||||
SysProcAttr: s.startCommand.SysProcAttr,
|
||||
}
|
||||
glog.Infof("Restarting server %q with start command", s.name)
|
||||
err = s.startCommand.Start()
|
||||
usedStartCmd = true
|
||||
if err != nil {
|
||||
// This should not happen, immediately stop the e2eService process.
|
||||
glog.Fatalf("Restarting server %s with startCommand failed. Error: %v.", s, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return readinessCheck(s.name, s.healthCheckUrls, errCh)
|
||||
}
|
||||
|
||||
// kill runs the server's kill command.
|
||||
func (s *server) kill() error {
|
||||
glog.Infof("Kill server %q", s.name)
|
||||
name := s.name
|
||||
cmd := s.startCommand
|
||||
|
||||
// If s has a restart loop, turn it off.
|
||||
if s.restartOnExit {
|
||||
s.stopRestartingCh <- true
|
||||
<-s.ackStopRestartingCh
|
||||
}
|
||||
|
||||
if s.killCommand != nil {
|
||||
return s.killCommand.Run()
|
||||
}
|
||||
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("could not kill %q because both `killCommand` and `startCommand` are nil", name)
|
||||
}
|
||||
|
||||
if cmd.Process == nil {
|
||||
glog.V(2).Infof("%q not running", name)
|
||||
return nil
|
||||
}
|
||||
pid := cmd.Process.Pid
|
||||
if pid <= 1 {
|
||||
return fmt.Errorf("invalid PID %d for %q", pid, name)
|
||||
}
|
||||
|
||||
// Attempt to shut down the process in a friendly manner before forcing it.
|
||||
waitChan := make(chan error)
|
||||
go func() {
|
||||
_, err := cmd.Process.Wait()
|
||||
waitChan <- err
|
||||
close(waitChan)
|
||||
}()
|
||||
|
||||
const timeout = 10 * time.Second
|
||||
for _, signal := range []string{"-TERM", "-KILL"} {
|
||||
glog.V(2).Infof("Killing process %d (%s) with %s", pid, name, signal)
|
||||
cmd := exec.Command("kill", signal, strconv.Itoa(pid))
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
glog.Errorf("Error signaling process %d (%s) with %s: %v", pid, name, signal, err)
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-waitChan:
|
||||
if err != nil {
|
||||
return fmt.Errorf("error stopping %q: %v", name, err)
|
||||
}
|
||||
// Success!
|
||||
return nil
|
||||
case <-time.After(timeout):
|
||||
// Continue.
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to stop %q", name)
|
||||
}
|
195
vendor/k8s.io/kubernetes/test/e2e_node/services/services.go
generated
vendored
Normal file
195
vendor/k8s.io/kubernetes/test/e2e_node/services/services.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/kardianos/osext"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
)
|
||||
|
||||
// E2EServices starts and stops e2e services in a separate process. The test
|
||||
// uses it to start and stop all e2e services.
|
||||
type E2EServices struct {
|
||||
// monitorParent determines whether the sub-processes should watch and die with the current
|
||||
// process.
|
||||
rmDirs []string
|
||||
monitorParent bool
|
||||
services *server
|
||||
kubelet *server
|
||||
logs logFiles
|
||||
}
|
||||
|
||||
// NewE2EServices returns a new E2EServices instance.
|
||||
func NewE2EServices(monitorParent bool) *E2EServices {
|
||||
return &E2EServices{
|
||||
monitorParent: monitorParent,
|
||||
// Special log files that need to be collected for additional debugging.
|
||||
logs: getLogFiles(),
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the e2e services in another process by calling back into the
|
||||
// test binary. Returns when all e2e services are ready or an error.
|
||||
//
|
||||
// We want to statically link e2e services into the test binary, but we don't
|
||||
// want their glog output to pollute the test result. So we run the binary in
|
||||
// run-services-mode to start e2e services in another process.
|
||||
// The function starts 2 processes:
|
||||
// * internal e2e services: services which statically linked in the test binary - apiserver, etcd and
|
||||
// namespace controller.
|
||||
// * kubelet: kubelet binary is outside. (We plan to move main kubelet start logic out when we have
|
||||
// standard kubelet launcher)
|
||||
func (e *E2EServices) Start() error {
|
||||
var err error
|
||||
if !framework.TestContext.NodeConformance {
|
||||
// Start kubelet
|
||||
e.kubelet, err = e.startKubelet()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start kubelet: %v", err)
|
||||
}
|
||||
}
|
||||
e.services, err = e.startInternalServices()
|
||||
return err
|
||||
}
|
||||
|
||||
// Stop stops the e2e services.
|
||||
func (e *E2EServices) Stop() {
|
||||
defer func() {
|
||||
if !framework.TestContext.NodeConformance {
|
||||
// Collect log files.
|
||||
e.collectLogFiles()
|
||||
}
|
||||
}()
|
||||
if e.services != nil {
|
||||
if err := e.services.kill(); err != nil {
|
||||
glog.Errorf("Failed to stop services: %v", err)
|
||||
}
|
||||
}
|
||||
if e.kubelet != nil {
|
||||
if err := e.kubelet.kill(); err != nil {
|
||||
glog.Errorf("Failed to stop kubelet: %v", err)
|
||||
}
|
||||
}
|
||||
if e.rmDirs != nil {
|
||||
for _, d := range e.rmDirs {
|
||||
err := os.RemoveAll(d)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to delete directory %s: %v", d, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RunE2EServices actually start the e2e services. This function is used to
|
||||
// start e2e services in current process. This is only used in run-services-mode.
|
||||
func RunE2EServices() {
|
||||
// Populate global DefaultFeatureGate with value from TestContext.FeatureGates.
|
||||
// This way, statically-linked components see the same feature gate config as the test context.
|
||||
utilfeature.DefaultFeatureGate.Set(framework.TestContext.FeatureGates)
|
||||
e := newE2EServices()
|
||||
if err := e.run(); err != nil {
|
||||
glog.Fatalf("Failed to run e2e services: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// services.log is the combined log of all services
|
||||
servicesLogFile = "services.log"
|
||||
// LOG_VERBOSITY_LEVEL is consistent with the level used in a cluster e2e test.
|
||||
LOG_VERBOSITY_LEVEL = "4"
|
||||
)
|
||||
|
||||
// startInternalServices starts the internal services in a separate process.
|
||||
func (e *E2EServices) startInternalServices() (*server, error) {
|
||||
testBin, err := osext.Executable()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get current binary: %v", err)
|
||||
}
|
||||
// Pass all flags into the child process, so that it will see the same flag set.
|
||||
startCmd := exec.Command(testBin, append([]string{"--run-services-mode"}, os.Args[1:]...)...)
|
||||
server := newServer("services", startCmd, nil, nil, getServicesHealthCheckURLs(), servicesLogFile, e.monitorParent, false)
|
||||
return server, server.start()
|
||||
}
|
||||
|
||||
// collectLogFiles collects logs of interest either via journalctl or by creating sym
|
||||
// links. Since we scp files from the remote directory, symlinks will be
|
||||
// treated as normal files and file contents will be copied over.
|
||||
func (e *E2EServices) collectLogFiles() {
|
||||
// Nothing to do if report dir is not specified.
|
||||
if framework.TestContext.ReportDir == "" {
|
||||
return
|
||||
}
|
||||
glog.Info("Fetching log files...")
|
||||
journaldFound := isJournaldAvailable()
|
||||
for targetFileName, log := range e.logs {
|
||||
targetLink := path.Join(framework.TestContext.ReportDir, targetFileName)
|
||||
if journaldFound {
|
||||
// Skip log files that do not have an equivalent in journald-based machines.
|
||||
if len(log.JournalctlCommand) == 0 {
|
||||
continue
|
||||
}
|
||||
glog.Infof("Get log file %q with journalctl command %v.", targetFileName, log.JournalctlCommand)
|
||||
out, err := exec.Command("journalctl", log.JournalctlCommand...).CombinedOutput()
|
||||
if err != nil {
|
||||
glog.Errorf("failed to get %q from journald: %v, %v", targetFileName, string(out), err)
|
||||
} else {
|
||||
if err = ioutil.WriteFile(targetLink, out, 0644); err != nil {
|
||||
glog.Errorf("failed to write logs to %q: %v", targetLink, err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, file := range log.Files {
|
||||
if _, err := os.Stat(file); err != nil {
|
||||
// Expected file not found on this distro.
|
||||
continue
|
||||
}
|
||||
if err := copyLogFile(file, targetLink); err != nil {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isJournaldAvailable returns whether the system executing the tests uses
|
||||
// journald.
|
||||
func isJournaldAvailable() bool {
|
||||
_, err := exec.LookPath("journalctl")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func copyLogFile(src, target string) error {
|
||||
// If not a journald based distro, then just symlink files.
|
||||
if out, err := exec.Command("cp", src, target).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to copy %q to %q: %v, %v", src, target, out, err)
|
||||
}
|
||||
if out, err := exec.Command("chmod", "a+r", target).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to make log file %q world readable: %v, %v", target, out, err)
|
||||
}
|
||||
return nil
|
||||
}
|
34
vendor/k8s.io/kubernetes/test/e2e_node/services/util.go
generated
vendored
Normal file
34
vendor/k8s.io/kubernetes/test/e2e_node/services/util.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 services
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// terminationSignals are signals that cause the program to exit in the
|
||||
// supported platforms (linux, darwin, windows).
|
||||
var terminationSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT}
|
||||
|
||||
// waitForTerminationSignal waits for termination signal.
|
||||
func waitForTerminationSignal() {
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, terminationSignals...)
|
||||
<-sig
|
||||
}
|
Reference in New Issue
Block a user