The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/5299

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===

From 7f3fac7d14e94a834ce652df514443ceea326dcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Wed, 21 Nov 2018 15:53:56 -0500
Subject: [PATCH 1/4] lxd/snapshots: Always use snap%d as pattern
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 doc/containers.md | 2 +-
 lxd/container.go  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/doc/containers.md b/doc/containers.md
index a9d0014b32..1ffa09448d 100644
--- a/doc/containers.md
+++ b/doc/containers.md
@@ -79,7 +79,7 @@ security.syscalls.blacklist\_default    | boolean   | true    
          | no
 security.syscalls.whitelist             | string    | -                 | no   
         | container\_syscall\_filtering        | A '\n' separated list of 
syscalls to whitelist (mutually exclusive with security.syscalls.blacklist\*)
 snapshots.schedule                      | string    | -                 | no   
         | snapshot\_scheduling                 | Cron expression (`<minute> 
<hour> <dom> <month> <dow>`)
 snapshots.schedule.stopped              | bool      | false             | no   
         | snapshot\_scheduling                 | Controls whether or not 
stopped containers are to be snapshoted automatically
-snapshots.pattern                       | string    | auto-snapshot     | no   
         | snapshot\_scheduling                 | Pongo2 template string which 
represents the snapshot name
+snapshots.pattern                       | string    | snap%d            | no   
         | snapshot\_scheduling                 | Pongo2 template string which 
represents the snapshot name (used for scheduled snapshots and unnamed 
snapshots)
 user.\*                                 | string    | -                 | n/a  
         | -                                    | Free form user key/value 
storage (can be used in search)
 
 The following volatile keys are currently internally used by LXD:
diff --git a/lxd/container.go b/lxd/container.go
index efab279ddc..5a3a9b48ca 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1639,7 +1639,7 @@ func autoCreateContainerSnapshots(ctx context.Context, d 
*Daemon) error {
 
                ch := make(chan struct{})
                go func() {
-                       snapshotName, err := 
containerDetermineNextSnapshotName(d, c, "auto-snapshot")
+                       snapshotName, err := 
containerDetermineNextSnapshotName(d, c, "snap%d")
                        if err != nil {
                                logger.Error("Error retrieving next snapshot 
name", log.Ctx{"err": err,
                                        "container": c})

From 0b47a151216f5b3d9899fbf5149bde8edeaea97c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Wed, 21 Nov 2018 15:55:36 -0500
Subject: [PATCH 2/4] lxd/snapshots: Validate pattern
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 shared/container.go | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/shared/container.go b/shared/container.go
index 47f2f75a88..cbcd2497cd 100644
--- a/shared/container.go
+++ b/shared/container.go
@@ -5,6 +5,9 @@ import (
        "regexp"
        "strconv"
        "strings"
+
+       "github.com/pkg/errors"
+       "gopkg.in/robfig/cron.v2"
 )
 
 type ContainerAction string
@@ -263,7 +266,22 @@ var KnownContainerConfigKeys = map[string]func(value 
string) error{
        "security.syscalls.blacklist":         IsAny,
        "security.syscalls.whitelist":         IsAny,
 
-       "snapshots.schedule":         IsAny,
+       "snapshots.schedule": func(value string) error {
+               if value == "" {
+                       return nil
+               }
+
+               if len(strings.Split(value, " ")) != 5 {
+                       return fmt.Errorf("Schedule must be of the form: 
<minute> <hour> <day-of-month> <month> <day-of-week>")
+               }
+
+               _, err := cron.Parse(fmt.Sprintf("* %s", value))
+               if err != nil {
+                       return errors.Wrap(err, "Error parsing schedule")
+               }
+
+               return nil
+       },
        "snapshots.schedule.stopped": IsBool,
        "snapshots.pattern":          IsAny,
 

From 58ec0b53bb94be59aa72ae2e39416771f2fe199f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Wed, 21 Nov 2018 15:56:31 -0500
Subject: [PATCH 3/4] lxd/snapshots: Don't run on startup
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/container.go | 2 --
 1 file changed, 2 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 5a3a9b48ca..9eb91043a4 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1558,8 +1558,6 @@ func autoCreateContainerSnapshotsTask(d *Daemon) 
(task.Func, task.Schedule) {
                logger.Info("Done creating scheduled container snapshots")
        }
 
-       f(context.Background())
-
        first := true
        schedule := func() (time.Duration, error) {
                interval := time.Minute

From d14578dd06122c09b051cbafe8de816a94a1a5f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Wed, 21 Nov 2018 15:56:48 -0500
Subject: [PATCH 4/4] lxd/snapshots: Only spawn operation when needed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/container.go | 127 +++++++++++++++++++++--------------------------
 1 file changed, 56 insertions(+), 71 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 9eb91043a4..272adf4068 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1539,8 +1539,54 @@ func containerCompareSnapshots(source container, target 
container) ([]container,
 
 func autoCreateContainerSnapshotsTask(d *Daemon) (task.Func, task.Schedule) {
        f := func(ctx context.Context) {
+               // Load all local containers
+               allContainers, err := containerLoadNodeAll(d.State())
+               if err != nil {
+                       logger.Error("Failed to load containers for scheduled 
snapshots", log.Ctx{"err": err})
+               }
+
+               // Figure out which need snapshotting (if any)
+               containers := []container{}
+               for _, c := range allContainers {
+                       logger.Debugf("considering: %v", c.Name())
+                       schedule := c.LocalConfig()["snapshots.schedule"]
+
+                       if schedule == "" {
+                               logger.Debugf("skipping, empty schedule")
+                               continue
+                       }
+
+                       // Extend our schedule to one that is accepted by the 
used cron parser
+                       sched, err := cron.Parse(fmt.Sprintf("* %s", schedule))
+                       if err != nil {
+                               logger.Debugf("skipping, parsing error: %v", 
err)
+                               continue
+                       }
+
+                       // Check if it's time to snapshot
+                       now := time.Now()
+                       next := sched.Next(now)
+
+                       if now.Add(time.Minute).Before(next) {
+                               logger.Debugf("skipping, %v is after %v", 
now.Add(time.Minute), next)
+                               continue
+                       }
+
+                       // Check if the container is running
+                       if 
!shared.IsTrue(c.LocalConfig()["snapshots.schedule.stopped"]) && !c.IsRunning() 
{
+                               logger.Debugf("skipping, container is stopped")
+                               continue
+                       }
+
+                       containers = append(containers, c)
+               }
+
+               if len(containers) == 0 {
+                       return
+               }
+
                opRun := func(op *operation) error {
-                       return autoCreateContainerSnapshots(ctx, d)
+                       return autoCreateContainerSnapshots(ctx, d, containers)
                }
 
                op, err := operationCreate(d.cluster, "", operationClassTask, 
db.OperationSnapshotCreate, nil, nil, opRun, nil, nil)
@@ -1573,82 +1619,21 @@ func autoCreateContainerSnapshotsTask(d *Daemon) 
(task.Func, task.Schedule) {
        return f, schedule
 }
 
-func autoCreateContainerSnapshots(ctx context.Context, d *Daemon) error {
-       containers, err := d.cluster.ContainersNodeList(db.CTypeRegular)
-       if err != nil {
-               return errors.Wrap(err, "Unable to retrieve the list of 
containers")
-       }
-
-       for _, container := range containers {
-               cId, err := d.cluster.ContainerID(container)
-               if err != nil {
-                       return errors.Wrap(err, "Error retrieving container ID")
-               }
-
-               c, err := containerLoadById(d.State(), cId)
-               if err != nil {
-                       return errors.Wrap(err, "Error loading container")
-               }
-
-               schedule := c.LocalConfig()["snapshots.schedule"]
-
-               if schedule == "" || 
(!shared.IsTrue(c.LocalConfig()["snapshots.schedule.stopped"]) && 
c.IsRunning()) {
-                       continue
-               }
-
-               if len(strings.Split(schedule, " ")) != 5 {
-                       logger.Error("Schedule must be of the form: <minute> 
<hour> <day-of-month> <month> <day-of-week>")
-                       continue
-               }
-
-               // Extend our schedule to one that is accepted by the used cron 
parser
-               sched, err := cron.Parse(fmt.Sprintf("* %s", schedule))
-               if err != nil {
-                       logger.Error("Error parsing schedule", log.Ctx{"err": 
err,
-                               "container": container, "schedule": schedule})
-                       continue
-               }
-
-               now := time.Now()
-               next := sched.Next(now).Format("2006-01-02T15:04")
-               skip := false
-
-               snapshots, err := c.Snapshots()
-               if err != nil {
-                       logger.Error("Error retrieving snapshots", 
log.Ctx{"err": err,
-                               "container": container})
-                       continue
-               }
-
-               for _, snap := range snapshots {
-                       // Check whether the container has been snapshotted 
within (at least)
-                       // the last minute.
-                       if snap.CreationDate().Format("2006-01-02T15:04") == 
next {
-                               skip = true
-                               break
-                       }
-               }
-
-               // Skip snapshotting if the container has been snapshotted 
within (at
-               // least) the last minute, or it's not time yet.
-               if skip || now.Format("2006-01-02T15:04") != next {
-                       continue
-               }
-
-               ch := make(chan struct{})
+func autoCreateContainerSnapshots(ctx context.Context, d *Daemon, containers 
[]container) error {
+       // Make the snapshots
+       for _, c := range containers {
+               ch := make(chan error)
                go func() {
                        snapshotName, err := 
containerDetermineNextSnapshotName(d, c, "snap%d")
                        if err != nil {
-                               logger.Error("Error retrieving next snapshot 
name", log.Ctx{"err": err,
-                                       "container": c})
-                               ch <- struct{}{}
+                               logger.Error("Error retrieving next snapshot 
name", log.Ctx{"err": err, "container": c})
+                               ch <- nil
                                return
                        }
 
                        snapshotName = fmt.Sprintf("%s%s%s", c.Name(), 
shared.SnapshotDelimiter, snapshotName)
 
                        args := db.ContainerArgs{
-                               Project:      c.Project(),
                                Architecture: c.Architecture(),
                                Config:       c.LocalConfig(),
                                Ctype:        db.CTypeSnapshot,
@@ -1656,16 +1641,16 @@ func autoCreateContainerSnapshots(ctx context.Context, 
d *Daemon) error {
                                Ephemeral:    c.IsEphemeral(),
                                Name:         snapshotName,
                                Profiles:     c.Profiles(),
+                               Project:      c.Project(),
                                Stateful:     false,
                        }
 
                        _, err = containerCreateAsSnapshot(d.State(), args, c)
                        if err != nil {
-                               logger.Error("Error creating snapshots", 
log.Ctx{"err": err,
-                                       "container": c})
+                               logger.Error("Error creating snapshots", 
log.Ctx{"err": err, "container": c})
                        }
 
-                       ch <- struct{}{}
+                       ch <- nil
                }()
                select {
                case <-ctx.Done():
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to