pkg/files/file_reader_writer.go (67 lines of code) (raw):
// Copyright 2018 Google LLC
//
// Licensed 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
//
// https://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 files
import (
"context"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
bundle "github.com/GoogleCloudPlatform/k8s-cluster-bundle/pkg/apis/bundle/v1alpha1"
)
// FileWriter is an interface for writing files. This interface is
// used by the export and patch commands in this package. There is a fake
// implementation in the testing package.
type FileWriter interface {
// WriteFile writes a Component or Bundle to the given file path.
WriteFile(ctx context.Context, path string, bytes []byte, permissions os.FileMode) error
}
// LocalFileSystemWriter implements the ComponentWriter interface and writes
// apps to the local filesystem.
type LocalFileSystemWriter struct{}
// WriteFile writes a file to disk.
func (*LocalFileSystemWriter) WriteFile(_ context.Context, path string, bytes []byte, permissions os.FileMode) error {
return ioutil.WriteFile(path, bytes, permissions)
}
// Ensure the LocalFileSystemReader fulfills the contract
var _ FileWriter = &LocalFileSystemWriter{}
// FileReader is a common command interface for reading files.
type FileReader interface {
ReadFile(ctx context.Context, path string) ([]byte, error)
}
// LocalFileSystemReader implements the FileReader interface and reads
// files from the local filesystem.
type LocalFileSystemReader struct{}
// ReadFile reads a file from disk.
func (r *LocalFileSystemReader) ReadFile(_ context.Context, path string) ([]byte, error) {
return ioutil.ReadFile(path)
}
// Ensure the LocalFileSystemReader fulfills the contract
var _ FileReader = &LocalFileSystemReader{}
// FileReaderWriter combines both file reading and file writing.
type FileReaderWriter interface {
FileReader
FileWriter
}
// LocalFileSystemReaderWriter combines both local file system file reading and
// writing.
type LocalFileSystemReaderWriter struct {
LocalFileSystemReader
LocalFileSystemWriter
}
// FileObjReader provides a generic file-reading interface for reading file
// objects
type FileObjReader interface {
ReadFileObj(ctx context.Context, file bundle.File) ([]byte, error)
}
// LocalFileObjReader is File object reader that defers to another FileReader that
// reads based on paths.
type LocalFileObjReader struct {
// WorkingDir specifies a working directory override. This is necessary
// because paths for inlined files are specified relative to the bundle, not
// the working directory of the user.
//
// TODO(kashomon): Get rid of this. Path manipulation should happen in the
// downstream libraries.
WorkingDir string
// Rdr is a FileReader object.
Rdr FileReader
}
// ReadFileObj reads a file object from the local filesystem by deferring to a
// local file reader.
func (r *LocalFileObjReader) ReadFileObj(ctx context.Context, file bundle.File) ([]byte, error) {
if file.URL == "" {
return nil, fmt.Errorf("file %v was specified but no file url was provided", file)
}
path, err := r.extractPath(file.URL)
if err != nil {
return nil, fmt.Errorf("file %v path could not be parsed: %v", file, err)
}
return r.Rdr.ReadFile(ctx, path)
}
// extractPath extracts a final path from a URL preserving legacy behavior
// while we figure out how to make things consistent.
//
// TODO(kashomon): Get rid of this. Path manipulation should happen in the
// downstream libraries.
func (r *LocalFileObjReader) extractPath(fileURL string) (string, error) {
u, err := url.Parse(fileURL)
if err != nil {
return "", err
}
if u.Host != "" {
return "", fmt.Errorf("unexpected host in url %q", u.Host)
}
if scheme := URLScheme(u.Scheme); scheme != EmptyScheme && scheme != FileScheme {
return "", fmt.Errorf("unsupported scheme %q (local object reader supports only 'file://' scheme)", scheme)
}
if u.Scheme == "file" || !filepath.IsAbs(u.Path) {
u.Path = filepath.Clean(filepath.Join(r.WorkingDir, u.Path))
}
return u.Path, nil
}