1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package services
18
19import (
20	"fmt"
21	"io/ioutil"
22	"net"
23	"os"
24
25	"k8s.io/apiserver/pkg/storage/storagebackend"
26
27	utilerrors "k8s.io/apimachinery/pkg/util/errors"
28	apiserver "k8s.io/kubernetes/cmd/kube-apiserver/app"
29	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
30	"k8s.io/kubernetes/test/e2e/framework"
31)
32
33const (
34	clusterIPRange = "10.0.0.1/24"
35	// This key is for testing purposes only and is not considered secure.
36	ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
37MHcCAQEEIEZmTmUhuanLjPA2CLquXivuwBDHTt5XYwgIr/kA1LtRoAoGCCqGSM49
38AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
39/IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
40-----END EC PRIVATE KEY-----`
41)
42
43// APIServer is a server which manages apiserver.
44type APIServer struct {
45	storageConfig storagebackend.Config
46	stopCh        chan struct{}
47}
48
49// NewAPIServer creates an apiserver.
50func NewAPIServer(storageConfig storagebackend.Config) *APIServer {
51	return &APIServer{
52		storageConfig: storageConfig,
53		stopCh:        make(chan struct{}),
54	}
55}
56
57// Start starts the apiserver, returns when apiserver is ready.
58func (a *APIServer) Start() error {
59	const tokenFilePath = "known_tokens.csv"
60
61	o := options.NewServerRunOptions()
62	o.Etcd.StorageConfig = a.storageConfig
63	_, ipnet, err := net.ParseCIDR(clusterIPRange)
64	if err != nil {
65		return err
66	}
67	if len(framework.TestContext.RuntimeConfig) > 0 {
68		o.APIEnablement.RuntimeConfig = framework.TestContext.RuntimeConfig
69	}
70	o.SecureServing.BindAddress = net.ParseIP("127.0.0.1")
71	o.ServiceClusterIPRanges = ipnet.String()
72	o.AllowPrivileged = true
73	if err := generateTokenFile(tokenFilePath); err != nil {
74		return fmt.Errorf("failed to generate token file %s: %v", tokenFilePath, err)
75	}
76	o.Authentication.TokenFile.TokenFile = tokenFilePath
77	o.Admission.GenericAdmission.DisablePlugins = []string{"ServiceAccount", "TaintNodesByCondition"}
78
79	saSigningKeyFile, err := ioutil.TempFile("/tmp", "insecure_test_key")
80	if err != nil {
81		return fmt.Errorf("create temp file failed: %v", err)
82	}
83	defer os.RemoveAll(saSigningKeyFile.Name())
84	if err = ioutil.WriteFile(saSigningKeyFile.Name(), []byte(ecdsaPrivateKey), 0666); err != nil {
85		return fmt.Errorf("write file %s failed: %v", saSigningKeyFile.Name(), err)
86	}
87	o.ServiceAccountSigningKeyFile = saSigningKeyFile.Name()
88	o.Authentication.APIAudiences = []string{"https://foo.bar.example.com"}
89	o.Authentication.ServiceAccounts.Issuers = []string{"https://foo.bar.example.com"}
90	o.Authentication.ServiceAccounts.KeyFiles = []string{saSigningKeyFile.Name()}
91
92	errCh := make(chan error)
93	go func() {
94		defer close(errCh)
95		completedOptions, err := apiserver.Complete(o)
96		if err != nil {
97			errCh <- fmt.Errorf("set apiserver default options error: %v", err)
98			return
99		}
100		if errs := completedOptions.Validate(); len(errs) != 0 {
101			errCh <- fmt.Errorf("failed to validate ServerRunOptions: %v", utilerrors.NewAggregate(errs))
102			return
103		}
104
105		err = apiserver.Run(completedOptions, a.stopCh)
106		if err != nil {
107			errCh <- fmt.Errorf("run apiserver error: %v", err)
108			return
109		}
110	}()
111
112	err = readinessCheck("apiserver", []string{getAPIServerHealthCheckURL()}, errCh)
113	if err != nil {
114		return err
115	}
116	return nil
117}
118
119// Stop stops the apiserver. Currently, there is no way to stop the apiserver.
120// The function is here only for completion.
121func (a *APIServer) Stop() error {
122	if a.stopCh != nil {
123		close(a.stopCh)
124		a.stopCh = nil
125	}
126	return nil
127}
128
129const apiserverName = "apiserver"
130
131// Name returns the name of APIServer.
132func (a *APIServer) Name() string {
133	return apiserverName
134}
135
136func getAPIServerClientURL() string {
137	return framework.TestContext.Host
138}
139
140func getAPIServerHealthCheckURL() string {
141	return framework.TestContext.Host + "/healthz"
142}
143
144func generateTokenFile(tokenFilePath string) error {
145	tokenFile := fmt.Sprintf("%s,kubelet,uid,system:masters\n", framework.TestContext.BearerToken)
146	return ioutil.WriteFile(tokenFilePath, []byte(tokenFile), 0644)
147}
148