Skip to content
This repository has been archived by the owner on Dec 5, 2020. It is now read-only.

Commit

Permalink
ignition: extend cache to keep files on disk to keep data intact betw…
Browse files Browse the repository at this point in the history
…een invocations

With errors like issue 116 [1], terraform restarts the provider server. And with terraform turning
on `AutoMTLS` for all plugins [2], the client cannot reattach to already running provider server [3].

Provider's memory only caches creates errors like `invalid file \"49e2cc7ba023d87070d9b49a0c8f02ff0f63b7f61b390fbce8f64b011bd42ed7\", unknown file id`
because the ignition_config resource tried to retrived a file id stored in cache before provider server was restarted.

This stores the objects in `diskv` store in users' cache directory, retireving it when the memory cache is not populated.
The in-memory cache keeps providing the no overhead access to objects.

[1]: hashicorp/go-plugin#116
[2]: hashicorp/terraform#19560
[3]: https://github.com/hashicorp/go-plugin/blob/5692942914bbdbc03558fde936b1f0bc2af365be/client.go#L203-L204
  • Loading branch information
abhinavdahiya committed May 13, 2019
1 parent 69baf50 commit 18ce0b3
Show file tree
Hide file tree
Showing 2 changed files with 263 additions and 52 deletions.
255 changes: 233 additions & 22 deletions ignition/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"sync"

"github.com/coreos/ignition/config/v2_1/types"
"github.com/coreos/ignition/config/validate/report"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
"github.com/peterbourgon/diskv"
)

// globalCache keeps the instances of the internal types of ignition generated
Expand Down Expand Up @@ -46,10 +49,22 @@ func Provider() terraform.ResourceProvider {
"ignition_user": dataSourceUser(),
"ignition_group": dataSourceGroup(),
},
ConfigureFunc: providerConfigure,
}
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
basePath := filepath.Join(os.TempDir(), "terraform-provider-ignition")

globalCache.d = diskv.New(diskv.Options{
BasePath: basePath,
CacheSizeMax: 100 * 1024 * 1024, // 100MB
})
return nil, nil
}

type cache struct {
// quick retrieval without marshal/unmarshal overhead
disks map[string]*types.Disk
arrays map[string]*types.Raid
filesystems map[string]*types.Filesystem
Expand All @@ -61,119 +76,315 @@ type cache struct {
users map[string]*types.PasswdUser
groups map[string]*types.PasswdGroup

// cached retrieval from disk and marshal/unmarshal overhead
d *diskv.Diskv

sync.Mutex
}

func (c *cache) addDisk(g *types.Disk) string {
c.Lock()
defer c.Unlock()

id := id(g)
id, raw := id(g)
c.disks[id] = g

c.d.Write(keyFromType("disks", id), raw)
return id
}

func (c *cache) getDisk(id string) (*types.Disk, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.disks[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("disks", id))
if err != nil {
return nil, err
}
out := &types.Disk{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.disks[id] = out
return out, nil
}

func (c *cache) addRaid(r *types.Raid) string {
c.Lock()
defer c.Unlock()

id := id(r)
id, raw := id(r)
c.arrays[id] = r

c.d.Write(keyFromType("arrays", id), raw)
return id
}

func (c *cache) getRaid(id string) (*types.Raid, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.arrays[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("arrays", id))
if err != nil {
return nil, err
}
out := &types.Raid{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.arrays[id] = out
return out, nil
}

func (c *cache) addFilesystem(f *types.Filesystem) string {
c.Lock()
defer c.Unlock()

id := id(f)
id, raw := id(f)
c.filesystems[id] = f

c.d.Write(keyFromType("fs", id), raw)
return id
}

func (c *cache) getFilesystem(id string) (*types.Filesystem, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.filesystems[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("fs", id))
if err != nil {
return nil, err
}
out := &types.Filesystem{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.filesystems[id] = out
return out, nil
}

func (c *cache) addFile(f *types.File) string {
c.Lock()
defer c.Unlock()

id := id(f)
id, raw := id(f)
c.files[id] = f

c.d.Write(keyFromType("files", id), raw)
return id
}

func (c *cache) getFile(id string) (*types.File, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.files[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("files", id))
if err != nil {
return nil, err
}
out := &types.File{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.files[id] = out
return out, nil
}

func (c *cache) addDirectory(d *types.Directory) string {
c.Lock()
defer c.Unlock()

id := id(d)
id, raw := id(d)
c.directories[id] = d

c.d.Write(keyFromType("dirs", id), raw)
return id
}

func (c *cache) getDirectory(id string) (*types.Directory, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.directories[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("dirs", id))
if err != nil {
return nil, err
}
out := &types.Directory{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.directories[id] = out
return out, nil
}

func (c *cache) addLink(l *types.Link) string {
c.Lock()
defer c.Unlock()

id := id(l)
id, raw := id(l)
c.links[id] = l

c.d.Write(keyFromType("links", id), raw)
return id
}

func (c *cache) getLink(id string) (*types.Link, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.links[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("links", id))
if err != nil {
return nil, err
}
out := &types.Link{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.links[id] = out
return out, nil
}

func (c *cache) addSystemdUnit(u *types.Unit) string {
c.Lock()
defer c.Unlock()

id := id(u)
id, raw := id(u)
c.systemdUnits[id] = u

c.d.Write(keyFromType("sunits", id), raw)
return id
}

func (c *cache) getSystemdUnit(id string) (*types.Unit, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.systemdUnits[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("sunits", id))
if err != nil {
return nil, err
}
out := &types.Unit{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.systemdUnits[id] = out
return out, nil
}

func (c *cache) addNetworkdUnit(u *types.Networkdunit) string {
c.Lock()
defer c.Unlock()

id := id(u)
id, raw := id(u)
c.networkdUnits[id] = u

c.d.Write(keyFromType("nunits", id), raw)
return id
}

func (c *cache) getNetworkdunit(id string) (*types.Networkdunit, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.networkdUnits[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("nunits", id))
if err != nil {
return nil, err
}
out := &types.Networkdunit{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.networkdUnits[id] = out
return out, nil
}

func (c *cache) addUser(u *types.PasswdUser) string {
c.Lock()
defer c.Unlock()

id := id(u)
id, raw := id(u)
c.users[id] = u

c.d.Write(keyFromType("users", id), raw)
return id
}

func (c *cache) getUser(id string) (*types.PasswdUser, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.users[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("users", id))
if err != nil {
return nil, err
}
out := &types.PasswdUser{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.users[id] = out
return out, nil
}

func (c *cache) addGroup(g *types.PasswdGroup) string {
c.Lock()
defer c.Unlock()

id := id(g)
id, raw := id(g)
c.groups[id] = g

c.d.Write(keyFromType("groups", id), raw)
return id
}

func id(input interface{}) string {
func (c *cache) getGroup(id string) (*types.PasswdGroup, error) {
c.Lock()
defer c.Unlock()

if v, ok := c.groups[id]; ok {
return v, nil
}
raw, err := c.d.Read(keyFromType("groups", id))
if err != nil {
return nil, err
}
out := &types.PasswdGroup{}
if err := json.Unmarshal(raw, out); err != nil {
return nil, err
}
c.groups[id] = out
return out, nil
}

// id returns the hashed and raw identifier for the input
func id(input interface{}) (string, []byte) {
b, _ := json.Marshal(input)
return hash(string(b))
return hash(string(b)), b
}

func hash(s string) string {
sha := sha256.Sum256([]byte(s))
return hex.EncodeToString(sha[:])
}

func keyFromType(t string, id string) string { return fmt.Sprintf("%s-%s", t, id) }

func castSliceInterface(i []interface{}) []string {
var o []string
for _, value := range i {
Expand Down
Loading

0 comments on commit 18ce0b3

Please sign in to comment.