This repository has been archived by the owner on Nov 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement ResourceStore to abstract out manifest operations
ResourceStore is meant to abstract out file operations on explicit manifest files, paving the way for supporting programatically-generated cluster resources.
- Loading branch information
Showing
17 changed files
with
326 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
package cluster | ||
|
||
import ( | ||
"bytes" | ||
"io/ioutil" | ||
"os" | ||
"path" | ||
"sync" | ||
|
||
"github.com/weaveworks/flux" | ||
"github.com/weaveworks/flux/git" | ||
"github.com/weaveworks/flux/image" | ||
"github.com/weaveworks/flux/policy" | ||
"github.com/weaveworks/flux/resource" | ||
) | ||
|
||
// ResourceStore manages all the cluster resources defined in a repository, explicitly declared in a file or not. | ||
// e.g., generated and updated by a .flux.yaml file, explicit Kubernetes .yaml manifests files ... | ||
type ResourceStore interface { | ||
SetWorkloadContainerImage(resourceID flux.ResourceID, container string, newImageID image.Ref) error | ||
GetAllResourcesByID() (map[string]resource.Resource, error) | ||
GetAllResourcesBySource() (map[string]resource.Resource, error) | ||
UpdateWorkloadPolicies(flux.ResourceID, policy.Update) (bool, error) | ||
} | ||
|
||
var _ ResourceStore = &resourceStore{} | ||
|
||
type resourceStore struct { | ||
manifests Manifests | ||
checkout *git.Checkout | ||
cachedResourcesBySource map[string]resource.Resource | ||
cachedResourcesByID map[string]resource.Resource | ||
// TODO(fons): do we really need locking, can the checkout shared across threads? | ||
sync.RWMutex | ||
} | ||
|
||
func NewResourceStore(manifests Manifests, checkout *git.Checkout) *resourceStore { | ||
return &resourceStore{ | ||
manifests: manifests, | ||
checkout: checkout, | ||
} | ||
} | ||
|
||
// Set the container image of a resource in the store | ||
func (s *resourceStore) SetWorkloadContainerImage(id flux.ResourceID, container string, newImageID image.Ref) error { | ||
resourcesByID, _, err := s.getAllResources() | ||
if err != nil { | ||
return err | ||
} | ||
resource, ok := resourcesByID[id.String()] | ||
if !ok { | ||
return ErrResourceNotFound(id.String()) | ||
} | ||
absolutePath := path.Join(s.checkout.Dir(), resource.Source()) | ||
def, err := ioutil.ReadFile(absolutePath) | ||
if err != nil { | ||
return err | ||
} | ||
newDef, err := s.manifests.SetWorkloadContainerImage(def, id, container, newImageID) | ||
if err != nil { | ||
return err | ||
} | ||
fi, err := os.Stat(absolutePath) | ||
if err != nil { | ||
return err | ||
} | ||
if err := ioutil.WriteFile(absolutePath, newDef, fi.Mode()); err != nil { | ||
return err | ||
} | ||
// Reset cached resources, since we have modified one | ||
// TODO(fons): make this more performant. | ||
// We could, for instance, parse it after modifying it and add it to the resources. | ||
s.resetCache() | ||
return nil | ||
} | ||
|
||
// Load all the resources in the store. The returned map is indexed by the resource IDs | ||
func (s *resourceStore) GetAllResourcesByID() (map[string]resource.Resource, error) { | ||
resourcesByID, _, err := s.getAllResources() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return resourcesByID, nil | ||
} | ||
|
||
// Load all the resources in the store. The returned map is indexed by the file which defines de resource | ||
func (s *resourceStore) GetAllResourcesBySource() (map[string]resource.Resource, error) { | ||
_, resourcesBySource, err := s.getAllResources() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return resourcesBySource, nil | ||
} | ||
|
||
// UpdateWorkloadPolicies modifies a resource in the store to apply the policy-update specified | ||
// It returns whether a change in the resource was actually made as a result of the change | ||
func (s *resourceStore) UpdateWorkloadPolicies(id flux.ResourceID, update policy.Update) (bool, error) { | ||
resourcesByID, _, err := s.getAllResources() | ||
if err != nil { | ||
return false, err | ||
} | ||
resource, ok := resourcesByID[id.String()] | ||
if !ok { | ||
return false, ErrResourceNotFound(id.String()) | ||
} | ||
absolutePath := path.Join(s.checkout.Dir(), resource.Source()) | ||
def, err := ioutil.ReadFile(absolutePath) | ||
if err != nil { | ||
return false, err | ||
} | ||
newDef, err := s.manifests.UpdateWorkloadPolicies(def, id, update) | ||
if err != nil { | ||
return false, err | ||
} | ||
fi, err := os.Stat(absolutePath) | ||
if err != nil { | ||
return false, err | ||
} | ||
if err := ioutil.WriteFile(absolutePath, newDef, fi.Mode()); err != nil { | ||
return false, err | ||
} | ||
comparison := bytes.Compare(def, newDef) | ||
// Reset cached resources, since we have modified one | ||
// TODO(fons): make this more performant. | ||
// We could, for instance, parse it after modifying it and add it to the resources. | ||
s.resetCache() | ||
return comparison != 0, nil | ||
} | ||
|
||
func (s *resourceStore) getAllResources() (map[string]resource.Resource, map[string]resource.Resource, error) { | ||
s.RLock() | ||
if s.cachedResourcesByID != nil && s.cachedResourcesBySource != nil { | ||
s.RUnlock() | ||
return s.cachedResourcesByID, s.cachedResourcesBySource, nil | ||
} | ||
s.RUnlock() | ||
|
||
resources, err := s.manifests.LoadManifests(s.checkout.Dir(), s.checkout.ManifestDirs()) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
resourcesByID := map[string]resource.Resource{} | ||
resourcesBySource := map[string]resource.Resource{} | ||
for _, r := range resources { | ||
resourcesByID[r.ResourceID().String()] = r | ||
resourcesBySource[r.Source()] = r | ||
} | ||
s.Lock() | ||
s.cachedResourcesByID = resourcesByID | ||
s.cachedResourcesBySource = resourcesBySource | ||
s.Unlock() | ||
return resourcesByID, resourcesBySource, nil | ||
} | ||
|
||
func (s *resourceStore) resetCache() { | ||
s.Lock() | ||
s.cachedResourcesByID = nil | ||
s.cachedResourcesBySource = nil | ||
s.Unlock() | ||
} |
Oops, something went wrong.