forked from AliyunContainerService/pouch
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add update daemon config function
Add update daemon config function. If pouchd is alive, it will change daemon config if it supports, and then change the config file. If pouchd is dead, it just update the daemon config file. Pouchd dead: You can specified the config file by set `--config-file` to update, more functions you can see by `pouch updatedaemon --help` Signed-off-by: Rudy Zhang <[email protected]>
- Loading branch information
Showing
15 changed files
with
329 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
|
||
"github.com/alibaba/pouch/apis/types" | ||
"github.com/alibaba/pouch/daemon/config" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// updateDescription is used to describe updatedaemon command in detail and auto generate command doc. | ||
var daemonUpdateDescription = "Update daemon's configurations, if daemon is stoped, it will just update config file. " + | ||
"Online update just including: image proxy, label, offline update including: manager white list, debug level, " + | ||
"execute root directory, bridge name, bridge IP, fixed CIDR, defaut gateway, iptables, ipforwark, userland proxy" + | ||
"If pouchd is alive, you can only use --offline=true to update config file" | ||
|
||
// DaemonUpdateCommand use to implement 'updatedaemon' command, it modifies the configurations of a container. | ||
type DaemonUpdateCommand struct { | ||
baseCommand | ||
|
||
configFile string | ||
offline bool | ||
|
||
debug bool | ||
imageProxy string | ||
label []string | ||
managerWhiteList string | ||
execRoot string | ||
bridgeName string | ||
bridgeIP string | ||
fixedCIDR string | ||
defaultGateway string | ||
iptables bool | ||
ipforward bool | ||
userlandProxy bool | ||
} | ||
|
||
// Init initialize updatedaemon command. | ||
func (udc *DaemonUpdateCommand) Init(c *Cli) { | ||
udc.cli = c | ||
udc.cmd = &cobra.Command{ | ||
Use: "updatedaemon [OPTIONS]", | ||
Short: "Update the configurations of pouchd", | ||
Long: daemonUpdateDescription, | ||
Args: cobra.NoArgs, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return udc.daemonUpdateRun(args) | ||
}, | ||
Example: daemonUpdateExample(), | ||
} | ||
udc.addFlags() | ||
} | ||
|
||
// addFlags adds flags for specific command. | ||
func (udc *DaemonUpdateCommand) addFlags() { | ||
flagSet := udc.cmd.Flags() | ||
flagSet.SetInterspersed(false) | ||
flagSet.StringVar(&udc.configFile, "config-file", "/etc/pouch/config.json", "specified config file for updating daemon") | ||
flagSet.BoolVar(&udc.offline, "offline", false, "just update daemon config file") | ||
|
||
flagSet.BoolVar(&udc.debug, "debug", false, "update daemon debug mode") | ||
flagSet.StringVar(&udc.imageProxy, "image-proxy", "", "update daemon image proxy") | ||
flagSet.StringVar(&udc.managerWhiteList, "manager-white-list", "", "update daemon manager white list") | ||
flagSet.StringSliceVar(&udc.label, "label", nil, "update daemon labels") | ||
flagSet.StringVar(&udc.execRoot, "exec-root-dir", "", "update exec root directory for network") | ||
flagSet.StringVar(&udc.bridgeName, "bridge-name", "", "update daemon bridge device") | ||
flagSet.StringVar(&udc.bridgeIP, "bip", "", "update daemon bridge IP") | ||
flagSet.StringVar(&udc.fixedCIDR, "fixed-cidr", "", "update daemon bridge fixed CIDR") | ||
flagSet.StringVar(&udc.defaultGateway, "default-gateway", "", "update daemon bridge default gateway") | ||
flagSet.BoolVar(&udc.iptables, "iptables", true, "update daemon with iptables") | ||
flagSet.BoolVar(&udc.ipforward, "ipforward", true, "udpate daemon with ipforward") | ||
flagSet.BoolVar(&udc.userlandProxy, "userland-proxy", false, "update daemon with userland proxy") | ||
} | ||
|
||
// daemonUpdateRun is the entry of updatedaemon command. | ||
func (udc *DaemonUpdateCommand) daemonUpdateRun(args []string) error { | ||
ctx := context.Background() | ||
|
||
apiClient := udc.cli.Client() | ||
|
||
msg, err := apiClient.SystemPing(ctx) | ||
if !udc.offline && err == nil && msg == "OK" { | ||
// TODO: daemon support more configures for update online, such as debug level. | ||
daemonConfig := &types.DaemonUpdateConfig{ | ||
ImageProxy: udc.imageProxy, | ||
Labels: udc.label, | ||
} | ||
|
||
err = apiClient.DaemonUpdate(ctx, daemonConfig) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to update alive daemon config") | ||
} | ||
} else { | ||
// offline update config file. | ||
err = udc.updateDaemonConfigFile() | ||
if err != nil { | ||
return errors.Wrap(err, "failed to update daemon config file.") | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (udc *DaemonUpdateCommand) updateDaemonConfigFile() error { | ||
// read config from file. | ||
contents, err := ioutil.ReadFile(udc.configFile) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to read config file(%s)", udc.configFile) | ||
} | ||
|
||
daemonConfig := &config.Config{} | ||
if err = json.NewDecoder(bytes.NewReader(contents)).Decode(daemonConfig); err != nil { | ||
return errors.Wrap(err, "failed to decode json: %s") | ||
} | ||
|
||
flagSet := udc.cmd.Flags() | ||
|
||
if flagSet.Changed("image-proxy") { | ||
daemonConfig.ImageProxy = udc.imageProxy | ||
} | ||
|
||
if flagSet.Changed("manager-white-list") { | ||
daemonConfig.TLS.ManagerWhiteList = udc.managerWhiteList | ||
} | ||
|
||
// TODO: add parse labels | ||
|
||
if flagSet.Changed("exec-root-dir") { | ||
daemonConfig.NetworkConfig.ExecRoot = udc.execRoot | ||
} | ||
|
||
if flagSet.Changed("bridge-name") { | ||
daemonConfig.NetworkConfig.BridgeConfig.Name = udc.bridgeName | ||
} | ||
|
||
if flagSet.Changed("bip") { | ||
daemonConfig.NetworkConfig.BridgeConfig.IP = udc.bridgeIP | ||
} | ||
|
||
if flagSet.Changed("fixed-cidr") { | ||
daemonConfig.NetworkConfig.BridgeConfig.FixedCIDR = udc.fixedCIDR | ||
} | ||
|
||
if flagSet.Changed("default-gateway") { | ||
daemonConfig.NetworkConfig.BridgeConfig.GatewayIPv4 = udc.defaultGateway | ||
} | ||
|
||
if flagSet.Changed("iptables") { | ||
daemonConfig.NetworkConfig.BridgeConfig.IPTables = udc.iptables | ||
} | ||
|
||
if flagSet.Changed("ipforward") { | ||
daemonConfig.NetworkConfig.BridgeConfig.IPForward = udc.ipforward | ||
} | ||
|
||
if flagSet.Changed("userland-proxy") { | ||
daemonConfig.NetworkConfig.BridgeConfig.UserlandProxy = udc.userlandProxy | ||
} | ||
|
||
// write config to file | ||
fd, err := os.OpenFile(udc.configFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to open config file(%s)", udc.configFile) | ||
} | ||
defer fd.Close() | ||
|
||
fd.Seek(0, io.SeekStart) | ||
encoder := json.NewEncoder(fd) | ||
encoder.SetIndent("", " ") | ||
err = encoder.Encode(daemonConfig) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to write config to file(%s)", udc.configFile) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// daemonUpdateExample shows examples in updatedaemon command, and is used in auto-generated cli docs. | ||
func daemonUpdateExample() string { | ||
return `$ pouch updatedaemon --debug=true` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/alibaba/pouch/apis/types" | ||
) | ||
|
||
// DaemonUpdate requests daemon to update daemon config. | ||
func (client *APIClient) DaemonUpdate(ctx context.Context, config *types.DaemonUpdateConfig) error { | ||
resp, err := client.post(ctx, "/daemon/update", nil, config, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ensureCloseReader(resp) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package client | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/alibaba/pouch/apis/types" | ||
) | ||
|
||
func TestDaemonUpdateError(t *testing.T) { | ||
client := &APIClient{ | ||
HTTPCli: newMockClient(errorMockResponse(http.StatusInternalServerError, "Server error")), | ||
} | ||
err := client.DaemonUpdate(context.Background(), &types.DaemonUpdateConfig{}) | ||
if err == nil || !strings.Contains(err.Error(), "Server error") { | ||
t.Fatalf("expected a Server Error, got %v", err) | ||
} | ||
} | ||
|
||
func TestDaemonUpdate(t *testing.T) { | ||
expectedURL := "/daemon/update" | ||
|
||
httpClient := newMockClient(func(req *http.Request) (*http.Response, error) { | ||
if !strings.HasPrefix(req.URL.Path, expectedURL) { | ||
return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL) | ||
} | ||
if req.Header.Get("Content-Type") == "application/json" { | ||
var updateConfig interface{} | ||
if err := json.NewDecoder(req.Body).Decode(&updateConfig); err != nil { | ||
return nil, fmt.Errorf("failed to parse json: %v", err) | ||
} | ||
} | ||
return &http.Response{ | ||
StatusCode: http.StatusOK, | ||
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), | ||
}, nil | ||
}) | ||
client := &APIClient{ | ||
HTTPCli: httpClient, | ||
} | ||
|
||
if err := client.DaemonUpdate(context.Background(), &types.DaemonUpdateConfig{Labels: []string{"abc=def"}}); err != nil { | ||
t.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.