blob: b4e201d4e2b481dff0d0a3fb6e5ca08afb62d48d [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001/*
2 * Copyright 2019-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Matteo Scandolod525ae32020-04-02 17:27:29 -070016package etcd
khenaidooab1f7bd2019-11-14 14:00:27 -050017
18import (
Divya Desai660dbba2019-10-16 07:06:49 +000019 "fmt"
khenaidooab1f7bd2019-11-14 14:00:27 -050020 "go.etcd.io/etcd/embed"
Divya Desai660dbba2019-10-16 07:06:49 +000021 "net/url"
khenaidooab1f7bd2019-11-14 14:00:27 -050022 "os"
23 "time"
24)
25
26const (
Divya Desai660dbba2019-10-16 07:06:49 +000027 serverStartUpTimeout = 10 * time.Second // Maximum time allowed to wait for the Etcd server to be ready
28 defaultLocalPersistentStorage = "voltha.test.embed.etcd"
khenaidooab1f7bd2019-11-14 14:00:27 -050029)
30
31//EtcdServer represents an embedded Etcd server. It is used for testing only.
32type EtcdServer struct {
33 server *embed.Etcd
34}
35
Divya Desai660dbba2019-10-16 07:06:49 +000036func islogLevelValid(logLevel string) bool {
37 valid := []string{"debug", "info", "warn", "error", "panic", "fatal"}
38 for _, l := range valid {
39 if l == logLevel {
40 return true
41 }
42 }
43 return false
44}
45
46/*
47* MKConfig creates an embedded Etcd config
48* :param configName: A name for this config
49* :param clientPort: The port the etcd client will connect to (do not use 2379 for unit test)
50* :param peerPort: The port the etcd server will listen for its peers (do not use 2380 for unit test)
51* :param localPersistentStorageDir: The name of a local directory which will hold the Etcd server data
52* :param logLevel: One of debug, info, warn, error, panic, or fatal. Default 'info'.
53 */
54func MKConfig(configName string, clientPort, peerPort int, localPersistentStorageDir string, logLevel string) *embed.Config {
55 cfg := embed.NewConfig()
56 cfg.Name = configName
57 cfg.Dir = localPersistentStorageDir
58 cfg.Logger = "zap"
59 if !islogLevelValid(logLevel) {
serkant.uluderya2ae470f2020-01-21 11:13:09 -080060 logger.Fatalf("Invalid log level -%s", logLevel)
Divya Desai660dbba2019-10-16 07:06:49 +000061 }
62 cfg.LogLevel = logLevel
63 acurl, err := url.Parse(fmt.Sprintf("http://localhost:%d", clientPort))
64 if err != nil {
serkant.uluderya2ae470f2020-01-21 11:13:09 -080065 logger.Fatalf("Invalid client port -%d", clientPort)
Divya Desai660dbba2019-10-16 07:06:49 +000066 }
67 cfg.ACUrls = []url.URL{*acurl}
68 cfg.LCUrls = []url.URL{*acurl}
69
70 apurl, err := url.Parse(fmt.Sprintf("http://localhost:%d", peerPort))
71 if err != nil {
serkant.uluderya2ae470f2020-01-21 11:13:09 -080072 logger.Fatalf("Invalid peer port -%d", peerPort)
Divya Desai660dbba2019-10-16 07:06:49 +000073 }
74 cfg.LPUrls = []url.URL{*apurl}
75 cfg.APUrls = []url.URL{*apurl}
76
77 cfg.ClusterState = embed.ClusterStateFlagNew
78 cfg.InitialCluster = cfg.Name + "=" + apurl.String()
79
80 return cfg
81}
82
khenaidooab1f7bd2019-11-14 14:00:27 -050083//getDefaultCfg specifies the default config
84func getDefaultCfg() *embed.Config {
85 cfg := embed.NewConfig()
Divya Desai660dbba2019-10-16 07:06:49 +000086 cfg.Dir = defaultLocalPersistentStorage
khenaidooab1f7bd2019-11-14 14:00:27 -050087 cfg.Logger = "zap"
88 cfg.LogLevel = "error"
89 return cfg
90}
91
92//StartEtcdServer creates and starts an embedded Etcd server. A local directory to store data is created for the
93//embedded server lifetime (for the duration of a unit test. The server runs at localhost:2379.
94func StartEtcdServer(cfg *embed.Config) *EtcdServer {
95 // If the server is already running, just return
96 if cfg == nil {
97 cfg = getDefaultCfg()
98 }
99 // Remove the local directory as
100 // a safeguard for the case where a prior test failed
Divya Desai660dbba2019-10-16 07:06:49 +0000101 if err := os.RemoveAll(cfg.Dir); err != nil {
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800102 logger.Fatalf("Failure removing local directory %s", cfg.Dir)
khenaidooab1f7bd2019-11-14 14:00:27 -0500103 }
104 e, err := embed.StartEtcd(cfg)
105 if err != nil {
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800106 logger.Fatal(err)
khenaidooab1f7bd2019-11-14 14:00:27 -0500107 }
108 select {
109 case <-e.Server.ReadyNotify():
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800110 logger.Debug("Embedded Etcd server is ready!")
khenaidooab1f7bd2019-11-14 14:00:27 -0500111 case <-time.After(serverStartUpTimeout):
112 e.Server.HardStop() // trigger a shutdown
113 e.Close()
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800114 logger.Fatal("Embedded Etcd server took too long to start!")
khenaidooab1f7bd2019-11-14 14:00:27 -0500115 case err := <-e.Err():
116 e.Server.HardStop() // trigger a shutdown
117 e.Close()
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800118 logger.Fatalf("Embedded Etcd server errored out - %s", err)
khenaidooab1f7bd2019-11-14 14:00:27 -0500119 }
120 return &EtcdServer{server: e}
121}
122
123//Stop closes the embedded Etcd server and removes the local data directory as well
124func (es *EtcdServer) Stop() {
125 if es != nil {
Divya Desai660dbba2019-10-16 07:06:49 +0000126 storage := es.server.Config().Dir
khenaidooab1f7bd2019-11-14 14:00:27 -0500127 es.server.Server.HardStop()
128 es.server.Close()
Divya Desai660dbba2019-10-16 07:06:49 +0000129 if err := os.RemoveAll(storage); err != nil {
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800130 logger.Fatalf("Failure removing local directory %s", es.server.Config().Dir)
khenaidooab1f7bd2019-11-14 14:00:27 -0500131 }
132 }
133}