control: Implement service command 'stop,restart,status'. (#2883)
- stop - stops all the servers. - restart - restart all the servers. - status - prints status of storage info about the cluster.master
parent
57f75b1d9b
commit
3cfb23750a
@ -0,0 +1,96 @@ |
|||||||
|
/* |
||||||
|
* Minio Cloud Storage, (C) 2016 Minio, Inc. |
||||||
|
* |
||||||
|
* 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 cmd |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/url" |
||||||
|
"path" |
||||||
|
|
||||||
|
"github.com/minio/cli" |
||||||
|
"github.com/minio/mc/pkg/console" |
||||||
|
) |
||||||
|
|
||||||
|
var serviceCmd = cli.Command{ |
||||||
|
Name: "service", |
||||||
|
Usage: "Service command line to manage Minio server.", |
||||||
|
Action: serviceControl, |
||||||
|
Flags: globalFlags, |
||||||
|
CustomHelpTemplate: `NAME: |
||||||
|
minio control {{.Name}} - {{.Usage}} |
||||||
|
|
||||||
|
USAGE: |
||||||
|
minio control {{.Name}} [status|restart|stop] URL |
||||||
|
|
||||||
|
FLAGS: |
||||||
|
{{range .Flags}}{{.}} |
||||||
|
{{end}} |
||||||
|
EXAMPLES: |
||||||
|
1. Prints current status information of the cluster. |
||||||
|
$ minio control service status http://10.1.10.92:9000/
|
||||||
|
|
||||||
|
2. Restarts the url and all the servers in the cluster. |
||||||
|
$ minio control service restart http://localhost:9000/
|
||||||
|
|
||||||
|
3. Shuts down the url and all the servers in the cluster. |
||||||
|
$ minio control service stop http://localhost:9000/
|
||||||
|
`, |
||||||
|
} |
||||||
|
|
||||||
|
// "minio control service" entry point.
|
||||||
|
func serviceControl(c *cli.Context) { |
||||||
|
if !c.Args().Present() && len(c.Args()) != 2 { |
||||||
|
cli.ShowCommandHelpAndExit(c, "service", 1) |
||||||
|
} |
||||||
|
|
||||||
|
var signal serviceSignal |
||||||
|
switch c.Args().Get(0) { |
||||||
|
case "status": |
||||||
|
signal = serviceStatus |
||||||
|
case "restart": |
||||||
|
signal = serviceRestart |
||||||
|
case "stop": |
||||||
|
signal = serviceStop |
||||||
|
default: |
||||||
|
fatalIf(errInvalidArgument, "Unsupported signalling requested %s", c.Args().Get(0)) |
||||||
|
} |
||||||
|
|
||||||
|
parsedURL, err := url.Parse(c.Args().Get(1)) |
||||||
|
fatalIf(err, "Unable to parse URL %s", c.Args().Get(1)) |
||||||
|
|
||||||
|
authCfg := &authConfig{ |
||||||
|
accessKey: serverConfig.GetCredential().AccessKeyID, |
||||||
|
secretKey: serverConfig.GetCredential().SecretAccessKey, |
||||||
|
secureConn: parsedURL.Scheme == "https", |
||||||
|
address: parsedURL.Host, |
||||||
|
path: path.Join(reservedBucket, controlPath), |
||||||
|
loginMethod: "Controller.LoginHandler", |
||||||
|
} |
||||||
|
client := newAuthClient(authCfg) |
||||||
|
|
||||||
|
args := &ServiceArgs{ |
||||||
|
Signal: signal, |
||||||
|
// This is necessary so that the remotes,
|
||||||
|
// don't end up sending requests back and forth.
|
||||||
|
Remote: true, |
||||||
|
} |
||||||
|
reply := &ServiceReply{} |
||||||
|
err = client.Call("Controller.ServiceHandler", args, reply) |
||||||
|
fatalIf(err, "Service command %s failed for %s", c.Args().Get(0), parsedURL.Host) |
||||||
|
if signal == serviceStatus { |
||||||
|
console.Println(getStorageInfoMsg(reply.StorageInfo)) |
||||||
|
} |
||||||
|
} |
@ -1,79 +0,0 @@ |
|||||||
/* |
|
||||||
* Minio Cloud Storage, (C) 2016 Minio, Inc. |
|
||||||
* |
|
||||||
* 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 cmd |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/url" |
|
||||||
"path" |
|
||||||
|
|
||||||
"github.com/minio/cli" |
|
||||||
) |
|
||||||
|
|
||||||
var shutdownFlags = []cli.Flag{ |
|
||||||
cli.BoolFlag{ |
|
||||||
Name: "restart", |
|
||||||
Usage: "Restart the server.", |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
var shutdownCmd = cli.Command{ |
|
||||||
Name: "shutdown", |
|
||||||
Usage: "Shutdown or restart the server.", |
|
||||||
Action: shutdownControl, |
|
||||||
Flags: append(shutdownFlags, globalFlags...), |
|
||||||
CustomHelpTemplate: `NAME: |
|
||||||
minio control {{.Name}} - {{.Usage}} |
|
||||||
|
|
||||||
USAGE: |
|
||||||
minio control {{.Name}} http://localhost:9000/
|
|
||||||
|
|
||||||
FLAGS: |
|
||||||
{{range .Flags}}{{.}} |
|
||||||
{{end}} |
|
||||||
|
|
||||||
EXAMPLES: |
|
||||||
1. Shutdown the server: |
|
||||||
$ minio control shutdown http://localhost:9000/
|
|
||||||
|
|
||||||
2. Reboot the server: |
|
||||||
$ minio control shutdown --restart http://localhost:9000/
|
|
||||||
`, |
|
||||||
} |
|
||||||
|
|
||||||
// "minio control shutdown" entry point.
|
|
||||||
func shutdownControl(c *cli.Context) { |
|
||||||
if len(c.Args()) != 1 { |
|
||||||
cli.ShowCommandHelpAndExit(c, "shutdown", 1) |
|
||||||
} |
|
||||||
|
|
||||||
parsedURL, err := url.Parse(c.Args()[0]) |
|
||||||
fatalIf(err, "Unable to parse URL.") |
|
||||||
|
|
||||||
authCfg := &authConfig{ |
|
||||||
accessKey: serverConfig.GetCredential().AccessKeyID, |
|
||||||
secretKey: serverConfig.GetCredential().SecretAccessKey, |
|
||||||
secureConn: parsedURL.Scheme == "https", |
|
||||||
address: parsedURL.Host, |
|
||||||
path: path.Join(reservedBucket, controlPath), |
|
||||||
loginMethod: "Controller.LoginHandler", |
|
||||||
} |
|
||||||
client := newAuthClient(authCfg) |
|
||||||
|
|
||||||
args := &ShutdownArgs{Restart: c.Bool("restart")} |
|
||||||
err = client.Call("Controller.ShutdownHandler", args, &GenericReply{}) |
|
||||||
errorIf(err, "Shutting down Minio server at %s failed.", parsedURL.Host) |
|
||||||
} |
|
@ -0,0 +1,91 @@ |
|||||||
|
/* |
||||||
|
* Minio Cloud Storage, (C) 2016 Minio, Inc. |
||||||
|
* |
||||||
|
* 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 cmd |
||||||
|
|
||||||
|
import "testing" |
||||||
|
|
||||||
|
// Tests initialization of remote controller clients.
|
||||||
|
func TestInitRemoteControllerClients(t *testing.T) { |
||||||
|
rootPath, err := newTestConfig("us-east-1") |
||||||
|
if err != nil { |
||||||
|
t.Fatal("Unable to initialize config", err) |
||||||
|
} |
||||||
|
defer removeAll(rootPath) |
||||||
|
|
||||||
|
testCases := []struct { |
||||||
|
srvCmdConfig serverCmdConfig |
||||||
|
totalClients int |
||||||
|
}{ |
||||||
|
// Test - 1 no allocation if server config is not distributed XL.
|
||||||
|
{ |
||||||
|
srvCmdConfig: serverCmdConfig{ |
||||||
|
isDistXL: false, |
||||||
|
}, |
||||||
|
totalClients: 0, |
||||||
|
}, |
||||||
|
// Test - 2 two clients allocated with 4 disks with 2 disks on same node each.
|
||||||
|
{ |
||||||
|
srvCmdConfig: serverCmdConfig{ |
||||||
|
isDistXL: true, |
||||||
|
disks: []string{ |
||||||
|
"10.1.10.1:/mnt/disk1", |
||||||
|
"10.1.10.1:/mnt/disk2", |
||||||
|
"10.1.10.2:/mnt/disk3", |
||||||
|
"10.1.10.2:/mnt/disk4", |
||||||
|
}, |
||||||
|
}, |
||||||
|
totalClients: 2, |
||||||
|
}, |
||||||
|
// Test - 3 4 clients allocated with 4 disks with 1 disk on each node.
|
||||||
|
{ |
||||||
|
srvCmdConfig: serverCmdConfig{ |
||||||
|
isDistXL: true, |
||||||
|
disks: []string{ |
||||||
|
"10.1.10.1:/mnt/disk1", |
||||||
|
"10.1.10.2:/mnt/disk2", |
||||||
|
"10.1.10.3:/mnt/disk3", |
||||||
|
"10.1.10.4:/mnt/disk4", |
||||||
|
}, |
||||||
|
}, |
||||||
|
totalClients: 4, |
||||||
|
}, |
||||||
|
// Test - 4 2 clients allocated with 4 disks with 1 disk ignored.
|
||||||
|
{ |
||||||
|
srvCmdConfig: serverCmdConfig{ |
||||||
|
isDistXL: true, |
||||||
|
disks: []string{ |
||||||
|
"10.1.10.1:/mnt/disk1", |
||||||
|
"10.1.10.2:/mnt/disk2", |
||||||
|
"10.1.10.3:/mnt/disk3", |
||||||
|
"10.1.10.4:/mnt/disk4", |
||||||
|
}, |
||||||
|
ignoredDisks: []string{ |
||||||
|
"10.1.10.1:/mnt/disk1", |
||||||
|
}, |
||||||
|
}, |
||||||
|
totalClients: 3, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
// Evaluate and validate all test cases.
|
||||||
|
for i, testCase := range testCases { |
||||||
|
rclients := initRemoteControllerClients(testCase.srvCmdConfig) |
||||||
|
if len(rclients) != testCase.totalClients { |
||||||
|
t.Errorf("Test %d, Expected %d, got %d RPC clients.", i+1, testCase.totalClients, len(rclients)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,121 @@ |
|||||||
|
/* |
||||||
|
* Minio Cloud Storage, (C) 2016 Minio, Inc. |
||||||
|
* |
||||||
|
* 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 cmd |
||||||
|
|
||||||
|
import ( |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"syscall" |
||||||
|
) |
||||||
|
|
||||||
|
// Represents a type of an exit func which will be invoked upon service signal.
|
||||||
|
type onExitFunc func(err error) |
||||||
|
|
||||||
|
// Represents a type for all the the callback functions invoked upon service signal.
|
||||||
|
type cleanupOnExitFunc func() error |
||||||
|
|
||||||
|
// Type of service signals currently supported.
|
||||||
|
type serviceSignal int |
||||||
|
|
||||||
|
const ( |
||||||
|
serviceStatus = iota // Gets status about the service.
|
||||||
|
serviceRestart // Restarts the service.
|
||||||
|
serviceStop // Stops the server.
|
||||||
|
// Add new service requests here.
|
||||||
|
) |
||||||
|
|
||||||
|
// Global service signal channel.
|
||||||
|
var globalServiceSignalCh chan serviceSignal |
||||||
|
|
||||||
|
// Global service done channel.
|
||||||
|
var globalServiceDoneCh chan struct{} |
||||||
|
|
||||||
|
// Initialize service mutex once.
|
||||||
|
func init() { |
||||||
|
globalServiceDoneCh = make(chan struct{}, 1) |
||||||
|
globalServiceSignalCh = make(chan serviceSignal, 1) |
||||||
|
} |
||||||
|
|
||||||
|
// restartProcess starts a new process passing it the active fd's. It
|
||||||
|
// doesn't fork, but starts a new process using the same environment and
|
||||||
|
// arguments as when it was originally started. This allows for a newly
|
||||||
|
// deployed binary to be started. It returns the pid of the newly started
|
||||||
|
// process when successful.
|
||||||
|
func restartProcess() error { |
||||||
|
// Use the original binary location. This works with symlinks such that if
|
||||||
|
// the file it points to has been changed we will use the updated symlink.
|
||||||
|
argv0, err := exec.LookPath(os.Args[0]) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Pass on the environment and replace the old count key with the new one.
|
||||||
|
cmd := exec.Command(argv0, os.Args[1:]...) |
||||||
|
cmd.Stdout = os.Stdout |
||||||
|
cmd.Stderr = os.Stderr |
||||||
|
return cmd.Start() |
||||||
|
} |
||||||
|
|
||||||
|
// Handles all serviceSignal and execute service functions.
|
||||||
|
func (m *ServerMux) handleServiceSignals() error { |
||||||
|
// Custom exit function
|
||||||
|
runExitFn := func(err error) { |
||||||
|
// If global profiler is set stop before we exit.
|
||||||
|
if globalProfiler != nil { |
||||||
|
globalProfiler.Stop() |
||||||
|
} |
||||||
|
|
||||||
|
// Call user supplied user exit function
|
||||||
|
fatalIf(err, "Unable to gracefully complete service operation.") |
||||||
|
|
||||||
|
// We are usually done here, close global service done channel.
|
||||||
|
globalServiceDoneCh <- struct{}{} |
||||||
|
} |
||||||
|
|
||||||
|
// Start listening on service signal. Monitor signals.
|
||||||
|
trapCh := signalTrap(os.Interrupt, syscall.SIGTERM) |
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-trapCh: |
||||||
|
// Initiate graceful stop.
|
||||||
|
globalServiceSignalCh <- serviceStop |
||||||
|
case signal := <-globalServiceSignalCh: |
||||||
|
switch signal { |
||||||
|
case serviceStatus: |
||||||
|
/// We don't do anything for this.
|
||||||
|
case serviceRestart: |
||||||
|
if err := m.Close(); err != nil { |
||||||
|
errorIf(err, "Unable to close server gracefully") |
||||||
|
} |
||||||
|
if err := restartProcess(); err != nil { |
||||||
|
errorIf(err, "Unable to restart the server.") |
||||||
|
} |
||||||
|
runExitFn(nil) |
||||||
|
case serviceStop: |
||||||
|
if err := m.Close(); err != nil { |
||||||
|
errorIf(err, "Unable to close server gracefully") |
||||||
|
} |
||||||
|
objAPI := newObjectLayerFn() |
||||||
|
if objAPI == nil { |
||||||
|
// Server not initialized yet, exit happily.
|
||||||
|
runExitFn(nil) |
||||||
|
} |
||||||
|
runExitFn(objAPI.Shutdown()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue