blob: f2e221cc5208cef36c4c52060bd3947d665f6dca [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -05001/*
Joey Armstrong9cdee9f2024-01-03 04:56:14 -05002* Copyright 2019-2024 Open Networking Foundation (ONF) and the ONF Contributors
khenaidoo59ce9dd2019-11-11 13:05:32 -05003
Joey Armstrong7f8436c2023-07-09 20:23:27 -04004* 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
khenaidoo59ce9dd2019-11-11 13:05:32 -05007
Joey Armstrong7f8436c2023-07-09 20:23:27 -04008* http://www.apache.org/licenses/LICENSE-2.0
khenaidoo59ce9dd2019-11-11 13:05:32 -05009
Joey Armstrong7f8436c2023-07-09 20:23:27 -040010* 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.
khenaidoo59ce9dd2019-11-11 13:05:32 -050015 */
khenaidoob6238b32020-04-07 12:07:36 -040016package etcd
khenaidoo59ce9dd2019-11-11 13:05:32 -050017
18import (
Neha Sharma94f16a92020-06-26 04:17:55 +000019 "context"
khenaidooc7005fc2019-11-18 19:23:57 -050020 "fmt"
khenaidooc7005fc2019-11-18 19:23:57 -050021 "net/url"
khenaidoo59ce9dd2019-11-11 13:05:32 -050022 "os"
23 "time"
khenaidoo26721882021-08-11 17:42:52 -040024
Abhay Kumar40252eb2025-10-13 13:25:53 +000025 "go.etcd.io/etcd/server/v3/embed"
khenaidoo59ce9dd2019-11-11 13:05:32 -050026)
27
28const (
khenaidooc7005fc2019-11-18 19:23:57 -050029 serverStartUpTimeout = 10 * time.Second // Maximum time allowed to wait for the Etcd server to be ready
30 defaultLocalPersistentStorage = "voltha.test.embed.etcd"
khenaidoo59ce9dd2019-11-11 13:05:32 -050031)
32
Joey Armstrong7f8436c2023-07-09 20:23:27 -040033// EtcdServer represents an embedded Etcd server. It is used for testing only.
khenaidoo59ce9dd2019-11-11 13:05:32 -050034type EtcdServer struct {
35 server *embed.Etcd
36}
37
khenaidooc7005fc2019-11-18 19:23:57 -050038func islogLevelValid(logLevel string) bool {
39 valid := []string{"debug", "info", "warn", "error", "panic", "fatal"}
40 for _, l := range valid {
41 if l == logLevel {
42 return true
43 }
44 }
45 return false
46}
47
48/*
49* MKConfig creates an embedded Etcd config
50* :param configName: A name for this config
51* :param clientPort: The port the etcd client will connect to (do not use 2379 for unit test)
52* :param peerPort: The port the etcd server will listen for its peers (do not use 2380 for unit test)
53* :param localPersistentStorageDir: The name of a local directory which will hold the Etcd server data
54* :param logLevel: One of debug, info, warn, error, panic, or fatal. Default 'info'.
55 */
Neha Sharma94f16a92020-06-26 04:17:55 +000056func MKConfig(ctx context.Context, configName string, clientPort, peerPort int, localPersistentStorageDir string, logLevel string) *embed.Config {
khenaidooc7005fc2019-11-18 19:23:57 -050057 cfg := embed.NewConfig()
58 cfg.Name = configName
59 cfg.Dir = localPersistentStorageDir
Abhay Kumar40252eb2025-10-13 13:25:53 +000060
khenaidooc7005fc2019-11-18 19:23:57 -050061 if !islogLevelValid(logLevel) {
Abhay Kumar40252eb2025-10-13 13:25:53 +000062 logger.Fatalf(ctx, "Invalid log level - %s", logLevel)
khenaidooc7005fc2019-11-18 19:23:57 -050063 }
Abhay Kumar40252eb2025-10-13 13:25:53 +000064
65 cfg.Logger = "zap"
khenaidoo26721882021-08-11 17:42:52 -040066
khenaidooc7005fc2019-11-18 19:23:57 -050067 acurl, err := url.Parse(fmt.Sprintf("http://localhost:%d", clientPort))
68 if err != nil {
Abhay Kumar40252eb2025-10-13 13:25:53 +000069 logger.Fatalf(ctx, "Invalid client port - %d", clientPort)
khenaidooc7005fc2019-11-18 19:23:57 -050070 }
Abhay Kumar40252eb2025-10-13 13:25:53 +000071 cfg.ListenClientUrls = []url.URL{*acurl}
72 cfg.AdvertiseClientUrls = []url.URL{*acurl}
khenaidooc7005fc2019-11-18 19:23:57 -050073
74 apurl, err := url.Parse(fmt.Sprintf("http://localhost:%d", peerPort))
75 if err != nil {
Abhay Kumar40252eb2025-10-13 13:25:53 +000076 logger.Fatalf(ctx, "Invalid peer port - %d", peerPort)
khenaidooc7005fc2019-11-18 19:23:57 -050077 }
Abhay Kumar40252eb2025-10-13 13:25:53 +000078 cfg.ListenPeerUrls = []url.URL{*apurl}
79 cfg.AdvertisePeerUrls = []url.URL{*apurl}
khenaidooc7005fc2019-11-18 19:23:57 -050080
81 cfg.ClusterState = embed.ClusterStateFlagNew
82 cfg.InitialCluster = cfg.Name + "=" + apurl.String()
83
84 return cfg
85}
86
Joey Armstrong7f8436c2023-07-09 20:23:27 -040087// getDefaultCfg specifies the default config
khenaidoo59ce9dd2019-11-11 13:05:32 -050088func getDefaultCfg() *embed.Config {
89 cfg := embed.NewConfig()
Abhay Kumar40252eb2025-10-13 13:25:53 +000090 cfg.Logger = "zap"
khenaidooc7005fc2019-11-18 19:23:57 -050091 cfg.Dir = defaultLocalPersistentStorage
khenaidoo59ce9dd2019-11-11 13:05:32 -050092 return cfg
93}
94
Joey Armstrong7f8436c2023-07-09 20:23:27 -040095// StartEtcdServer creates and starts an embedded Etcd server. A local directory to store data is created for the
96// embedded server lifetime (for the duration of a unit test. The server runs at localhost:2379.
Neha Sharma94f16a92020-06-26 04:17:55 +000097func StartEtcdServer(ctx context.Context, cfg *embed.Config) *EtcdServer {
khenaidoo59ce9dd2019-11-11 13:05:32 -050098 // If the server is already running, just return
99 if cfg == nil {
100 cfg = getDefaultCfg()
101 }
102 // Remove the local directory as
103 // a safeguard for the case where a prior test failed
khenaidooc7005fc2019-11-18 19:23:57 -0500104 if err := os.RemoveAll(cfg.Dir); err != nil {
Neha Sharma94f16a92020-06-26 04:17:55 +0000105 logger.Fatalf(ctx, "Failure removing local directory %s", cfg.Dir)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500106 }
107 e, err := embed.StartEtcd(cfg)
108 if err != nil {
Neha Sharma94f16a92020-06-26 04:17:55 +0000109 logger.Fatal(ctx, err)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500110 }
111 select {
112 case <-e.Server.ReadyNotify():
Neha Sharma94f16a92020-06-26 04:17:55 +0000113 logger.Debug(ctx, "Embedded Etcd server is ready!")
khenaidoo59ce9dd2019-11-11 13:05:32 -0500114 case <-time.After(serverStartUpTimeout):
115 e.Server.HardStop() // trigger a shutdown
116 e.Close()
Neha Sharma94f16a92020-06-26 04:17:55 +0000117 logger.Fatal(ctx, "Embedded Etcd server took too long to start!")
khenaidoo59ce9dd2019-11-11 13:05:32 -0500118 case err := <-e.Err():
119 e.Server.HardStop() // trigger a shutdown
120 e.Close()
Neha Sharma94f16a92020-06-26 04:17:55 +0000121 logger.Fatalf(ctx, "Embedded Etcd server errored out - %s", err)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500122 }
123 return &EtcdServer{server: e}
124}
125
Joey Armstrong7f8436c2023-07-09 20:23:27 -0400126// Stop closes the embedded Etcd server and removes the local data directory as well
Neha Sharma94f16a92020-06-26 04:17:55 +0000127func (es *EtcdServer) Stop(ctx context.Context) {
khenaidoo59ce9dd2019-11-11 13:05:32 -0500128 if es != nil {
khenaidooc7005fc2019-11-18 19:23:57 -0500129 storage := es.server.Config().Dir
khenaidoo59ce9dd2019-11-11 13:05:32 -0500130 es.server.Server.HardStop()
131 es.server.Close()
khenaidooc7005fc2019-11-18 19:23:57 -0500132 if err := os.RemoveAll(storage); err != nil {
Neha Sharma94f16a92020-06-26 04:17:55 +0000133 logger.Fatalf(ctx, "Failure removing local directory %s", es.server.Config().Dir)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500134 }
135 }
136}