Skip to content

Commit

Permalink
Create MetadataJSONFile helper for exposing metadata as a JSON file
Browse files Browse the repository at this point in the history
This is a common pattern, so provide a helper in the `plugin` module
and use it for AWS, Docker, and GCP.

Signed-off-by: Michael Smith <[email protected]>
  • Loading branch information
MikaelSmith committed Aug 1, 2019
1 parent 264e95d commit a9efac8
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 97 deletions.
2 changes: 2 additions & 0 deletions datastore/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ func (cache *MemCache) Get(category, key string) (interface{}, error) {
// If the value does not exist, then it generates the value using
// the generateValue function and stores it with the specified ttl.
// If resetTTLOnHit is true, will reset the cache expiration for the entry.
// A ttl of -1 means the item never expires, and a ttl of 0 uses the cache
// default of 1 minute.
func (cache *MemCache) GetOrUpdate(category, key string, ttl time.Duration, resetTTLOnHit bool, generateValue func() (interface{}, error)) (interface{}, error) {
cache.mux.RLock()
defer cache.mux.RUnlock()
Expand Down
5 changes: 3 additions & 2 deletions plugin/aws/ec2Instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func newEC2Instance(ctx context.Context, inst *ec2Client.Instance, session *sess
ec2Instance.client = client
ec2Instance.
SetTTLOf(plugin.ListOp, 30*time.Second).
DisableCachingFor(plugin.MetadataOp).
SetAttributes(getAttributes(inst))

return ec2Instance
Expand Down Expand Up @@ -163,7 +164,7 @@ func (inst *ec2Instance) Schema() *plugin.EntrySchema {
func (inst *ec2Instance) ChildSchemas() []*plugin.EntrySchema {
return []*plugin.EntrySchema{
(&ec2InstanceConsoleOutput{}).Schema(),
(&ec2InstanceMetadataJSON{}).Schema(),
(&plugin.MetadataJSONFile{}).Schema(),
(&volume.FS{}).Schema(),
}
}
Expand All @@ -177,7 +178,7 @@ func (inst *ec2Instance) List(ctx context.Context) ([]plugin.Entry, error) {

entries := []plugin.Entry{}

metadataJSON, err := newEC2InstanceMetadataJSON(ctx, inst)
metadataJSON, err := plugin.NewMetadataJSONFile(ctx, inst)
if err != nil {
return nil, err
}
Expand Down
50 changes: 0 additions & 50 deletions plugin/aws/ec2InstanceMetadataJSON.go

This file was deleted.

4 changes: 3 additions & 1 deletion plugin/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ type opFunc func() (interface{}, error)
// For example, CachedOp could be useful to cache an API request whose
// response lets you implement Open() and Metadata() for the given entry.
//
// CachedOp uses the supplied context to determine which activity to log to.
// A ttl of 0 uses the cache default of 1 minute. Negative ttls are not allowed.
//
// CachedOp uses the supplied context to determine where to log activity.
func CachedOp(ctx context.Context, opName string, entry Entry, ttl time.Duration, op opFunc) (interface{}, error) {
if !opNameRegex.MatchString(opName) {
panic(fmt.Sprintf("The opName %v does not match %v", opName, opNameRegex.String()))
Expand Down
40 changes: 0 additions & 40 deletions plugin/docker/container-metadata.go

This file was deleted.

9 changes: 6 additions & 3 deletions plugin/docker/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,23 @@ func (c *container) Schema() *plugin.EntrySchema {
func (c *container) ChildSchemas() []*plugin.EntrySchema {
return []*plugin.EntrySchema{
(&containerLogFile{}).Schema(),
(&containerMetadata{}).Schema(),
(&plugin.MetadataJSONFile{}).Schema(),
(&vol.FS{}).Schema(),
}
}

func (c *container) List(ctx context.Context) ([]plugin.Entry, error) {
// TODO: May be worth creating a helper that makes it easy to create
// read-only files. Lots of shared code between these two.
cm := newContainerMetadata(c)
cm, err := plugin.NewMetadataJSONFile(ctx, c)
if err != nil {
return nil, err
}
clf := newContainerLogFile(c)

// Include a view of the remote filesystem using volume.FS. Use a small maxdepth because
// VMs can have lots of files and Exec is fast.
return []plugin.Entry{cm, clf, vol.NewFS("fs", c, 3)}, nil
return []plugin.Entry{clf, cm, vol.NewFS("fs", c, 3)}, nil
}

func (c *container) Exec(ctx context.Context, cmd string, args []string, opts plugin.ExecOptions) (plugin.ExecCommand, error) {
Expand Down
11 changes: 10 additions & 1 deletion plugin/gcp/computeInst.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,21 @@ func newComputeInstance(inst *compute.Instance, c computeProjectService) *comput
instance: inst,
service: c,
}
comp.Attributes().SetMeta(inst)
comp.
DisableCachingFor(plugin.MetadataOp).
Attributes().SetMeta(inst)
return comp
}

func (c *computeInstance) List(ctx context.Context) ([]plugin.Entry, error) {
metadataJSONFile, err := plugin.NewMetadataJSONFile(ctx, c)
if err != nil {
return nil, err
}

return []plugin.Entry{
newComputeInstanceConsoleOutput(c.instance, c.service),
metadataJSONFile,
// Include a view of the remote filesystem using volume.FS. Use a small maxdepth because
// VMs can have lots of files and SSH is fast.
volume.NewFS("fs", c, 3),
Expand All @@ -53,6 +61,7 @@ func (c *computeInstance) Schema() *plugin.EntrySchema {
func (c *computeInstance) ChildSchemas() []*plugin.EntrySchema {
return []*plugin.EntrySchema{
(&computeInstanceConsoleOutput{}).Schema(),
(&plugin.MetadataJSONFile{}).Schema(),
(&volume.FS{}).Schema(),
}
}
Expand Down
54 changes: 54 additions & 0 deletions plugin/metadataJSON.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package plugin

import (
"bytes"
"context"
"encoding/json"
)

// MetadataJSONFile represents a metadata.json file that contains another entry's metadata.
type MetadataJSONFile struct {
EntryBase
other Entry
}

// NewMetadataJSONFile creates a new MetadataJSONFile. If caching Metadata on the `other` entry is
// disabled, it will use that to compute the file size upfront.
func NewMetadataJSONFile(ctx context.Context, other Entry) (*MetadataJSONFile, error) {
meta := &MetadataJSONFile{
EntryBase: NewEntry("metadata.json"),
other: other,
}

if other.getTTLOf(MetadataOp) < 0 {
// Content is presumably easy to get, so use it to determine size.
content, err := meta.Open(ctx)
if err != nil {
return nil, err
}

meta.Attributes().SetSize(uint64(content.Size()))
}

return meta, nil
}

// Schema defines the schema of a metadata.json file.
func (m *MetadataJSONFile) Schema() *EntrySchema {
return NewEntrySchema(m, "metadata.json").IsSingleton()
}

// Open returns the metadata of the `other` entry as its content.
func (m *MetadataJSONFile) Open(ctx context.Context) (SizedReader, error) {
meta, err := CachedMetadata(ctx, m.other)
if err != nil {
return nil, err
}

prettyMeta, err := json.MarshalIndent(meta, "", " ")
if err != nil {
return nil, err
}

return bytes.NewReader(prettyMeta), nil
}
24 changes: 24 additions & 0 deletions plugin/metadataJSON_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package plugin

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
)

type basicEntry struct {
EntryBase
}

func (e *basicEntry) Schema() *EntrySchema {
return NewEntrySchema(e, "basic")
}

func TestMetadataJSONFile(t *testing.T) {
basic := basicEntry{NewEntry("foo")}
inst, err := NewMetadataJSONFile(context.Background(), &basic)
assert.NoError(t, err)
assert.Equal(t, "metadata.json", inst.Name())
assert.Implements(t, (*Readable)(nil), inst)
}

0 comments on commit a9efac8

Please sign in to comment.