Introduce assets
This change introduces the basic concept of assets. It is far from
fully featured, however, it is enough to start adding support for various
storage kinds that require access to I/O-backed data (files, etc).
The challenge is that Coconut is deterministic by design, and so you
cannot simply read a file in an ad-hoc manner and present the bytes to
a resource provider. Instead, we will model "assets" as first class
entities whose data source is described to the system in a more declarative
manner, so that the system and resource providers can manage them.
There are three ways to create an asset at the moment:
1. A constant, in-memory string.
2. A path to a file on the local filesystem.
3. A URI, whose scheme is extensible.
Eventually, we want to support byte blobs, but due to our use of a
"JSON-like" type system, this isn't easily expressible just yet.
The URI scheme is extensible in that file://, http://, and https://
are supported "out of the box", but individual providers are free to
recognize their own schemes and support them. For instance, copying
one S3 object to another will be supported simply by passing a URI
with the s3:// protocol in the usual way.
Many utility functions are yet to be written, but this is a start.
2017-04-17 22:00:26 +02:00
|
|
|
// Copyright 2017 Pulumi, Inc. All rights reserved.
|
|
|
|
|
|
|
|
package resource
|
|
|
|
|
2017-04-17 22:34:19 +02:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/pulumi/coconut/pkg/util/contract"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Asset is a serialized asset reference. It is a union: thus, only one of its fields will be non-nil. Several helper
|
|
|
|
// routines exist as members in order to easily interact with the assets referenced by an instance of this type.
|
Introduce assets
This change introduces the basic concept of assets. It is far from
fully featured, however, it is enough to start adding support for various
storage kinds that require access to I/O-backed data (files, etc).
The challenge is that Coconut is deterministic by design, and so you
cannot simply read a file in an ad-hoc manner and present the bytes to
a resource provider. Instead, we will model "assets" as first class
entities whose data source is described to the system in a more declarative
manner, so that the system and resource providers can manage them.
There are three ways to create an asset at the moment:
1. A constant, in-memory string.
2. A path to a file on the local filesystem.
3. A URI, whose scheme is extensible.
Eventually, we want to support byte blobs, but due to our use of a
"JSON-like" type system, this isn't easily expressible just yet.
The URI scheme is extensible in that file://, http://, and https://
are supported "out of the box", but individual providers are free to
recognize their own schemes and support them. For instance, copying
one S3 object to another will be supported simply by passing a URI
with the s3:// protocol in the usual way.
Many utility functions are yet to be written, but this is a start.
2017-04-17 22:00:26 +02:00
|
|
|
type Asset struct {
|
|
|
|
Text *string `json:"text,omitempty"` // a textual asset.
|
|
|
|
Path *string `json:"text,omitempty"` // a file on the current filesystem.
|
|
|
|
URI *string `json:"uri,omitempty"` // a URI to a reference fetched (file://, http://, https://, or custom).
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a Asset) IsText() bool { return a.Text != nil }
|
|
|
|
func (a Asset) IsPath() bool { return a.Path != nil }
|
|
|
|
func (a Asset) IsURI() bool { return a.URI != nil }
|
|
|
|
|
2017-04-17 22:34:19 +02:00
|
|
|
func (a Asset) GetText() (string, bool) {
|
Introduce assets
This change introduces the basic concept of assets. It is far from
fully featured, however, it is enough to start adding support for various
storage kinds that require access to I/O-backed data (files, etc).
The challenge is that Coconut is deterministic by design, and so you
cannot simply read a file in an ad-hoc manner and present the bytes to
a resource provider. Instead, we will model "assets" as first class
entities whose data source is described to the system in a more declarative
manner, so that the system and resource providers can manage them.
There are three ways to create an asset at the moment:
1. A constant, in-memory string.
2. A path to a file on the local filesystem.
3. A URI, whose scheme is extensible.
Eventually, we want to support byte blobs, but due to our use of a
"JSON-like" type system, this isn't easily expressible just yet.
The URI scheme is extensible in that file://, http://, and https://
are supported "out of the box", but individual providers are free to
recognize their own schemes and support them. For instance, copying
one S3 object to another will be supported simply by passing a URI
with the s3:// protocol in the usual way.
Many utility functions are yet to be written, but this is a start.
2017-04-17 22:00:26 +02:00
|
|
|
if a.IsText() {
|
|
|
|
return *a.Text, true
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
2017-04-17 22:34:19 +02:00
|
|
|
func (a Asset) GetPath() (string, bool) {
|
Introduce assets
This change introduces the basic concept of assets. It is far from
fully featured, however, it is enough to start adding support for various
storage kinds that require access to I/O-backed data (files, etc).
The challenge is that Coconut is deterministic by design, and so you
cannot simply read a file in an ad-hoc manner and present the bytes to
a resource provider. Instead, we will model "assets" as first class
entities whose data source is described to the system in a more declarative
manner, so that the system and resource providers can manage them.
There are three ways to create an asset at the moment:
1. A constant, in-memory string.
2. A path to a file on the local filesystem.
3. A URI, whose scheme is extensible.
Eventually, we want to support byte blobs, but due to our use of a
"JSON-like" type system, this isn't easily expressible just yet.
The URI scheme is extensible in that file://, http://, and https://
are supported "out of the box", but individual providers are free to
recognize their own schemes and support them. For instance, copying
one S3 object to another will be supported simply by passing a URI
with the s3:// protocol in the usual way.
Many utility functions are yet to be written, but this is a start.
2017-04-17 22:00:26 +02:00
|
|
|
if a.IsPath() {
|
|
|
|
return *a.Path, true
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
2017-04-17 22:34:19 +02:00
|
|
|
func (a Asset) GetURI() (string, bool) {
|
Introduce assets
This change introduces the basic concept of assets. It is far from
fully featured, however, it is enough to start adding support for various
storage kinds that require access to I/O-backed data (files, etc).
The challenge is that Coconut is deterministic by design, and so you
cannot simply read a file in an ad-hoc manner and present the bytes to
a resource provider. Instead, we will model "assets" as first class
entities whose data source is described to the system in a more declarative
manner, so that the system and resource providers can manage them.
There are three ways to create an asset at the moment:
1. A constant, in-memory string.
2. A path to a file on the local filesystem.
3. A URI, whose scheme is extensible.
Eventually, we want to support byte blobs, but due to our use of a
"JSON-like" type system, this isn't easily expressible just yet.
The URI scheme is extensible in that file://, http://, and https://
are supported "out of the box", but individual providers are free to
recognize their own schemes and support them. For instance, copying
one S3 object to another will be supported simply by passing a URI
with the s3:// protocol in the usual way.
Many utility functions are yet to be written, but this is a start.
2017-04-17 22:00:26 +02:00
|
|
|
if a.IsURI() {
|
|
|
|
return *a.URI, true
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
2017-04-17 22:34:19 +02:00
|
|
|
|
|
|
|
func (a Asset) Read() (AssetReader, error) {
|
|
|
|
if text, istext := a.GetText(); istext {
|
|
|
|
return newBytesReader([]byte(text)), nil
|
|
|
|
} else if path, ispath := a.GetPath(); ispath {
|
|
|
|
return os.Open(path)
|
|
|
|
} else if uri, isuri := a.GetURI(); isuri {
|
|
|
|
url, err := url.Parse(uri)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
switch s := url.Scheme; s {
|
|
|
|
case "http", "https":
|
|
|
|
resp, err := http.Get(uri)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newBytesReader(b), nil
|
|
|
|
case "file":
|
|
|
|
contract.Assert(url.Host == "")
|
|
|
|
contract.Assert(url.User == nil)
|
|
|
|
contract.Assert(url.RawQuery == "")
|
|
|
|
contract.Assert(url.Fragment == "")
|
|
|
|
return os.Open(url.Path)
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("Unrecognized or unsupported URI scheme: %v", s)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
contract.Failf("Invalid asset; one of Text, Path, or URI must be non-nil")
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AssetReader reads an asset's contents, offering Read, Seek, and Close functionality.
|
|
|
|
type AssetReader interface {
|
|
|
|
io.Reader
|
|
|
|
io.Seeker
|
|
|
|
io.Closer
|
|
|
|
}
|
|
|
|
|
|
|
|
// bytesReader turns a *bytes.Reader into an AssetReader by adding an empty Close method.
|
|
|
|
type bytesReader struct {
|
|
|
|
*bytes.Reader
|
|
|
|
}
|
|
|
|
|
|
|
|
func newBytesReader(b []byte) AssetReader {
|
|
|
|
return bytesReader{
|
|
|
|
Reader: bytes.NewReader(b),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b bytesReader) Close() error {
|
|
|
|
return nil // intentionally blank
|
|
|
|
}
|