Skip to content

Commit 54c6aff

Browse files
k-gostevdimitar-dimitrowstoyan-zoubev
authored
Add containers update agent service (#187)
[#186] Implement containers update agent service Merge update-agent feature branch * Extend daemon configuration and dummy implementation for update agent (#168) * Upgrade google.golang.org/grpc to version 1.53.0 or later (#172) * Preparation for implementing apply of containers desired state (#175) * Implement apply of desired state for containers update agent (#177) * Implement current state reporting for containers update agent (#179) Signed-off-by: Kristiyan Gostev <[email protected]> Co-authored-by: Dimitar Dimitrov <[email protected]> Co-authored-by: Stoyan Zoubev <[email protected]>
1 parent 332de48 commit 54c6aff

31 files changed

+3149
-144
lines changed

containerm/cli/cli_commamd_ctrs_base_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,12 @@ func execTestsRun(t *testing.T, cliTest cliCommandTest) {
122122
// perform the real call
123123
resultErr := cliTest.runCommand(testCase.args)
124124
// assert result
125-
testutil.AssertError(t, expectedRunErr, resultErr)
125+
if expectedRunErr == nil {
126+
testutil.AssertNil(t, resultErr)
127+
} else {
128+
testutil.AssertNotNil(t, resultErr)
129+
testutil.AssertContainsString(t, resultErr.Error(), expectedRunErr.Error())
130+
}
126131
})
127132
}
128133
}

containerm/cli/cli_command_ctrs_create.go

Lines changed: 3 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ package main
1515
import (
1616
"context"
1717
"fmt"
18-
"net"
19-
"strconv"
20-
"strings"
2118
"time"
2219

2320
"github.com/eclipse-kanto/container-management/containerm/containers/types"
@@ -119,23 +116,23 @@ func (cc *createCmd) run(args []string) error {
119116
}
120117

121118
if cc.config.devices != nil {
122-
devs, err := parseDevices(cc.config.devices)
119+
devs, err := util.ParseDeviceMappings(cc.config.devices)
123120
if err != nil {
124121
return err
125122
}
126123
ctrToCreate.HostConfig.Devices = devs
127124
}
128125

129126
if cc.config.mountPoints != nil {
130-
mounts, err := parseMountPoints(cc.config.mountPoints)
127+
mounts, err := util.ParseMountPoints(cc.config.mountPoints)
131128
if err != nil {
132129
return err
133130
} else if mounts != nil {
134131
ctrToCreate.Mounts = mounts
135132
}
136133
}
137134
if cc.config.ports != nil {
138-
mappings, err := parsePortMappings(cc.config.ports)
135+
mappings, err := util.ParsePortMappings(cc.config.ports)
139136
if err != nil {
140137
return err
141138
}
@@ -310,122 +307,3 @@ func (cc *createCmd) setupFlags() {
310307
flagSet.StringSliceVar(&cc.config.decKeys, "dec-keys", nil, "Sets a list of private keys filenames (GPG private key ring, JWE and PKCS7 private key). Each entry can include an optional password separated by a colon after the filename.")
311308
flagSet.StringSliceVar(&cc.config.decRecipients, "dec-recipients", nil, "Sets a recipients certificates list of the image (used only for PKCS7 and must be an x509)")
312309
}
313-
314-
func parseDevices(devices []string) ([]types.DeviceMapping, error) {
315-
var devs []types.DeviceMapping
316-
for _, devPair := range devices {
317-
pair := strings.Split(strings.TrimSpace(devPair), ":")
318-
if len(pair) == 2 {
319-
devs = append(devs, types.DeviceMapping{
320-
PathOnHost: pair[0],
321-
PathInContainer: pair[1],
322-
CgroupPermissions: "rwm",
323-
})
324-
} else if len(pair) == 3 {
325-
if len(pair[2]) == 0 || len(pair[2]) > 3 {
326-
return nil, log.NewError("incorrect device cgroup permissions format")
327-
}
328-
for i := 0; i < len(pair[2]); i++ {
329-
if (pair[2])[i] != "w"[0] && (pair[2])[i] != "r"[0] && (pair[2])[i] != "m"[0] {
330-
return nil, log.NewError("incorrect device cgroup permissions format")
331-
}
332-
}
333-
334-
devs = append(devs, types.DeviceMapping{
335-
PathOnHost: pair[0],
336-
PathInContainer: pair[1],
337-
CgroupPermissions: pair[2],
338-
})
339-
} else {
340-
return nil, log.NewError("incorrect device configuration format")
341-
}
342-
}
343-
return devs, nil
344-
}
345-
346-
func parseMountPoints(mps []string) ([]types.MountPoint, error) {
347-
var mountPoints []types.MountPoint
348-
var mountPoint types.MountPoint
349-
for _, mp := range mps {
350-
mount := strings.Split(strings.TrimSpace(mp), ":")
351-
// if propagation mode is omitted, "rprivate" is set as default
352-
if len(mount) < 2 || len(mount) > 3 {
353-
return nil, log.NewError("Incorrect number of parameters of the mount point")
354-
}
355-
mountPoint = types.MountPoint{
356-
Destination: mount[1],
357-
Source: mount[0],
358-
}
359-
if len(mount) == 2 {
360-
log.Debug("propagation mode ommited - setting default to rprivate")
361-
mountPoint.PropagationMode = types.RPrivatePropagationMode
362-
} else {
363-
mountPoint.PropagationMode = mount[2]
364-
}
365-
mountPoints = append(mountPoints, mountPoint)
366-
}
367-
return mountPoints, nil
368-
}
369-
370-
func parsePortMappings(mappings []string) ([]types.PortMapping, error) {
371-
var (
372-
portMappings []types.PortMapping
373-
err error
374-
protocol string
375-
containerPort int64
376-
hostIP string
377-
hostPort int64
378-
hostPortEnd int64
379-
)
380-
381-
for _, mapping := range mappings {
382-
mappingWithProto := strings.Split(strings.TrimSpace(mapping), "/")
383-
mapping = mappingWithProto[0]
384-
if len(mappingWithProto) == 2 {
385-
// port is specified, e.g.80:80/tcp
386-
protocol = mappingWithProto[1]
387-
}
388-
addressAndPorts := strings.Split(strings.TrimSpace(mapping), ":")
389-
hostPortIdx := 0 // if host ip not set
390-
if len(addressAndPorts) == 2 {
391-
// host address not specified, e.g. 80:80
392-
} else if len(addressAndPorts) == 3 {
393-
hostPortIdx = 1
394-
hostIP = addressAndPorts[0]
395-
validIP := net.ParseIP(hostIP)
396-
if validIP == nil {
397-
return nil, log.NewError("Incorrect host ip port mapping configuration")
398-
}
399-
hostPort, err = strconv.ParseInt(addressAndPorts[1], 10, 32)
400-
containerPort, err = strconv.ParseInt(addressAndPorts[2], 10, 32)
401-
402-
} else {
403-
return nil, log.NewError("Incorrect port mapping configuration")
404-
}
405-
406-
hostPortWithRange := strings.Split(strings.TrimSpace(addressAndPorts[hostPortIdx]), "-")
407-
if len(hostPortWithRange) == 2 {
408-
hostPortEnd, err = strconv.ParseInt(hostPortWithRange[1], 10, 32)
409-
if err != nil {
410-
return nil, log.NewError("Incorrect host range port mapping configuration")
411-
}
412-
hostPort, err = strconv.ParseInt(hostPortWithRange[0], 10, 32)
413-
} else {
414-
hostPort, err = strconv.ParseInt(addressAndPorts[hostPortIdx], 10, 32)
415-
}
416-
containerPort, err = strconv.ParseInt(addressAndPorts[hostPortIdx+1], 10, 32)
417-
if err != nil {
418-
return nil, log.NewError("Incorrect port mapping configuration, parsing error")
419-
}
420-
421-
portMappings = append(portMappings, types.PortMapping{
422-
Proto: protocol,
423-
ContainerPort: uint16(containerPort),
424-
HostIP: hostIP,
425-
HostPort: uint16(hostPort),
426-
HostPortEnd: uint16(hostPortEnd),
427-
})
428-
}
429-
return portMappings, nil
430-
431-
}

containerm/cli/cli_command_ctrs_create_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,12 +701,12 @@ func (createTc *createCommandTest) mockExecCreateDevicesWithPrivileged(args []st
701701

702702
func (createTc *createCommandTest) mockExecCreateDevicesErrConfigFormat(args []string) error {
703703
createTc.mockClient.EXPECT().Create(gomock.AssignableToTypeOf(context.Background()), gomock.Any()).Times(0)
704-
return log.NewError("incorrect device configuration format")
704+
return log.NewError("incorrect configuration value for device mapping")
705705
}
706706

707707
func (createTc *createCommandTest) mockExecCreateDevicesErrCgroupFormat(args []string) error {
708708
createTc.mockClient.EXPECT().Create(gomock.AssignableToTypeOf(context.Background()), gomock.Any()).Times(0)
709-
return log.NewError("incorrect device cgroup permissions format")
709+
return log.NewError("incorrect cgroup permissions format for device mapping")
710710
}
711711

712712
func (createTc *createCommandTest) mockExecCreateWithMountPoints(args []string) error {
@@ -878,7 +878,7 @@ func (createTc *createCommandTest) mockExecCreateWithPortsIncorrectPortsConfig(a
878878

879879
func (createTc *createCommandTest) mockExecCreateWithPortsIncorrectPortsConfigParseErr(args []string) error {
880880
createTc.mockClient.EXPECT().Create(gomock.AssignableToTypeOf(context.Background()), gomock.Any()).Times(0)
881-
return log.NewError("Incorrect port mapping configuration, parsing error")
881+
return log.NewError("Incorrect container port mapping configuration")
882882
}
883883

884884
func (createTc *createCommandTest) mockExecCreateWithPortsIncorrectHostRange(args []string) error {

containerm/daemon/daemon_command.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ func setupCommandFlags(cmd *cobra.Command) {
8585
flagSet.StringVar(&cfg.ThingsConfig.ThingsMetaPath, "things-home-dir", cfg.ThingsConfig.ThingsMetaPath, "Specify the home directory for the things container management service persistent storage")
8686
flagSet.StringSliceVar(&cfg.ThingsConfig.Features, "things-features", cfg.ThingsConfig.Features, "Specify the desired Ditto features that will be registered for the containers Ditto thing")
8787

88+
// init update agent
89+
flagSet.BoolVar(&cfg.UpdateAgentConfig.UpdateAgentEnable, "ua-enable", cfg.UpdateAgentConfig.UpdateAgentEnable, "Enable the update agent for containers")
90+
flagSet.StringVar(&cfg.UpdateAgentConfig.DomainName, "ua-domain", cfg.UpdateAgentConfig.DomainName, "Specify the domain name for the containers update agent")
91+
flagSet.StringSliceVar(&cfg.UpdateAgentConfig.SystemContainers, "ua-system-containers", cfg.UpdateAgentConfig.SystemContainers, "Specify the list of system containers which shall be skipped during update process by the update agent")
92+
flagSet.BoolVar(&cfg.UpdateAgentConfig.VerboseInventoryReport, "ua-verbose-inventory-report", cfg.UpdateAgentConfig.VerboseInventoryReport, "Enables verbose reporting of current inventory of containers by the update agent")
93+
8894
// init local communication flags
8995
flagSet.StringVar(&cfg.LocalConnection.BrokerURL, "conn-broker-url", cfg.LocalConnection.BrokerURL, "Specify the MQTT broker URL to connect to")
9096
flagSet.StringVar(&cfg.LocalConnection.KeepAlive, "conn-keep-alive", cfg.LocalConnection.KeepAlive, "Specify the keep alive duration for the MQTT requests as duration string")

containerm/daemon/daemon_config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ type config struct {
3535

3636
ThingsConfig *thingsConfig `json:"things,omitempty"`
3737

38+
UpdateAgentConfig *updateAgentConfig `json:"update_agent,omitempty"`
39+
3840
LocalConnection *localConnectionConfig `json:"connection,omitempty"`
3941
}
4042

@@ -151,6 +153,14 @@ type thingsConfig struct {
151153
ThingsConnectionConfig *thingsConnectionConfig `json:"connection,omitempty"`
152154
}
153155

156+
// things client configuration
157+
type updateAgentConfig struct {
158+
UpdateAgentEnable bool `json:"enable,omitempty"`
159+
DomainName string `json:"domain,omitempty"`
160+
SystemContainers []string `json:"system_containers,omitempty"`
161+
VerboseInventoryReport bool `json:"verbose_inventory_report,omitempty"`
162+
}
163+
154164
// local connection config
155165
type localConnectionConfig struct {
156166
BrokerURL string `json:"broker_url,omitempty"`

containerm/daemon/daemon_config_default.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ const (
9595
deploymentModeDefault = string(deployment.UpdateMode)
9696
deploymentMetaPathDefault = managerMetaPathDefault
9797
deploymentCtrPathDefault = "/etc/container-management/containers"
98+
99+
// default update agent config
100+
updateAgentEnableDefault = false
101+
updateAgentDomainDefault = "containers"
102+
updateAgentVerboseInventoryReportDefault = false
98103
)
99104

100105
var (
@@ -178,6 +183,12 @@ func getDefaultInstance() *config {
178183
DeploymentMetaPath: deploymentMetaPathDefault,
179184
DeploymentCtrPath: deploymentCtrPathDefault,
180185
},
186+
UpdateAgentConfig: &updateAgentConfig{
187+
UpdateAgentEnable: updateAgentEnableDefault,
188+
DomainName: updateAgentDomainDefault,
189+
SystemContainers: []string{}, // no system containers by defaults
190+
VerboseInventoryReport: updateAgentVerboseInventoryReportDefault,
191+
},
181192
LocalConnection: &localConnectionConfig{
182193
BrokerURL: connectionBrokerURLDefault,
183194
KeepAlive: connectionKeepAliveDefault,

containerm/daemon/daemon_config_util.go

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/eclipse-kanto/container-management/containerm/network"
2929
"github.com/eclipse-kanto/container-management/containerm/server"
3030
"github.com/eclipse-kanto/container-management/containerm/things"
31+
"github.com/eclipse-kanto/container-management/containerm/updateagent"
3132
"github.com/spf13/pflag"
3233
)
3334

@@ -117,15 +118,6 @@ func extractThingsOptions(daemonConfig *config) []things.ContainerThingsManagerO
117118
}
118119
}
119120

120-
parseDuration := func(duration, defaultDuration string) time.Duration {
121-
d, err := time.ParseDuration(duration)
122-
if err != nil {
123-
log.Warn("Invalid Duration string: %s", duration)
124-
d, _ = time.ParseDuration(defaultDuration)
125-
}
126-
return d
127-
}
128-
129121
thingsOpts = append(thingsOpts,
130122
things.WithMetaPath(daemonConfig.ThingsConfig.ThingsMetaPath),
131123
things.WithFeatures(daemonConfig.ThingsConfig.Features),
@@ -145,6 +137,30 @@ func extractThingsOptions(daemonConfig *config) []things.ContainerThingsManagerO
145137
return thingsOpts
146138
}
147139

140+
func extractUpdateAgentOptions(daemonConfig *config) []updateagent.ContainersUpdateAgentOpt {
141+
updateAgentOpts := []updateagent.ContainersUpdateAgentOpt{}
142+
updateAgentOpts = append(updateAgentOpts,
143+
updateagent.WithDomainName(daemonConfig.UpdateAgentConfig.DomainName),
144+
updateagent.WithSystemContainers(daemonConfig.UpdateAgentConfig.SystemContainers),
145+
updateagent.WithVerboseInventoryReport(daemonConfig.UpdateAgentConfig.VerboseInventoryReport),
146+
147+
updateagent.WithConnectionBroker(daemonConfig.LocalConnection.BrokerURL),
148+
updateagent.WithConnectionKeepAlive(parseDuration(daemonConfig.LocalConnection.KeepAlive, connectionKeepAliveDefault)),
149+
updateagent.WithConnectionDisconnectTimeout(parseDuration(daemonConfig.LocalConnection.DisconnectTimeout, connectionDisconnectTimeoutDefault)),
150+
updateagent.WithConnectionClientUsername(daemonConfig.LocalConnection.ClientUsername),
151+
updateagent.WithConnectionClientPassword(daemonConfig.LocalConnection.ClientPassword),
152+
updateagent.WithConnectionConnectTimeout(parseDuration(daemonConfig.LocalConnection.ConnectTimeout, connectTimeoutTimeoutDefault)),
153+
updateagent.WithConnectionAcknowledgeTimeout(parseDuration(daemonConfig.LocalConnection.AcknowledgeTimeout, acknowledgeTimeoutDefault)),
154+
updateagent.WithConnectionSubscribeTimeout(parseDuration(daemonConfig.LocalConnection.SubscribeTimeout, subscribeTimeoutDefault)),
155+
updateagent.WithConnectionUnsubscribeTimeout(parseDuration(daemonConfig.LocalConnection.UnsubscribeTimeout, unsubscribeTimeoutDefault)),
156+
)
157+
transport := daemonConfig.LocalConnection.Transport
158+
if transport != nil {
159+
updateAgentOpts = append(updateAgentOpts, updateagent.WithTLSConfig(transport.RootCA, transport.ClientCert, transport.ClientKey))
160+
}
161+
return updateAgentOpts
162+
}
163+
148164
func extractDeploymentMgrOptions(daemonConfig *config) []deployment.Opt {
149165
return []deployment.Opt{
150166
deployment.WithMode(daemonConfig.DeploymentManagerConfig.DeploymentMode),
@@ -218,6 +234,9 @@ func dumpConfiguration(configInstance *config) {
218234
// dump things client config
219235
dumpThingsClient(configInstance)
220236

237+
// dump update agent config
238+
dumpUpdateAgent(configInstance)
239+
221240
// dump deployment manager config
222241
dumpDeploymentManager(configInstance)
223242

@@ -335,6 +354,17 @@ func dumpThingsClient(configInstance *config) {
335354
}
336355
}
337356

357+
func dumpUpdateAgent(configInstance *config) {
358+
if configInstance.UpdateAgentConfig != nil {
359+
log.Debug("[daemon_cfg][ua-enable] : %v", configInstance.UpdateAgentConfig.UpdateAgentEnable)
360+
if configInstance.UpdateAgentConfig.UpdateAgentEnable {
361+
log.Debug("[daemon_cfg][ua-domain] : %s", configInstance.UpdateAgentConfig.DomainName)
362+
log.Debug("[daemon_cfg][ua-system-containers] : %s", configInstance.UpdateAgentConfig.SystemContainers)
363+
log.Debug("[daemon_cfg][ua-verbose-inventory-report] : %v", configInstance.UpdateAgentConfig.VerboseInventoryReport)
364+
}
365+
}
366+
}
367+
338368
func dumpDeploymentManager(configInstance *config) {
339369
if configInstance.DeploymentManagerConfig != nil {
340370
log.Debug("[daemon_cfg][deployment-enable] : %v", configInstance.DeploymentManagerConfig.DeploymentEnable)
@@ -428,3 +458,12 @@ func applyInsecureRegistryConfig(registriesConfig map[string]*ctr.RegistryConfig
428458
}
429459
return res
430460
}
461+
462+
func parseDuration(duration, defaultDuration string) time.Duration {
463+
d, err := time.ParseDuration(duration)
464+
if err != nil {
465+
log.Warn("Invalid Duration string: %s", duration)
466+
d, _ = time.ParseDuration(defaultDuration)
467+
}
468+
return d
469+
}

0 commit comments

Comments
 (0)