github.com/zntrio/harp/v2@v2.0.9/pkg/bundle/container.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package bundle
    19  
    20  import (
    21  	"bytes"
    22  	"compress/gzip"
    23  	"fmt"
    24  	"io"
    25  
    26  	bundlev1 "github.com/zntrio/harp/v2/api/gen/go/harp/bundle/v1"
    27  	containerv1 "github.com/zntrio/harp/v2/api/gen/go/harp/container/v1"
    28  	"github.com/zntrio/harp/v2/pkg/container"
    29  	"github.com/zntrio/harp/v2/pkg/sdk/types"
    30  )
    31  
    32  // Statistic hold bundle statistic information.
    33  type Statistic struct {
    34  	PackageCount                 uint32
    35  	CSOCompliantPackageNameCount uint32
    36  	SecretCount                  uint32
    37  }
    38  
    39  const (
    40  	bundleContentType    = "application/vnd.harp.v1.Bundle"
    41  	gzipCompressionLevel = 9
    42  )
    43  
    44  // FromContainerReader returns a Bundle extracted from a secret container.
    45  func FromContainerReader(r io.Reader) (*bundlev1.Bundle, error) {
    46  	// Check parameters
    47  	if types.IsNil(r) {
    48  		return nil, fmt.Errorf("unable to process nil reader")
    49  	}
    50  
    51  	// Load secret container
    52  	c, err := container.Load(r)
    53  	if err != nil {
    54  		return nil, fmt.Errorf("unable to load Bundle: %w", err)
    55  	}
    56  
    57  	// Delegate to bundle loader
    58  	return FromContainer(c)
    59  }
    60  
    61  // ToContainerWriter returns a Bundle packaged as a secret container.
    62  func ToContainerWriter(w io.Writer, b *bundlev1.Bundle) error {
    63  	// Check parameters
    64  	if types.IsNil(w) {
    65  		return fmt.Errorf("unable to process nil writer")
    66  	}
    67  	if b == nil {
    68  		return fmt.Errorf("unable to process nil bundle")
    69  	}
    70  
    71  	// Create a container
    72  	c, err := ToContainer(b)
    73  	if err != nil {
    74  		return fmt.Errorf("unable to wrap bundle as a container: %w", err)
    75  	}
    76  
    77  	// Prepare secret container
    78  	return container.Dump(w, c)
    79  }
    80  
    81  // FromContainer unwraps a Bundle from a secret container.
    82  func FromContainer(c *containerv1.Container) (*bundlev1.Bundle, error) {
    83  	// Check parameters
    84  	if types.IsNil(c) {
    85  		return nil, fmt.Errorf("unable to process nil container")
    86  	}
    87  
    88  	// Check headers
    89  	if types.IsNil(c.Headers) {
    90  		return nil, fmt.Errorf("unable to process nil container headers")
    91  	}
    92  	if c.Headers.ContentType != bundleContentType {
    93  		return nil, fmt.Errorf("invalid content type for Bundle loader")
    94  	}
    95  	if c.Headers.ContentEncoding != "gzip" {
    96  		return nil, fmt.Errorf("invalid content encoding for Bundle loader")
    97  	}
    98  
    99  	// Decompress bundle
   100  	zr, err := gzip.NewReader(bytes.NewReader(c.Raw))
   101  	if err != nil {
   102  		return nil, fmt.Errorf("unable to initialize compression reader")
   103  	}
   104  
   105  	// Delegate to bundle loader
   106  	return Load(zr)
   107  }
   108  
   109  // ToContainer wrpas a Bundle as a container object.
   110  func ToContainer(b *bundlev1.Bundle) (*containerv1.Container, error) {
   111  	if b == nil {
   112  		return nil, fmt.Errorf("unable to process nil bundle")
   113  	}
   114  
   115  	// Dump bundle
   116  	payload := &bytes.Buffer{}
   117  
   118  	// Compress with gzip
   119  	zw, errGz := gzip.NewWriterLevel(payload, gzipCompressionLevel)
   120  	if errGz != nil {
   121  		return nil, fmt.Errorf("unable to compress bundle content: %w", errGz)
   122  	}
   123  
   124  	// Delegate to Bundle Writer
   125  	if errDump := Dump(zw, b); errDump != nil {
   126  		return nil, fmt.Errorf("unable to dump container: %w", errDump)
   127  	}
   128  
   129  	// Close gzip writer
   130  	if errGz = zw.Close(); errGz != nil {
   131  		return nil, fmt.Errorf("unable to close compression writer: %w", errGz)
   132  	}
   133  
   134  	// Return container
   135  	return &containerv1.Container{
   136  		Headers: &containerv1.Header{
   137  			ContentEncoding: "gzip",
   138  			ContentType:     bundleContentType,
   139  		},
   140  		Raw: payload.Bytes(),
   141  	}, nil
   142  }