Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds state artifact fetcher #55

Merged
merged 23 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b0304ff
adding state artifact fetcher to fetch the state from harbor
Mehul-Kumar-27 Sep 23, 2024
8a5ba11
completing the url fetcher
Mehul-Kumar-27 Sep 25, 2024
c291e6a
adding schedulers and process to satellite
Mehul-Kumar-27 Sep 29, 2024
fe299f8
adding simple notifier to fetch state process
Mehul-Kumar-27 Sep 29, 2024
b6b5dea
added description to the scheduler
Mehul-Kumar-27 Sep 29, 2024
91222d4
coderabbit fixes and changes to fetcher and schedulers
Mehul-Kumar-27 Sep 29, 2024
09419ce
adding new format of the state file
Mehul-Kumar-27 Oct 2, 2024
c60f7a4
adding config to process new state artifact file
Mehul-Kumar-27 Oct 8, 2024
18b5633
Merge branch 'main' into satellite-state-fetcher
Mehul-Kumar-27 Oct 8, 2024
dec1ba0
coderabbit review
Mehul-Kumar-27 Oct 8, 2024
ef0d82a
added ./zot to gitignore
Mehul-Kumar-27 Oct 8, 2024
74fc4b9
fixing the replication process
Mehul-Kumar-27 Oct 10, 2024
3d0e209
fixing the replication and deletion process
Mehul-Kumar-27 Oct 12, 2024
6e54a14
fixing paning while removing the null tags
Mehul-Kumar-27 Oct 13, 2024
8904e99
using repository name instead of the image name while uploading the i…
Mehul-Kumar-27 Oct 16, 2024
a08405d
fixes
Mehul-Kumar-27 Oct 29, 2024
f754085
moving from toml config to json config
Mehul-Kumar-27 Nov 4, 2024
abb1a3e
making config.json work with the replicator
Mehul-Kumar-27 Nov 4, 2024
a641df3
avoid printing confedential information in log
Mehul-Kumar-27 Nov 4, 2024
2f11137
coderabbit fixes
Mehul-Kumar-27 Nov 4, 2024
5ca08c7
dagger version
Mehul-Kumar-27 Nov 19, 2024
9c6d293
Merge branch 'main' into satellite-state-fetcher
Mehul-Kumar-27 Nov 21, 2024
9782e7e
replication fix
Mehul-Kumar-27 Nov 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ ZOT_URL="127.0.0.1:8585"
TOKEN=""
ENV=dev
USE_UNSECURE=true
GROUP_NAME=test-satellite-group
STATE_ARTIFACT_NAME=state-artifact
4 changes: 2 additions & 2 deletions ci/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ func (m *HarborSatellite) Service(
}

// Would build the project with the source provided. The name should be the name of the project.
func (m *HarborSatellite) build(source *dagger.Directory, name string) *dagger.Directory {
fmt.Printf("Building %s\n", name)
func (m *HarborSatellite) build(source *dagger.Directory, component string) *dagger.Directory {
fmt.Printf("Building %s\n", component)

gooses := []string{"linux", "darwin"}
goarches := []string{"amd64", "arm64"}
Expand Down
3 changes: 2 additions & 1 deletion config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ own_registry_adr = "127.0.0.1"
own_registry_port = "8585"

# URL of remote registry OR local file path
url_or_file = "https://demo.goharbor.io/v2/myproject/album-server"
# url_or_file = "https://demo.goharbor.io/v2/myproject/album-server"
url_or_file = "https://demo.goharbor.io"
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved

# Default path for Zot registry config.json
zotConfigPath = "./registry/config.json"
Expand Down
34 changes: 34 additions & 0 deletions internal/config/artifact.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package config

// ArtifactReader defines an interface for reading artifact data
type ArtifactReader interface {
// GetRepository returns the repository of the artifact
GetRepository() string
// GetTag returns the tag of the artifact
GetTag() string
// GetHash returns the hash of the artifact
GetHash() string
}

// Artifact represents an artifact object in the registry
type Artifact struct {
Repository string `json:"repository"`
Tag string `json:"tag"`
Hash string `json:"hash"`
}

func NewArtifact() ArtifactReader {
return &Artifact{}
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved

func (a *Artifact) GetRepository() string {
return a.Repository
}

func (a *Artifact) GetTag() string {
return a.Tag
}

func (a *Artifact) GetHash() string {
return a.Hash
}
77 changes: 49 additions & 28 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,26 @@ import (
var AppConfig *Config

type Config struct {
log_level string
own_registry bool
own_registry_adr string
own_registry_port string
zot_config_path string
input string
zot_url string
registry string
repository string
user_input string
scheme string
api_version string
image string
harbor_password string
harbor_username string
env string
use_unsecure bool
log_level string
own_registry bool
own_registry_adr string
own_registry_port string
zot_config_path string
input string
zot_url string
registry string
repository string
user_input string
scheme string
api_version string
image string
harbor_password string
harbor_username string
env string
use_unsecure bool
remote_registry_url string
group_name string
state_artifact_name string
}

func GetLogLevel() string {
Expand Down Expand Up @@ -122,6 +125,22 @@ func GetHarborUsername() string {
return AppConfig.harbor_username
}

func SetRemoteRegistryURL(url string) {
AppConfig.remote_registry_url = url
}

func GetRemoteRegistryURL() string {
return AppConfig.remote_registry_url
}

func GetGroupName() string {
return AppConfig.group_name
}

func GetStateArtifactName() string {
return AppConfig.state_artifact_name
}

func LoadConfig() (*Config, error) {
viper.SetConfigName("config")
viper.SetConfigType("toml")
Expand All @@ -142,17 +161,19 @@ func LoadConfig() (*Config, error) {
}

return &Config{
log_level: viper.GetString("log_level"),
own_registry: viper.GetBool("bring_own_registry"),
own_registry_adr: viper.GetString("own_registry_adr"),
own_registry_port: viper.GetString("own_registry_port"),
zot_config_path: viper.GetString("zotConfigPath"),
input: viper.GetString("url_or_file"),
harbor_password: os.Getenv("HARBOR_PASSWORD"),
harbor_username: os.Getenv("HARBOR_USERNAME"),
env: os.Getenv("ENV"),
zot_url: os.Getenv("ZOT_URL"),
use_unsecure: use_unsecure,
log_level: viper.GetString("log_level"),
own_registry: viper.GetBool("bring_own_registry"),
own_registry_adr: viper.GetString("own_registry_adr"),
own_registry_port: viper.GetString("own_registry_port"),
zot_config_path: viper.GetString("zotConfigPath"),
input: viper.GetString("url_or_file"),
harbor_password: os.Getenv("HARBOR_PASSWORD"),
harbor_username: os.Getenv("HARBOR_USERNAME"),
env: os.Getenv("ENV"),
zot_url: os.Getenv("ZOT_URL"),
use_unsecure: use_unsecure,
group_name: os.Getenv("GROUP_NAME"),
state_artifact_name: os.Getenv("STATE_ARTIFACT_NAME"),
}, nil
}

Expand Down
180 changes: 180 additions & 0 deletions internal/config/state_aritfact.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package config
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved

import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"

"oras.land/oras-go/v2"
"oras.land/oras-go/v2/content/file"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
"oras.land/oras-go/v2/registry/remote/retry"
)

// Registry defines an interface for registry operations
type StateReader interface {
// GetRegistryURL returns the URL of the registry
GetRegistryURL() string
// GetRegistryType returns the list of artifacts that needs to be pulled
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
GetArtifacts() []ArtifactReader
// GetArtifactByRepository takes in the repository name and returns the artifact associated with it
GetArtifactByRepository(repo string) (ArtifactReader, error)
}

type State struct {
Registry string `json:"registry"`
Artifacts []Artifact `json:"artifacts"`
}

func NewState(artifact *Artifact) StateReader {
state := &State{
Registry: "",
Artifacts: []Artifact{*artifact},
}
return state
}

func (a *State) GetRegistryURL() string {
return a.Registry
}

func (a *State) GetArtifacts() []ArtifactReader {
var artifact_readers []ArtifactReader
for _, artifact := range a.Artifacts {
artifact_readers = append(artifact_readers, &artifact)
}
return artifact_readers
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved

func (a *State) GetArtifactByRepository(repo string) (ArtifactReader, error) {
for _, artifact := range a.Artifacts {
if artifact.GetRepository() == repo {
return &artifact, nil
}
}
return &Artifact{}, fmt.Errorf("artifact not found in the list")
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved

type StateArtifactFetcher interface {
// Fetches the state artifact from the registry
FetchStateArtifact() error
}

type URLStateArtifactFetcher struct {
url string
group_name string
state_artifact_name string
state_artifact_reader StateReader
}

func NewURLStateArtifactFetcher() StateArtifactFetcher {
url := GetRemoteRegistryURL()
// Trim the "https://" or "http://" prefix if present
if len(url) >= 8 && url[:8] == "https://" {
url = url[8:]
} else if len(url) >= 7 && url[:7] == "http://" {
url = url[7:]
}
artifact := NewArtifact()
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
state_artifact_reader := NewState(artifact.(*Artifact))
return &URLStateArtifactFetcher{
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
url: url,
group_name: GetGroupName(),
state_artifact_name: GetStateArtifactName(),
state_artifact_reader: state_artifact_reader,
}
}

type FileStateArtifactFetcher struct {
filePath string
}

func (f *URLStateArtifactFetcher) FetchStateArtifact() error {
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current working directory: %v", err)
}
// Creating a file store in the current working directory
fs, err := file.New(fmt.Sprintf("%s/state-artifact", cwd))
if err != nil {
return fmt.Errorf("failed to create file store: %v", err)
}
defer fs.Close()

ctx := context.Background()

repo, err := remote.NewRepository(fmt.Sprintf("%s/%s/%s", f.url, f.group_name, f.state_artifact_name))
if err != nil {
return fmt.Errorf("failed to create remote repository: %v", err)
}

// Setting up the authentication for the remote registry
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(
f.url,
auth.Credential{
Username: GetHarborUsername(),
Password: GetHarborPassword(),
},
),
}
// Copy from the remote repository to the file store
tag := "latest"
_, err = oras.Copy(ctx, repo, tag, fs, tag, oras.DefaultCopyOptions)
if err != nil {
return fmt.Errorf("failed to copy from remote repository to file store: %v", err)
}
stateArtifactDir := filepath.Join(cwd, "state-artifact")
// Find the state artifact file in the state-artifact directory that is created temporarily
err = filepath.Walk(stateArtifactDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if filepath.Ext(info.Name()) == ".json" {
content, err := os.ReadFile(path)
if err != nil {
return err
}
fmt.Printf("Contents of %s:\n", info.Name())
fmt.Println(string(content))

state_artifact_reader, err := FromJSON(content, f.state_artifact_reader.(*State))
if err != nil {
return fmt.Errorf("failed to parse the state artifact file: %v", err)
}
fmt.Println(state_artifact_reader)

}
return nil
})

if err != nil {
return fmt.Errorf("failed to read the state artifact file: %v", err)
}
// Clean up everything inside the state-artifact folder
err = os.RemoveAll(stateArtifactDir)
if err != nil {
return fmt.Errorf("failed to remove state-artifact directory: %v", err)
}
return nil
}

// FromJSON parses the input JSON data into a StateArtifactReader
func FromJSON(data []byte, reg *State) (StateReader, error) {

if err := json.Unmarshal(data, &reg); err != nil {
fmt.Print("Error in unmarshalling")
return nil, err
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
fmt.Print(reg)
// Validation
if reg.Registry == "" {
return nil, fmt.Errorf("registry URL is required")
}
return reg, nil
}
8 changes: 7 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,14 @@ func processInput(ctx context.Context, log *zerolog.Logger) (store.ImageFetcher,
}

log.Info().Msg("Input is a valid URL")
config.SetRemoteRegistryURL(input)
state_arifact_fetcher := config.NewURLStateArtifactFetcher()
if err := state_arifact_fetcher.FetchStateArtifact(); err != nil {
log.Error().Err(err).Msg("Error fetching state artifact")
return nil, err
}
Mehul-Kumar-27 marked this conversation as resolved.
Show resolved Hide resolved
fetcher := store.RemoteImageListFetcher(ctx, input)
utils.SetUrlConfig(input)
// utils.SetUrlConfig(input)
return fetcher, nil
}

Expand Down