Skip to content

Commit

Permalink
Refactor store and modularize code
Browse files Browse the repository at this point in the history
Signed-off-by: bupd <[email protected]>
  • Loading branch information
bupd committed Jul 3, 2024
1 parent bc42eae commit 530cd8f
Showing 1 changed file with 135 additions and 99 deletions.
234 changes: 135 additions & 99 deletions internal/store/in-memory-store.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,152 +9,185 @@ import (
"github.com/google/go-containerregistry/pkg/crane"
)

// Image represents a container image with its digest and name.
type Image struct {
Digest string
Name string
}

// inMemoryStore stores images in memory and uses an ImageFetcher to manage images.
type inMemoryStore struct {
images map[string]string
fetcher ImageFetcher
}

// Storer is the interface for image storage operations.
type Storer interface {
List(ctx context.Context) ([]Image, error)
Add(ctx context.Context, digest string, image string) error
Remove(ctx context.Context, digest string, image string) error
Add(ctx context.Context, digest, image string) error
Remove(ctx context.Context, digest, image string) error
}

// ImageFetcher is the interface for fetching images.
type ImageFetcher interface {
List(ctx context.Context) ([]Image, error)
GetDigest(ctx context.Context, tag string) (string, error)
SourceType() string
}

// NewInMemoryStore creates a new in-memory store with the given ImageFetcher.
func NewInMemoryStore(fetcher ImageFetcher) Storer {
return &inMemoryStore{
images: make(map[string]string),
fetcher: fetcher,
}
}

// List retrieves and synchronizes the list of images from the fetcher.
func (s *inMemoryStore) List(ctx context.Context) ([]Image, error) {
var imageList []Image
var change bool

// Fetch images from the file/remote source
// fetch List of images
imageList, err := s.fetcher.List(ctx)
if err != nil {
return nil, err
}

// Handle File and Remote fetcher types differently
var changeDetected bool

switch s.fetcher.SourceType() {
case "File":
for _, img := range imageList {
// Check if the image already exists in the store
if _, exists := s.images[img.Name]; !exists {
// Add the image to the store
s.AddImage(ctx, img.Name)
change = true
} else {
fmt.Printf("Image %s already exists in the store\n", img.Name)
}
changeDetected, err = s.handleFileSource(ctx, imageList)
case "Remote":
changeDetected, err = s.handleRemoteSource(ctx, imageList)
default:
return nil, fmt.Errorf("unknown source type")
}
if err != nil {
return nil, err
}

if changeDetected {
fmt.Println("Changes detected in the store")
return s.getImageList(), nil
} else {
fmt.Println("No changes detected in the store")
return nil, nil
}
}

// handleFileSource handles image from a file
func (s *inMemoryStore) handleFileSource(ctx context.Context, imageList []Image) (bool, error) {
var change bool
for _, img := range imageList {
// Check if the image already exists in the store
if _, exists := s.images[img.Name]; !exists {
// Add the image to the store
s.AddImage(ctx, img.Name)
change = true
} else {
fmt.Printf("Image %s already exists in the store\n", img.Name)
}
}

// Iterate over s.images and remove any image that is not found in imageList
for image := range s.images {
found := false
for _, img := range imageList {
if img.Name == image {
found = true
break
}
}
if !found {
s.RemoveImage(ctx, image)
change = true
// Iterate over s.images and remove any image that is not found in imageList
for image := range s.images {
found := false
for _, img := range imageList {
if img.Name == image {
found = true
break
}
}

// Empty and refill imageList with the contents from s.images
imageList = imageList[:0]
for name, digest := range s.images {
imageList = append(imageList, Image{Name: name, Digest: digest})
if !found {
s.RemoveImage(ctx, image)
change = true
}
}

// Print out the entire store for debugging purposes
fmt.Println("Current store:")
for image := range s.images {
fmt.Printf("Image: %s\n", image)
}
// Empty and refill imageList with the contents from s.images
imageList = imageList[:0]
for name, digest := range s.images {
imageList = append(imageList, Image{Name: name, Digest: digest})
}

case "Remote":
// Trim the imageList elements to remove the project name from the image reference
for i, img := range imageList {
parts := strings.Split(img.Name, "/")
if len(parts) > 1 {
// Take the second part as the new Reference
imageList[i].Name = parts[1]
}
}
// iterate over imageList and call GetDigest for each tag
for _, img := range imageList {
// Split the image reference to get the tag
tagParts := strings.Split(img.Name, ":")
// Check if there is a tag part, min length is 1 char
if len(tagParts) < 2 {
fmt.Println("No tag part found in the image reference")
}
// Use the last part as the tag
tag := tagParts[len(tagParts)-1]
// Get the digest for the tag
digest, err := s.fetcher.GetDigest(ctx, tag)
if err != nil {
return nil, err
}
// Print out the entire store for debugging purposes
fmt.Println("Current store:")
for image := range s.images {
fmt.Printf("Image: %s\n", image)
}

// Check if the image exists and matches the digest
if !(s.checkImageAndDigest(digest, img.Name)) {
change = true
}
return change, nil
}

// handleRemoteSource handles images fetched from a remote source.
func (s *inMemoryStore) handleRemoteSource(ctx context.Context, imageList []Image) (bool, error) {
var change bool
// Trim the imageList elements to remove the project name from the image reference
for i, img := range imageList {
parts := strings.Split(img.Name, "/")
if len(parts) > 1 {
// Take the second part as the new Reference
imageList[i].Name = parts[1]
}

// Create imageMap filled with all images from remote imageList
imageMap := make(map[string]bool)
for _, img := range imageList {
imageMap[img.Name] = true
}
// iterate over imageList and call GetDigest for each tag
for _, img := range imageList {
// Split the image reference to get the tag
tagParts := strings.Split(img.Name, ":")
// Check if there is a tag part, min length is 1 char
if len(tagParts) < 2 {
fmt.Println("No tag part found in the image reference")
}

// Iterate over in memory store and remove any image that is not found in imageMap
for digest, image := range s.images {
if _, exists := imageMap[image]; !exists {
s.Remove(ctx, digest, image)
change = true
}
// Use the last part as the tag
tag := tagParts[len(tagParts)-1]
// Get the digest for the tag
digest, err := s.fetcher.GetDigest(ctx, tag)
if err != nil {
return false, err
}
// Print out the entire store for debugging purposes
fmt.Println("Current store:")
for digest, imageRef := range s.images {
fmt.Printf("Digest: %s, Image: %s\n", digest, imageRef)

// Check if the image exists and matches the digest
if !(s.checkImageAndDigest(digest, img.Name)) {
change = true
}

// Empty and refill imageList with the contents from s.images
imageList = imageList[:0]
for _, name := range s.images {
imageList = append(imageList, Image{Digest: "", Name: name})
}

// Create imageMap filled with all images from remote imageList
imageMap := make(map[string]bool)
for _, img := range imageList {
imageMap[img.Name] = true
}

// Iterate over in memory store and remove any image that is not found in imageMap
for digest, image := range s.images {
if _, exists := imageMap[image]; !exists {
s.Remove(ctx, digest, image)
change = true
}
}
// Print out the entire store for debugging purposes
fmt.Println("Current store:")
for digest, imageRef := range s.images {
fmt.Printf("Digest: %s, Image: %s\n", digest, imageRef)
}

// Empty and refill imageList with the contents from s.images
imageList = imageList[:0]
for _, name := range s.images {
imageList = append(imageList, Image{Digest: "", Name: name})
}
if change {
fmt.Println("Changes detected in the store")
change = false
return imageList, nil
} else {
fmt.Println("No changes detected in the store")
return nil, nil

return change, nil
}

// getImageList converts the in-memory store to a list of Image structs.
func (s *inMemoryStore) getImageList() []Image {
var imageList []Image
// Empty and refill imageList with the contents from s.images
for _, name := range s.images {
imageList = append(imageList, Image{Digest: "", Name: name})
}
return imageList
}

func (s *inMemoryStore) Add(ctx context.Context, digest string, image string) error {
Expand All @@ -170,13 +203,13 @@ func (s *inMemoryStore) Add(ctx context.Context, digest string, image string) er
}
}

func (s *inMemoryStore) AddImage(ctx context.Context, image string) error {
func (s *inMemoryStore) AddImage(ctx context.Context, image string) {
// Add the image to the store
s.images[image] = ""
fmt.Printf("Added image: %s\n", image)
return nil
}

// Removes the image from the store
func (s *inMemoryStore) Remove(ctx context.Context, digest string, image string) error {
// Check if the image exists in the store
if _, exists := s.images[digest]; exists {
Expand All @@ -190,11 +223,10 @@ func (s *inMemoryStore) Remove(ctx context.Context, digest string, image string)
}
}

func (s *inMemoryStore) RemoveImage(ctx context.Context, image string) error {
// Remove the image from the store
// Remove the image from the store
func (s *inMemoryStore) RemoveImage(ctx context.Context, image string) {
delete(s.images, image)
fmt.Printf("Removed image: %s\n", image)
return nil
}

// TODO: Rework complicated logic and add support for multiple repositories
Expand Down Expand Up @@ -222,7 +254,11 @@ func (s *inMemoryStore) checkImageAndDigest(digest string, image string) bool {
}
} else {
// Digest exists but does not match the current image reference
s.Remove(context.Background(), storeDigest, storeImage)
if err := s.Remove(context.Background(), storeDigest, storeImage); err != nil {
fmt.Errorf("Error: %w", err)
return false
}

s.Add(context.Background(), digest, image)
return false
}
Expand Down

0 comments on commit 530cd8f

Please sign in to comment.