diff --git a/constants/constants.go b/constants/constants.go deleted file mode 100644 index 434d91ede..000000000 --- a/constants/constants.go +++ /dev/null @@ -1,11 +0,0 @@ -package constants - -// Common constants to be used across gd2 modules -const ( - UseTLS = "usetls" - CertFile = "cert-file" - KeyFile = "key-file" - CAFile = "ca-file" - ClntCertFile = "client-cert-file" - ClntKeyFile = "client-key-file" -) diff --git a/doc/quick-start-user-guide.md b/doc/quick-start-user-guide.md index 8cbb58912..4163e9760 100644 --- a/doc/quick-start-user-guide.md +++ b/doc/quick-start-user-guide.md @@ -31,19 +31,26 @@ These packages require dependencies present in [EPEL](https://fedoraproject.org/ Install packages that provide GlusterFS server (brick process) and client (fuse, libgfapi): ```sh -# curl -o /etc/yum.repos.d/glusterfs-nighthly-master.repo http://artifacts.ci.centos.org/gluster/nightly/master.repo +# curl -o /etc/yum.repos.d/glusterfs-nightly-master.repo http://artifacts.ci.centos.org/gluster/nightly/master.repo # yum install glusterfs-server glusterfs-fuse glusterfs-api ``` ### Download glusterd2 -Glusterd2 is a single binary without any external dependencies. Like all Go programs, dependencies are statically linked. You can download the [latest release](https://github.com/gluster/glusterd2/releases) from Github. +As we do not have releases often, our nightly RPMs are generally more stable +as they contain the latest fixes. If you are on centos 7, you can download the +latest glusterd2 nightly RPM using the following method: ```sh -$ wget https://github.com/gluster/glusterd2/releases/download/v4.0dev-7/glusterd2-v4.0dev-7-linux-amd64.tar.xz -$ tar -xf glusterd2-v4.0dev-7-linux-amd64.tar.xz +# curl -o /etc/yum.repos.d/glusterd2-nightly-master.repo http://artifacts.ci.centos.org/gluster/gd2-nightly/gd2-master.repo +# yum install glusterd2 ``` +Alternatlively, if you are using a non-RPM based distro, you can download +binaries of the latest release. Like all Go programs, glusterd2 is a single +binary (statically linked) without external dependencies. You can download the +[latest release](https://github.com/gluster/glusterd2/releases) from Github. + ### Running glusterd2 **Create a working directory:** This is where glusterd2 will store all data which includes logs, pid files, etcd information etc. For this example, we will be using a temporary path. If a working directory is not specified, it defaults to current directory. diff --git a/glustercli/cmd/georep.go b/glustercli/cmd/georep.go index 326d09930..ac1714c08 100644 --- a/glustercli/cmd/georep.go +++ b/glustercli/cmd/georep.go @@ -274,27 +274,27 @@ func (a *georepAction) String() string { } func handleGeorepAction(args []string, action georepAction) { - masterdata, remotedata, err := getVolIDs(args) + masterVolID, remoteVolID, err := getVolIDs(args) if err != nil { failure(fmt.Sprintf("Geo-replication %s failed.\n", action.String()), err, 1) } switch action { case georepStart: - _, err = client.GeorepStart(masterdata.id, remotedata.id, flagGeorepCmdForce) + _, err = client.GeorepStart(masterVolID, remoteVolID, flagGeorepCmdForce) case georepStop: - _, err = client.GeorepStop(masterdata.id, remotedata.id, flagGeorepCmdForce) + _, err = client.GeorepStop(masterVolID, remoteVolID, flagGeorepCmdForce) case georepPause: - _, err = client.GeorepPause(masterdata.id, remotedata.id, flagGeorepCmdForce) + _, err = client.GeorepPause(masterVolID, remoteVolID, flagGeorepCmdForce) case georepResume: - _, err = client.GeorepResume(masterdata.id, remotedata.id, flagGeorepCmdForce) + _, err = client.GeorepResume(masterVolID, remoteVolID, flagGeorepCmdForce) case georepDelete: - err = client.GeorepDelete(masterdata.id, remotedata.id, flagGeorepCmdForce) + err = client.GeorepDelete(masterVolID, remoteVolID, flagGeorepCmdForce) } if err != nil { if verbose { log.WithFields(log.Fields{ - "volume": masterdata.volname, + "volume": args[0], "error": err.Error(), }).Error("geo-replication", action.String(), "failed") } @@ -363,33 +363,49 @@ func getRemoteClient(host string) (string, *restclient.Client, error) { return clienturl, restclient.New(clienturl, "", "", "", true), nil } -func getVolIDs(pargs []string) (*volumeDetails, *volumeDetails, error) { - var masterdata *volumeDetails - var remotedata *volumeDetails - var err error +func getVolIDs(pargs []string) (string, string, error) { + var ( + masterVolID string + remoteVolID string + ) + + allSessions, err := client.GeorepStatus("", "") + if err != nil { + failure(errGeorepStatusCommandFailed, err, 1) + } if len(pargs) >= 1 { - masterdata, err = getVolumeDetails(pargs[0], nil) - if err != nil { - return nil, nil, err + for _, s := range allSessions { + if s.MasterVol == pargs[0] { + masterVolID = s.MasterID.String() + } + } + if masterVolID == "" { + return "", "", errors.New("failed to get master volume info") } } if len(pargs) >= 2 { _, remotehost, remotevol, err := parseRemoteData(pargs[1]) if err != nil { - return nil, nil, err + return "", "", err } - _, rclient, err := getRemoteClient(remotehost) - if err != nil { - return nil, nil, err + + for _, s := range allSessions { + if s.RemoteVol == remotevol { + + for _, host := range s.RemoteHosts { + if host.Hostname == remotehost { + remoteVolID = s.RemoteID.String() + } + } + } } - remotedata, err = getVolumeDetails(remotevol, rclient) - if err != nil { - return nil, nil, err + if remoteVolID == "" { + return "", "", errors.New("failed to get remote volume info") } } - return masterdata, remotedata, nil + return masterVolID, remoteVolID, nil } var georepStatusCmd = &cobra.Command{ @@ -398,23 +414,23 @@ var georepStatusCmd = &cobra.Command{ Args: cobra.RangeArgs(0, 2), Run: func(cmd *cobra.Command, args []string) { var err error - masterdata, remotedata, err := getVolIDs(args) + masterVolID, remoteVolID, err := getVolIDs(args) if err != nil { failure(errGeorepStatusCommandFailed, err, 1) } var sessions []georepapi.GeorepSession - // If mastervolid or remotevolid is empty then get status of all and then filter - if masterdata == nil || remotedata == nil { + // If masterVolID or remoteVolID is empty then get status of all and then filter + if masterVolID == "" || remoteVolID == "" { allSessions, err := client.GeorepStatus("", "") if err != nil { failure(errGeorepStatusCommandFailed, err, 1) } for _, s := range allSessions { - if masterdata != nil && s.MasterID.String() != masterdata.id { + if masterVolID != "" && s.MasterID.String() != masterVolID { continue } - if remotedata != nil && s.RemoteID.String() != remotedata.id { + if remoteVolID != "" && s.RemoteID.String() != remoteVolID { continue } sessionDetail, err := client.GeorepStatus(s.MasterID.String(), s.RemoteID.String()) @@ -424,7 +440,7 @@ var georepStatusCmd = &cobra.Command{ sessions = append(sessions, sessionDetail[0]) } } else { - sessions, err = client.GeorepStatus(masterdata.id, remotedata.id) + sessions, err = client.GeorepStatus(masterVolID, remoteVolID) if err != nil { failure(errGeorepStatusCommandFailed, err, 1) } @@ -469,12 +485,12 @@ var georepGetCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { var err error - masterdata, remotedata, err := getVolIDs(args) + masterVolID, remoteVolID, err := getVolIDs(args) if err != nil { failure("Error getting Volume IDs", err, 1) } - opts, err := client.GeorepGet(masterdata.id, remotedata.id) + opts, err := client.GeorepGet(masterVolID, remoteVolID) if err != nil { failure("Error getting Options", err, 1) } @@ -540,7 +556,7 @@ var georepSetCmd = &cobra.Command{ Args: cobra.ExactArgs(4), Run: func(cmd *cobra.Command, args []string) { var err error - masterdata, remotedata, err := getVolIDs(args) + masterVolID, remoteVolID, err := getVolIDs(args) if err != nil { failure("Error getting Volume IDs", err, 1) } @@ -548,7 +564,7 @@ var georepSetCmd = &cobra.Command{ opts := make(map[string]string) opts[args[2]] = args[3] - err = client.GeorepSet(masterdata.id, remotedata.id, opts) + err = client.GeorepSet(masterVolID, remoteVolID, opts) if err != nil { failure("Geo-replication session config set failed", err, 1) } @@ -562,12 +578,12 @@ var georepResetCmd = &cobra.Command{ Args: cobra.MinimumNArgs(3), Run: func(cmd *cobra.Command, args []string) { var err error - masterdata, remotedata, err := getVolIDs(args) + masterVolID, remoteVolID, err := getVolIDs(args) if err != nil { failure(err.Error(), err, 1) } - err = client.GeorepReset(masterdata.id, remotedata.id, args[2:]) + err = client.GeorepReset(masterVolID, remoteVolID, args[2:]) if err != nil { failure("Geo-replication session config reset failed", err, 1) } diff --git a/glusterd2/store/config.go b/glusterd2/store/config.go index d82dc46f1..405480f3b 100644 --- a/glusterd2/store/config.go +++ b/glusterd2/store/config.go @@ -6,7 +6,6 @@ import ( "os" "path" - "github.com/gluster/glusterd2/constants" "github.com/gluster/glusterd2/pkg/elasticetcd" "github.com/pelletier/go-toml" @@ -16,14 +15,28 @@ import ( ) const ( - noEmbedOpt = "noembed" - etcdEndpointsOpt = "etcdendpoints" - etcdCURLsOpt = "etcdcurls" - etcdPURLsOpt = "etcdpurls" - etcdLogFileOpt = "etcdlogfile" - + // etcd client options + noEmbedOpt = "noembed" + // TODO: Remove noembed as config option, presence of "etcdendpoints" would be sufficient + etcdEndpointsOpt = "etcdendpoints" + etcdClientCertFileOpt = "etcd-client-cert-file" + etcdClientKeyFileOpt = "etcd-client-key-file" + etcdClientCAFileOpt = "etcd-client-ca-file" + + // etcd server (elasticetcd) options + etcdCURLsOpt = "etcdcurls" + etcdPURLsOpt = "etcdpurls" + etcdLogFileOpt = "etcdlogfile" defaultEtcdLogFile = "etcd.log" + // TODO: Fix these too. Make elasticetcd support TLS if it doesn't + // already. + useTLSOpt = "usetls" + caFileOpt = "ca-file" + certFileOpt = "cert-file" + keyFileOpt = "key-file" + + // common options storeConfFile = "store.toml" ) @@ -35,6 +48,10 @@ func InitFlags() { flag.StringSlice(etcdEndpointsOpt, nil, fmt.Sprintf("ETCD endpoints of a remote etcd cluster for the store to connect to. (Defaults to: %s)", elasticetcd.DefaultEndpoint)) flag.StringSlice(etcdCURLsOpt, nil, fmt.Sprintf("URLs which etcd server will use for peer to peer communication. (Defaults to: %s)", elasticetcd.DefaultCURL)) flag.StringSlice(etcdPURLsOpt, nil, fmt.Sprintf("URLs which etcd server will use to receive etcd client requests. (Defaults to: %s)", elasticetcd.DefaultPURL)) + + flag.String(etcdClientCertFileOpt, "", "identify secure etcd client using this TLS certificate file") + flag.String(etcdClientKeyFileOpt, "", "identify secure etcd client using this TLS key file") + flag.String(etcdClientCAFileOpt, "", "verify certificates of TLS-enabled secure etcd servers using this CA bundle") } // Config is the GD2 store configuration @@ -44,31 +61,39 @@ type Config struct { PURLs []string NoEmbed bool UseTLS bool + Dir string + ConfFile string - Dir string - ConfFile string - CertFile string - KeyFile string - CAFile string + // etcd server configuration + CertFile string + KeyFile string + CAFile string + + // etcd client configuration ClntCertFile string ClntKeyFile string + ClntCAFile string } +// TODO: This is also a mess. We should just create a package level global +// instance of *Config and pass its fields directly to flag.* functions. + // NewConfig returns a new store Config with defaults func NewConfig() *Config { return &Config{ - []string{elasticetcd.DefaultEndpoint}, - []string{elasticetcd.DefaultCURL}, - []string{elasticetcd.DefaultPURL}, - false, - config.GetBool(constants.UseTLS), - path.Join(config.GetString("localstatedir"), "store"), - path.Join(config.GetString("localstatedir"), storeConfFile), - config.GetString(constants.CertFile), - config.GetString(constants.KeyFile), - config.GetString(constants.CAFile), - config.GetString(constants.ClntCertFile), - config.GetString(constants.ClntKeyFile), + Endpoints: []string{elasticetcd.DefaultEndpoint}, + CURLs: []string{elasticetcd.DefaultCURL}, + PURLs: []string{elasticetcd.DefaultPURL}, + NoEmbed: false, + UseTLS: false, + Dir: path.Join(config.GetString("localstatedir"), "store"), + ConfFile: path.Join(config.GetString("localstatedir"), storeConfFile), + CertFile: config.GetString(certFileOpt), + KeyFile: config.GetString(keyFileOpt), + CAFile: config.GetString(caFileOpt), + ClntCertFile: config.GetString(etcdClientCertFileOpt), + ClntKeyFile: config.GetString(etcdClientKeyFileOpt), + ClntCAFile: config.GetString(etcdClientCAFileOpt), } } @@ -104,69 +129,56 @@ func GetConfig() *Config { conf = NewConfig() } - var saveconf bool endpoints := config.GetStringSlice(etcdEndpointsOpt) if len(endpoints) > 0 { - saveconf = true conf.Endpoints = endpoints } curls := config.GetStringSlice(etcdCURLsOpt) if len(curls) > 0 { - saveconf = true conf.CURLs = curls } purls := config.GetStringSlice(etcdPURLsOpt) if len(purls) > 0 { - saveconf = true conf.PURLs = purls } - certfile := config.GetString(constants.CertFile) + certfile := config.GetString(certFileOpt) if len(certfile) > 0 { - saveconf = true conf.CertFile = certfile } - keyfile := config.GetString(constants.KeyFile) + keyfile := config.GetString(keyFileOpt) if len(keyfile) > 0 { - saveconf = true conf.KeyFile = keyfile } - cafile := config.GetString(constants.CAFile) + cafile := config.GetString(etcdClientCAFileOpt) if len(cafile) > 0 { - saveconf = true - conf.CAFile = cafile + conf.ClntCAFile = cafile } - clntcertfile := config.GetString(constants.ClntCertFile) + clntcertfile := config.GetString(etcdClientCertFileOpt) if len(clntcertfile) > 0 { - saveconf = true conf.ClntCertFile = clntcertfile } - clntkeyfile := config.GetString(constants.ClntKeyFile) + clntkeyfile := config.GetString(etcdClientKeyFileOpt) if len(clntkeyfile) > 0 { - saveconf = true conf.ClntKeyFile = clntkeyfile } if config.IsSet(noEmbedOpt) { - saveconf = true conf.NoEmbed = config.GetBool(noEmbedOpt) } - if config.IsSet(constants.UseTLS) { - saveconf = true - conf.UseTLS = config.GetBool(constants.UseTLS) + if config.IsSet(useTLSOpt) { + conf.UseTLS = config.GetBool(useTLSOpt) } - if saveconf { - log.Debug("saving updated store config") - if err := conf.Save(); err != nil { - log.WithError(err).Warn("failed to save updated store config") - } + log.Debug("saving updated store config") + if err := conf.Save(); err != nil { + log.WithError(err).Warn("failed to save updated store config") } return conf diff --git a/glusterd2/store/remote.go b/glusterd2/store/remote.go index 9f2207efe..118ba666a 100644 --- a/glusterd2/store/remote.go +++ b/glusterd2/store/remote.go @@ -21,16 +21,16 @@ func newRemoteStore(conf *Config) (*GDStore, error) { if conf.ClntCertFile != "" && conf.ClntKeyFile != "" { tlsCert, err := tls.LoadX509KeyPair(conf.ClntCertFile, conf.ClntKeyFile) if err != nil { - log.WithError(err).Error("failed to load certificate file") + log.WithError(err).Error("failed to load client certificate file") return nil, err } tlsConfig.Certificates = []tls.Certificate{tlsCert} tlsConfig.ClientAuth = tls.RequestClientCert } - if conf.CAFile != "" { - caCert, err := ioutil.ReadFile(conf.CAFile) + if conf.ClntCAFile != "" { + caCert, err := ioutil.ReadFile(conf.ClntCAFile) if err != nil { - log.WithError(err).Error("failed to load CA file") + log.WithError(err).Error("failed to load client CA file") return nil, err } caCertPool := x509.NewCertPool() diff --git a/plugins/events/rest.go b/plugins/events/rest.go index 1d2745529..36340bd6b 100644 --- a/plugins/events/rest.go +++ b/plugins/events/rest.go @@ -49,7 +49,7 @@ func webhookAddHandler(w http.ResponseWriter, r *http.Request) { return } - restutils.SendHTTPResponse(ctx, w, http.StatusOK, "Webhook Added") + restutils.SendHTTPResponse(ctx, w, http.StatusOK, nil) } func webhookDeleteHandler(w http.ResponseWriter, r *http.Request) { diff --git a/plugins/georeplication/rest.go b/plugins/georeplication/rest.go index b9b9964f4..24cb2f6b4 100644 --- a/plugins/georeplication/rest.go +++ b/plugins/georeplication/rest.go @@ -1109,5 +1109,5 @@ func georepSSHKeyPushHandler(w http.ResponseWriter, r *http.Request) { return } - restutils.SendHTTPResponse(ctx, w, http.StatusCreated, "SSH Keys added successfully") + restutils.SendHTTPResponse(ctx, w, http.StatusCreated, nil) } diff --git a/plugins/rebalance/rest.go b/plugins/rebalance/rest.go index 4b3746e81..92082aba2 100644 --- a/plugins/rebalance/rest.go +++ b/plugins/rebalance/rest.go @@ -164,13 +164,13 @@ func rebalanceStopHandler(w http.ResponseWriter, r *http.Request) { rebalInfo, err := GetRebalanceInfo(volname) if err != nil { - restutils.SendHTTPError(r.Context(), w, http.StatusBadRequest, ErrRebalanceNotStarted) + restutils.SendHTTPError(ctx, w, http.StatusBadRequest, ErrRebalanceNotStarted) return } // Check whether the rebalance state is started if rebalInfo.State != rebalanceapi.Started { - restutils.SendHTTPError(r.Context(), w, http.StatusBadRequest, ErrRebalanceNotStarted) + restutils.SendHTTPError(ctx, w, http.StatusBadRequest, ErrRebalanceNotStarted) return } @@ -193,7 +193,6 @@ func rebalanceStopHandler(w http.ResponseWriter, r *http.Request) { return } - rebalInfo.Volname = volname rebalInfo.State = rebalanceapi.Stopped rebalInfo.Cmd = rebalanceapi.CmdStop @@ -209,12 +208,12 @@ func rebalanceStopHandler(w http.ResponseWriter, r *http.Request) { logger.WithError(err).WithFields(log.Fields{ "volname": volname, }).Error("failed to stop rebalance on volume") - restutils.SendHTTPError(r.Context(), w, http.StatusInternalServerError, err) + restutils.SendHTTPError(ctx, w, http.StatusInternalServerError, err) return } logger.WithField("volname", rebalInfo.Volname).Info("rebalance stopped") - restutils.SendHTTPResponse(r.Context(), w, http.StatusOK, nil) + restutils.SendHTTPResponse(ctx, w, http.StatusOK, nil) } func rebalanceStatusHandler(w http.ResponseWriter, r *http.Request) { @@ -288,7 +287,7 @@ func rebalanceStatusHandler(w http.ResponseWriter, r *http.Request) { return } - restutils.SendHTTPResponse(r.Context(), w, http.StatusOK, response) + restutils.SendHTTPResponse(ctx, w, http.StatusOK, response) } func createRebalanceStatusResp(ctx transaction.TxnCtx, volInfo *volume.Volinfo) (*rebalanceapi.RebalStatus, error) {