pkg/template/archive/create.go (116 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you 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 archive import ( "archive/tar" "compress/gzip" "errors" "fmt" "io" "io/fs" "github.com/gobwas/glob" "go.uber.org/zap" "github.com/elastic/harp/pkg/sdk/log" "github.com/elastic/harp/pkg/sdk/types" ) // Create an archive from given options to the given writer. // //nolint:gocyclo,funlen // to refactor func Create(fileSystem fs.FS, w io.Writer, opts ...CreateOption) error { // Check arguments if types.IsNil(fileSystem) { return errors.New("fileSystem is nil") } if types.IsNil(w) { return errors.New("output writer is nil") } // Prepare arguments dopts := &createOptions{ rootPath: ".", includeGlobs: []string{"**"}, excludeGlobs: []string{}, } for _, o := range opts { o(dopts) } // Ensure that the root path is valid if !fs.ValidPath(dopts.rootPath) { return fmt.Errorf("root path '%s' is not a valid path", dopts.rootPath) } // Ensure the root actually exists before trying to tar it rootFi, err := fs.Stat(fileSystem, dopts.rootPath) if err != nil { return fmt.Errorf("unable to tar files: %w", err) } if !rootFi.IsDir() { return errors.New("root path must be a directory") } // Compile inclusion filters var includes []glob.Glob for _, f := range dopts.includeGlobs { // Try to compile glob filter. filter, err := glob.Compile(f) if err != nil { return fmt.Errorf("unable to compile glob filter '%s' for inclusion: %w", f, err) } // Add to explusion filters. includes = append(includes, filter) } // Compile explusion filters var excludes []glob.Glob for _, f := range dopts.excludeGlobs { // Try to compile glob filter. filter, err := glob.Compile(f) if err != nil { return fmt.Errorf("unable to compile glob filter '%s' for exclusion: %w", f, err) } // Add to explusion filters. excludes = append(excludes, filter) } // Create writer chain. zr := gzip.NewWriter(w) tw := tar.NewWriter(zr) // walk through every file in the folder if errWalk := fs.WalkDir(fileSystem, dopts.rootPath, func(file string, dirEntry fs.DirEntry, errIn error) error { // return on any error if errIn != nil { return errIn } // ignore invalid file path if !fs.ValidPath(file) { log.Bg().Debug("ignoring invalid path file ...", zap.String("file", file)) return nil } // Process inclusions keep := false for _, f := range includes { if f.Match(file) { keep = true } } for _, f := range excludes { if f.Match(file) { keep = false } } if !keep { // Ignore this file. log.Bg().Debug("ignoring file ...", zap.String("file", file)) return nil } // Get FileInfo fi, err := dirEntry.Info() if err != nil { return fmt.Errorf("unable to retrieve fileInfo for '%s': %w", file, err) } log.Bg().Info("Add file to archive ...", zap.String("file", file)) // generate tar header header, err := tar.FileInfoHeader(fi, file) if err != nil { return fmt.Errorf("unable to create TAR File header: %w", err) } // must provide real name header.Name = file header.Size = fi.Size() header.Mode = int64(fi.Mode()) header.ModTime = fi.ModTime() // write header if err := tw.WriteHeader(header); err != nil { return err } // if not a dir, write file content if !fi.IsDir() { data, err := fileSystem.Open(file) if err != nil { return err } if _, err := io.Copy(tw, io.LimitReader(data, 25*1024*1024)); err != nil { return err } } // No error return nil }); errWalk != nil { return fmt.Errorf("fail to walk folders for archive compression: %w", errWalk) } // produce tar if err := tw.Close(); err != nil { return err } // produce gzip if err := zr.Close(); err != nil { return err } // No error return nil }