Skip to content

Commit 4e97bdc

Browse files
authored
Add nats Stash addon (#2)
* Add NATS Stash Addon Signed-off-by: hmsayem <hmsayem@appscode.com> * Add TLS Authentication Signed-off-by: hmsayem <hmsayem@appscode.com> * Add Token Authentication Signed-off-by: hmsayem <hmsayem@appscode.com> * Add JWT Authentication Signed-off-by: hmsayem <hmsayem@appscode.com> * Update TLS Signed-off-by: hmsayem <hmsayem@appscode.com> * Add Nkey authentication Signed-off-by: hmsayem <hmsayem@appscode.com> * Add client certificate authentication Signed-off-by: hmsayem <hmsayem@appscode.com> * Update code Signed-off-by: hmsayem <hmsayem@appscode.com> * Add overwrite flag Signed-off-by: hmsayem <hmsayem@appscode.com> * Update args Signed-off-by: hmsayem <hmsayem@appscode.com> * Update flag usage Signed-off-by: hmsayem <hmsayem@appscode.com>
1 parent 48941bd commit 4e97bdc

File tree

8 files changed

+305
-89
lines changed

8 files changed

+305
-89
lines changed

Dockerfile.dbg

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
FROM debian:bullseye
15+
FROM alpine
1616

1717
ENV DEBIAN_FRONTEND noninteractive
1818
ENV DEBCONF_NONINTERACTIVE_SEEN true
1919

2020
RUN set -x \
21-
&& apt-get update \
22-
&& apt-get install -y --no-install-recommends apt-transport-https ca-certificates curl bzip2 tar
21+
&& apk update \
22+
&& apk add ca-certificates curl bzip2 tar
2323

2424
RUN set -x \
2525
&& curl -fsSL -o restic.bz2 https://github.com/stashed/restic/releases/download/v{RESTIC_VER}/restic_{RESTIC_VER}_{ARG_OS}_{ARG_ARCH}.bz2 \
@@ -35,11 +35,13 @@ ENV DEBIAN_FRONTEND noninteractive
3535
ENV DEBCONF_NONINTERACTIVE_SEEN true
3636

3737
RUN set -x \
38-
&& apt-get update \
39-
&& apt-get install -y --no-install-recommends ca-certificates \
38+
&& apk update \
39+
&& apk add ca-certificates \
4040
&& rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man /tmp/*
4141

4242
COPY --from=0 restic /bin/restic
4343
COPY bin/{ARG_OS}_{ARG_ARCH}/{ARG_BIN} /{ARG_BIN}
4444

45+
USER nobody
46+
4547
ENTRYPOINT ["/{ARG_BIN}"]

Dockerfile.in

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
FROM debian:bullseye
15+
FROM alpine
1616

1717
ENV DEBIAN_FRONTEND noninteractive
1818
ENV DEBCONF_NONINTERACTIVE_SEEN true
1919

2020
RUN set -x \
21-
&& apt-get update \
22-
&& apt-get install -y --no-install-recommends apt-transport-https ca-certificates curl bzip2 tar
21+
&& apk update \
22+
&& apk add --no-cache ca-certificates curl bzip2 tar
2323

2424
RUN set -x \
2525
&& curl -fsSL -o restic.bz2 https://github.com/stashed/restic/releases/download/v{RESTIC_VER}/restic_{RESTIC_VER}_{ARG_OS}_{ARG_ARCH}.bz2 \
@@ -34,8 +34,8 @@ ENV DEBIAN_FRONTEND noninteractive
3434
ENV DEBCONF_NONINTERACTIVE_SEEN true
3535

3636
RUN set -x \
37-
&& apt-get update \
38-
&& apt-get install -y --no-install-recommends ca-certificates \
37+
&& apk update \
38+
&& apk add ca-certificates \
3939
&& rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man /tmp/*
4040

4141
COPY --from=0 /restic /bin/restic

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ BIN_PLATFORMS := $(DOCKER_PLATFORMS)
5858
OS := $(if $(GOOS),$(GOOS),$(shell go env GOOS))
5959
ARCH := $(if $(GOARCH),$(GOARCH),$(shell go env GOARCH))
6060

61-
BASEIMAGE_PROD ?= natsio/natsbox:0.6.0
62-
BASEIMAGE_DBG ?= natsio/natsbox:0.6.0
61+
BASEIMAGE_PROD ?= natsio/nats-box:0.6.0
62+
BASEIMAGE_DBG ?= natsio/nats-box:0.6.0
6363

6464
IMAGE := $(REGISTRY)/$(BIN)
6565
VERSION_PROD := $(VERSION)
File renamed without changes.
File renamed without changes.

pkg/backup.go

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ package pkg
1818

1919
import (
2020
"context"
21+
"encoding/json"
22+
"io/ioutil"
2123
"path/filepath"
22-
"strconv"
23-
"strings"
2424

2525
api_v1beta1 "stash.appscode.dev/apimachinery/apis/stash/v1beta1"
2626
stash "stash.appscode.dev/apimachinery/client/clientset/versioned"
@@ -33,6 +33,7 @@ import (
3333
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3434
"k8s.io/client-go/kubernetes"
3535
"k8s.io/client-go/tools/clientcmd"
36+
"k8s.io/klog/v2"
3637
appcatalog "kmodules.xyz/custom-resources/apis/appcatalog/v1alpha1"
3738
appcatalog_cs "kmodules.xyz/custom-resources/client/clientset/versioned"
3839
v1 "kmodules.xyz/offshoot-api/api/v1"
@@ -49,8 +50,7 @@ func NewCmdBackup() *cobra.Command {
4950
EnableCache: false,
5051
},
5152
backupOptions: restic.BackupOptions{
52-
Host: restic.DefaultHost,
53-
StdinFileName: NATSDumpFile,
53+
Host: restic.DefaultHost,
5454
},
5555
}
5656
)
@@ -110,15 +110,14 @@ func NewCmdBackup() *cobra.Command {
110110
}
111111

112112
return nil
113-
114113
},
115114
}
116115

117116
cmd.Flags().StringVar(&opt.natsArgs, "nats-args", opt.natsArgs, "Additional arguments")
118117
cmd.Flags().Int32Var(&opt.waitTimeout, "wait-timeout", opt.waitTimeout, "Time limit to wait for the database to be ready")
119118

120119
cmd.Flags().StringVar(&masterURL, "master", masterURL, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
121-
cmd.Flags().StringVar(&kubeconfigPath, "kubeconfig", kubeconfigPath, "Path to kubeconfig file with authorization information (the master location is set by the master flag).")
120+
cmd.Flags().StringVar(&kubeconfigPath, "kubeconfig", kubeconfigPath, "Path to kubeconfig file with authorization information (the master location is set by the master flag)")
122121
cmd.Flags().StringVar(&opt.namespace, "namespace", "default", "Namespace of Backup/Restore Session")
123122
cmd.Flags().StringVar(&opt.backupSessionName, "backupsession", opt.backupSessionName, "Name of the Backup Session")
124123
cmd.Flags().StringVar(&opt.appBindingName, "appbinding", opt.appBindingName, "Name of the app binding")
@@ -134,7 +133,6 @@ func NewCmdBackup() *cobra.Command {
134133
cmd.Flags().Int64Var(&opt.setupOptions.MaxConnections, "max-connections", opt.setupOptions.MaxConnections, "Specify maximum concurrent connections for GCS, Azure and B2 backend")
135134

136135
cmd.Flags().StringVar(&opt.backupOptions.Host, "hostname", opt.backupOptions.Host, "Name of the host machine")
137-
138136
cmd.Flags().Int64Var(&opt.backupOptions.RetentionPolicy.KeepLast, "retention-keep-last", opt.backupOptions.RetentionPolicy.KeepLast, "Specify value for retention strategy")
139137
cmd.Flags().Int64Var(&opt.backupOptions.RetentionPolicy.KeepHourly, "retention-keep-hourly", opt.backupOptions.RetentionPolicy.KeepHourly, "Specify value for retention strategy")
140138
cmd.Flags().Int64Var(&opt.backupOptions.RetentionPolicy.KeepDaily, "retention-keep-daily", opt.backupOptions.RetentionPolicy.KeepDaily, "Specify value for retention strategy")
@@ -145,8 +143,9 @@ func NewCmdBackup() *cobra.Command {
145143
cmd.Flags().BoolVar(&opt.backupOptions.RetentionPolicy.Prune, "retention-prune", opt.backupOptions.RetentionPolicy.Prune, "Specify whether to prune old snapshot data")
146144
cmd.Flags().BoolVar(&opt.backupOptions.RetentionPolicy.DryRun, "retention-dry-run", opt.backupOptions.RetentionPolicy.DryRun, "Specify whether to test retention policy without deleting actual data")
147145

146+
cmd.Flags().StringVar(&opt.interimDataDir, "interim-data-dir", opt.interimDataDir, "Directory where the targeted data will be stored temporarily before uploading to the backend")
148147
cmd.Flags().StringVar(&opt.outputDir, "output-dir", opt.outputDir, "Directory where output.json file will be written (keep empty if you don't need to write output in file)")
149-
148+
cmd.Flags().StringSliceVar(&opt.streams, "streams", opt.streams, "List of streams to backup. Keep empty to backup all streams")
150149
return cmd
151150
}
152151

@@ -177,50 +176,104 @@ func (opt *natsOptions) backupNATS(targetRef api_v1beta1.TargetRef) (*restic.Bac
177176
if err != nil {
178177
return nil, err
179178
}
180-
181179
// get app binding
182180
appBinding, err := opt.catalogClient.AppcatalogV1alpha1().AppBindings(opt.namespace).Get(context.TODO(), opt.appBindingName, metav1.GetOptions{})
183181
if err != nil {
184182
return nil, err
185183
}
186-
187-
// init restic wrapper
188-
resticWrapper, err := restic.NewResticWrapper(opt.setupOptions)
184+
// clear directory
185+
klog.Infoln("Cleaning up temporary data directory directory: ", opt.interimDataDir)
186+
if err := clearDir(opt.interimDataDir); err != nil {
187+
return nil, err
188+
}
189+
// wait for NATS ready
190+
err = opt.waitForNATSReady(appBinding)
189191
if err != nil {
190192
return nil, err
191193
}
192194

195+
// run separate shell to perform backup
196+
backupShell := NewSessionWrapper()
197+
backupShell.ShowCMD = true
193198
// set access credentials
194-
err = opt.setCredentials(resticWrapper, appBinding)
199+
err = opt.setCredentials(backupShell, appBinding)
195200
if err != nil {
196201
return nil, err
197202
}
198203

199-
// setup pipe command
200-
backupCmd := restic.Command{
201-
Name: NATSBackupCMD,
202-
Args: []interface{}{
203-
"-host", appBinding.Spec.ClientConfig.Service.Name,
204-
},
205-
}
206-
for _, arg := range strings.Fields(opt.natsArgs) {
207-
backupCmd.Args = append(backupCmd.Args, arg)
204+
// set TLS
205+
err = opt.setTLS(backupShell, appBinding)
206+
if err != nil {
207+
return nil, err
208208
}
209209

210-
// if port is specified, append port in the arguments
211-
if appBinding.Spec.ClientConfig.Service.Port != 0 {
212-
backupCmd.Args = append(backupCmd.Args, "-port", strconv.Itoa(int(appBinding.Spec.ClientConfig.Service.Port)))
210+
backupArgs := []interface{}{
211+
"stream",
212+
"backup",
213+
"--server", appBinding.Spec.ClientConfig.Service.Name,
213214
}
214215

215-
// wait for DB ready
216-
err = opt.waitForDBReady(appBinding)
216+
streams, err := opt.getStreams(backupShell, appBinding)
217217
if err != nil {
218218
return nil, err
219219
}
220220

221-
// add backup command in the pipeline
222-
opt.backupOptions.StdinPipeCommands = append(opt.backupOptions.StdinPipeCommands, backupCmd)
221+
for i := range streams {
222+
args := append(backupArgs, streams[i], filepath.Join(opt.interimDataDir, streams[i]))
223+
backupShell.Command(NATSCMD, args...)
224+
if err := backupShell.Run(); err != nil {
225+
return nil, err
226+
}
227+
}
228+
229+
// data snapshot has been stored in the interim data dir. Now, we will backup this directory using Stash.
230+
opt.backupOptions.BackupPaths = []string{opt.interimDataDir}
231+
232+
// init restic wrapper
233+
resticWrapper, err := restic.NewResticWrapper(opt.setupOptions)
234+
if err != nil {
235+
return nil, err
236+
}
223237

224238
// Run backup
225239
return resticWrapper.RunBackup(opt.backupOptions, targetRef)
226240
}
241+
242+
func (opt *natsOptions) getStreams(sh *SessionWrapper, appBinding *appcatalog.AppBinding) ([]string, error) {
243+
if len(opt.streams) == 0 {
244+
streamArgs := []interface{}{
245+
"stream",
246+
"ls",
247+
"--json",
248+
"--server", appBinding.Spec.ClientConfig.Service.Name,
249+
}
250+
251+
sh.Command(NATSCMD, streamArgs...)
252+
err := sh.WriteStdout(filepath.Join(opt.interimDataDir, NATSStreamsFile))
253+
if err != nil {
254+
return nil, err
255+
}
256+
byteStreams, err := ioutil.ReadFile(filepath.Join(opt.interimDataDir, NATSStreamsFile))
257+
if err != nil {
258+
return nil, err
259+
}
260+
var streams []string
261+
err = json.Unmarshal(byteStreams, &streams)
262+
if err != nil {
263+
return nil, err
264+
}
265+
return streams, nil
266+
267+
} else {
268+
byteStreams, err := json.Marshal(opt.streams)
269+
if err != nil {
270+
return nil, err
271+
}
272+
273+
err = ioutil.WriteFile(filepath.Join(opt.interimDataDir, NATSStreamsFile), byteStreams, 0644)
274+
if err != nil {
275+
return nil, err
276+
}
277+
return opt.streams, nil
278+
}
279+
}

0 commit comments

Comments
 (0)