Skip to content

Commit 8920fe1

Browse files
authored
Merge pull request #427 from jack-w-shaw/JUJU-5926_add_method_to_retreive_raw_bundle
#427 Add this RawBundle method to Bundle interface and BundleDataSource In Juju, we have come across a need to run some extra parsing + verification which we do not want to do in juju/charm. We want to keep the data model clean in juju/charm. As such, we need to way to keep the raw bundle data around for later parsing Add this to BundleDataSource, as this is what we use in Juju However, we also implement our own BundleDataSource in Juju. built with the Bundle interface here. So we need to add this method to Bundle as well You can see a draft implementation for this change here: juju/juju#17350 As a flyby, delete some incorrect comments ## QA Steps Compile into juju form the following PR and run it's QA steps: juju/juju#17350
2 parents 9ea5816 + 3845e5d commit 8920fe1

10 files changed

+124
-70
lines changed

bundle.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
type Bundle interface {
1616
// Data returns the contents of the bundle's bundle.yaml file.
1717
Data() *BundleData
18+
// BundleBytes returns the raw bytes content of a bundle
19+
BundleBytes() []byte
1820
// ReadMe returns the contents of the bundle's README.md file.
1921
ReadMe() string
2022
// ContainsOverlays returns true if the bundle contains any overlays.

bundle_test.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
package charm_test
55

66
import (
7+
"os"
8+
"path/filepath"
9+
710
"github.com/juju/testing"
811
jc "github.com/juju/testing/checkers"
912
gc "gopkg.in/check.v1"
@@ -23,7 +26,7 @@ func (*BundleSuite) TestReadBundleDir(c *gc.C) {
2326
c.Assert(err, gc.IsNil)
2427
c.Assert(b.ContainsOverlays(), jc.IsFalse)
2528
c.Assert(b, gc.FitsTypeOf, (*charm.BundleDir)(nil))
26-
checkWordpressBundle(c, b, path)
29+
checkWordpressBundle(c, b, path, "wordpress-simple")
2730
}
2831

2932
func (*BundleSuite) TestReadMultiDocBundleDir(c *gc.C) {
@@ -32,7 +35,7 @@ func (*BundleSuite) TestReadMultiDocBundleDir(c *gc.C) {
3235
c.Assert(err, gc.IsNil)
3336
c.Assert(b.ContainsOverlays(), jc.IsTrue)
3437
c.Assert(b, gc.FitsTypeOf, (*charm.BundleDir)(nil))
35-
checkWordpressBundle(c, b, path)
38+
checkWordpressBundle(c, b, path, "wordpress-simple-multidoc")
3639
}
3740

3841
func (*BundleSuite) TestReadBundleArchive(c *gc.C) {
@@ -41,7 +44,7 @@ func (*BundleSuite) TestReadBundleArchive(c *gc.C) {
4144
c.Assert(err, gc.IsNil)
4245
c.Assert(b.ContainsOverlays(), jc.IsFalse)
4346
c.Assert(b, gc.FitsTypeOf, (*charm.BundleDir)(nil))
44-
checkWordpressBundle(c, b, path)
47+
checkWordpressBundle(c, b, path, "wordpress-simple")
4548
}
4649

4750
func (*BundleSuite) TestReadMultiDocBundleArchive(c *gc.C) {
@@ -50,10 +53,10 @@ func (*BundleSuite) TestReadMultiDocBundleArchive(c *gc.C) {
5053
c.Assert(err, gc.IsNil)
5154
c.Assert(b.ContainsOverlays(), jc.IsTrue)
5255
c.Assert(b, gc.FitsTypeOf, (*charm.BundleDir)(nil))
53-
checkWordpressBundle(c, b, path)
56+
checkWordpressBundle(c, b, path, "wordpress-simple-multidoc")
5457
}
5558

56-
func checkWordpressBundle(c *gc.C, b charm.Bundle, path string) {
59+
func checkWordpressBundle(c *gc.C, b charm.Bundle, path string, bundleName string) {
5760
// Load the charms required by the bundle.
5861
wordpressCharm := readCharmDir(c, "wordpress")
5962
mysqlCharm := readCharmDir(c, "mysql")
@@ -87,6 +90,11 @@ func checkWordpressBundle(c *gc.C, b charm.Bundle, path string) {
8790
case *charm.BundleDir:
8891
c.Assert(b.Path, gc.Equals, path)
8992
}
93+
94+
bundlePath := filepath.Join("internal/test-charm-repo/bundle", bundleName, "bundle.yaml")
95+
raw, err := os.ReadFile(bundlePath)
96+
c.Assert(err, jc.ErrorIsNil)
97+
c.Assert(string(b.BundleBytes()), gc.Equals, string(raw))
9098
}
9199

92100
func verifyOk(string) error {

bundlearchive.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ package charm
66
import (
77
"bytes"
88
"io"
9-
"io/ioutil"
109

1110
ziputil "github.com/juju/utils/v4/zip"
1211
)
1312

1413
type BundleArchive struct {
1514
zopen zipOpener
1615

17-
Path string
18-
data *BundleData
19-
readMe string
16+
Path string
17+
data *BundleData
18+
bundleBytes []byte
19+
readMe string
2020

2121
containsOverlays bool
2222
}
@@ -61,7 +61,12 @@ func readBundleArchive(zopen zipOpener) (*BundleArchive, error) {
6161
if err != nil {
6262
return nil, err
6363
}
64-
a.data, a.containsOverlays, err = readBaseFromMultidocBundle(reader)
64+
b, err := io.ReadAll(reader)
65+
if err != nil {
66+
return nil, err
67+
}
68+
a.bundleBytes = b
69+
a.data, a.containsOverlays, err = readBaseFromMultidocBundle(b)
6570
reader.Close()
6671
if err != nil {
6772
return nil, err
@@ -70,7 +75,7 @@ func readBundleArchive(zopen zipOpener) (*BundleArchive, error) {
7075
if err != nil {
7176
return nil, err
7277
}
73-
readMe, err := ioutil.ReadAll(reader)
78+
readMe, err := io.ReadAll(reader)
7479
if err != nil {
7580
return nil, err
7681
}
@@ -83,6 +88,11 @@ func (a *BundleArchive) Data() *BundleData {
8388
return a.data
8489
}
8590

91+
// BundleBytes implements Bundle.BundleBytes.
92+
func (a *BundleArchive) BundleBytes() []byte {
93+
return a.bundleBytes
94+
}
95+
8696
// ReadMe implements Bundle.ReadMe.
8797
func (a *BundleArchive) ReadMe() string {
8898
return a.readMe

bundlearchive_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package charm_test
55

66
import (
77
"fmt"
8-
"io/ioutil"
98
"os"
109
"path/filepath"
1110

@@ -21,35 +20,37 @@ type BundleArchiveSuite struct {
2120
archivePath string
2221
}
2322

23+
const bundleName = "wordpress-simple"
24+
2425
func (s *BundleArchiveSuite) SetUpSuite(c *gc.C) {
25-
s.archivePath = archivePath(c, readBundleDir(c, "wordpress-simple"))
26+
s.archivePath = archivePath(c, readBundleDir(c, bundleName))
2627
}
2728

2829
func (s *BundleArchiveSuite) TestReadBundleArchive(c *gc.C) {
2930
archive, err := charm.ReadBundleArchive(s.archivePath)
3031
c.Assert(err, gc.IsNil)
31-
checkWordpressBundle(c, archive, s.archivePath)
32+
checkWordpressBundle(c, archive, s.archivePath, bundleName)
3233
}
3334

3435
func (s *BundleArchiveSuite) TestReadBundleArchiveBytes(c *gc.C) {
35-
data, err := ioutil.ReadFile(s.archivePath)
36+
data, err := os.ReadFile(s.archivePath)
3637
c.Assert(err, gc.IsNil)
3738

3839
archive, err := charm.ReadBundleArchiveBytes(data)
3940
c.Assert(err, gc.IsNil)
4041
c.Assert(archive.ContainsOverlays(), jc.IsFalse)
41-
checkWordpressBundle(c, archive, "")
42+
checkWordpressBundle(c, archive, "", bundleName)
4243
}
4344

4445
func (s *BundleArchiveSuite) TestReadMultiDocBundleArchiveBytes(c *gc.C) {
4546
path := archivePath(c, readBundleDir(c, "wordpress-simple-multidoc"))
46-
data, err := ioutil.ReadFile(path)
47+
data, err := os.ReadFile(path)
4748
c.Assert(err, gc.IsNil)
4849

4950
archive, err := charm.ReadBundleArchiveBytes(data)
5051
c.Assert(err, gc.IsNil)
5152
c.Assert(archive.ContainsOverlays(), jc.IsTrue)
52-
checkWordpressBundle(c, archive, "")
53+
checkWordpressBundle(c, archive, "", "wordpress-simple-multidoc")
5354
}
5455

5556
func (s *BundleArchiveSuite) TestReadBundleArchiveFromReader(c *gc.C) {
@@ -61,7 +62,7 @@ func (s *BundleArchiveSuite) TestReadBundleArchiveFromReader(c *gc.C) {
6162

6263
archive, err := charm.ReadBundleArchiveFromReader(f, info.Size())
6364
c.Assert(err, gc.IsNil)
64-
checkWordpressBundle(c, archive, "")
65+
checkWordpressBundle(c, archive, "", bundleName)
6566
}
6667

6768
func (s *BundleArchiveSuite) TestReadBundleArchiveWithoutBundleYAML(c *gc.C) {

bundledata.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,10 @@ type BundleData struct {
4949
// Series holds the default series to use when
5050
// the bundle deploys applications. A series defined for an application
5151
// takes precedence.
52-
// Series and Base cannot be mixed.
5352
Series string `bson:",omitempty" json:",omitempty" yaml:",omitempty"`
5453

5554
// Base holds the default base to use when the bundle deploys
5655
// applications. A base defined for an application takes precedence.
57-
// Series and Base cannot be mixed.
5856
DefaultBase string `bson:"default-base,omitempty" json:"default-base,omitempty" yaml:"default-base,omitempty"`
5957

6058
// Relations holds a slice of 2-element slices,
@@ -104,11 +102,9 @@ type ApplicationSpec struct {
104102
Revision *int `bson:"revision,omitempty" yaml:"revision,omitempty" json:"revision,omitempty"`
105103

106104
// Series is the series to use when deploying the application.
107-
// Series and Base cannot be mixed.
108105
Series string `bson:",omitempty" yaml:",omitempty" json:",omitempty"`
109106

110107
// Base is the base to use when deploying the application.
111-
// Series and Base cannot be mixed.
112108
Base string `bson:",omitempty" yaml:",omitempty" json:",omitempty"`
113109

114110
// Resources is the set of resource revisions to deploy for the
@@ -355,7 +351,11 @@ type OfferSpec struct {
355351
// The returned data is not verified - call Verify to ensure
356352
// that it is OK.
357353
func ReadBundleData(r io.Reader) (*BundleData, error) {
358-
bd, _, err := readBaseFromMultidocBundle(r)
354+
b, err := io.ReadAll(r)
355+
if err != nil {
356+
return nil, err
357+
}
358+
bd, _, err := readBaseFromMultidocBundle(b)
359359
if err != nil {
360360
return nil, err
361361
}
@@ -370,8 +370,8 @@ func ReadBundleData(r io.Reader) (*BundleData, error) {
370370
//
371371
// Clients that are interested in reading multi-doc bundle data should use the
372372
// new helpers: LocalBundleDataSource and StreamBundleDataSource.
373-
func readBaseFromMultidocBundle(r io.Reader) (*BundleData, bool, error) {
374-
parts, err := parseBundleParts(r)
373+
func readBaseFromMultidocBundle(b []byte) (*BundleData, bool, error) {
374+
parts, err := parseBundleParts(b)
375375
if err != nil {
376376
return nil, false, err
377377
}

bundledatasrc.go

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package charm
66
import (
77
"bytes"
88
"io"
9-
"io/ioutil"
109
"os"
1110
"path/filepath"
1211
"strings"
@@ -55,19 +54,25 @@ type BundleDataPart struct {
5554
// list of composable parts.
5655
type BundleDataSource interface {
5756
Parts() []*BundleDataPart
57+
BundleBytes() []byte
5858
BasePath() string
5959
ResolveInclude(path string) ([]byte, error)
6060
}
6161

6262
type resolvedBundleDataSource struct {
63-
basePath string
64-
parts []*BundleDataPart
63+
basePath string
64+
bundleBytes []byte
65+
parts []*BundleDataPart
6566
}
6667

6768
func (s *resolvedBundleDataSource) Parts() []*BundleDataPart {
6869
return s.parts
6970
}
7071

72+
func (s *resolvedBundleDataSource) BundleBytes() []byte {
73+
return s.bundleBytes
74+
}
75+
7176
func (s *resolvedBundleDataSource) BasePath() string {
7277
return s.basePath
7378
}
@@ -95,7 +100,7 @@ func (s *resolvedBundleDataSource) ResolveInclude(path string) ([]byte, error) {
95100
return nil, errors.Errorf("include path %q resolves to a folder", absPath)
96101
}
97102

98-
data, err := ioutil.ReadFile(absPath)
103+
data, err := os.ReadFile(absPath)
99104
if err != nil {
100105
return nil, errors.Annotatef(err, "reading include file at %q", absPath)
101106
}
@@ -131,15 +136,20 @@ func LocalBundleDataSource(path string) (BundleDataSource, error) {
131136
}
132137
defer func() { _ = f.Close() }()
133138

134-
parts, pErr := parseBundleParts(f)
139+
b, err := io.ReadAll(f)
140+
if err != nil {
141+
return nil, err
142+
}
143+
parts, pErr := parseBundleParts(b)
135144
if pErr == nil {
136145
absPath, err := filepath.Abs(path)
137146
if err != nil {
138147
return nil, errors.Annotatef(err, "resolve absolute path to %s", path)
139148
}
140149
return &resolvedBundleDataSource{
141-
basePath: filepath.Dir(absPath),
142-
parts: parts,
150+
basePath: filepath.Dir(absPath),
151+
parts: parts,
152+
bundleBytes: b,
143153
}, nil
144154
}
145155

@@ -159,10 +169,15 @@ func LocalBundleDataSource(path string) (BundleDataSource, error) {
159169
}
160170
defer func() { _ = r.Close() }()
161171

162-
if parts, pErr = parseBundleParts(r); pErr == nil {
172+
b, err = io.ReadAll(r)
173+
if err != nil {
174+
return nil, err
175+
}
176+
if parts, pErr = parseBundleParts(b); pErr == nil {
163177
return &resolvedBundleDataSource{
164-
basePath: "", // use empty base path for archives
165-
parts: parts,
178+
basePath: "", // use empty base path for archives
179+
parts: parts,
180+
bundleBytes: b,
166181
}, nil
167182
}
168183

@@ -185,20 +200,19 @@ func isNotExistsError(err error) bool {
185200
// StreamBundleDataSource reads a (potentially multi-part) bundle from r and
186201
// returns a BundleDataSource for it.
187202
func StreamBundleDataSource(r io.Reader, basePath string) (BundleDataSource, error) {
188-
parts, err := parseBundleParts(r)
203+
b, err := io.ReadAll(r)
204+
if err != nil {
205+
return nil, err
206+
}
207+
parts, err := parseBundleParts(b)
189208
if err != nil {
190209
return nil, errors.NotValidf("cannot unmarshal bundle contents: %v", err)
191210
}
192211

193-
return &resolvedBundleDataSource{parts: parts, basePath: basePath}, nil
212+
return &resolvedBundleDataSource{parts: parts, bundleBytes: b, basePath: basePath}, nil
194213
}
195214

196-
func parseBundleParts(r io.Reader) ([]*BundleDataPart, error) {
197-
b, err := ioutil.ReadAll(r)
198-
if err != nil {
199-
return nil, err
200-
}
201-
215+
func parseBundleParts(b []byte) ([]*BundleDataPart, error) {
202216
var (
203217
// Ideally, we would be using a single reader and we would
204218
// rewind it to read each block in structured and raw mode.
@@ -216,7 +230,7 @@ func parseBundleParts(r io.Reader) ([]*BundleDataPart, error) {
216230
for docIdx := 0; ; docIdx++ {
217231
var part BundleDataPart
218232

219-
err = structDec.Decode(&part.Data)
233+
err := structDec.Decode(&part.Data)
220234
if err == io.EOF {
221235
break
222236
} else if err != nil && !strings.HasPrefix(err.Error(), "yaml: unmarshal errors:") {

0 commit comments

Comments
 (0)