Skip to content

Commit

Permalink
Merge pull request #93 from meltred/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
KunalSin9h committed Mar 10, 2024
2 parents 3c019d3 + f26bfbe commit 17ef92c
Show file tree
Hide file tree
Showing 17 changed files with 232 additions and 90 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

![Discord](https://img.shields.io/discord/1086894797622624257)

> [!Caution]
> `meltcd` is very far from production use, unless we achieve **1.0.0**
> [!Caution] > `meltcd` is very far from production use, unless we achieve **1.0.0**
Argo-cd like GitDevOps Continuous Development platform for docker swarm.

Expand Down Expand Up @@ -88,6 +87,10 @@ make run

This will start the server on port `11771`

> [!TIP]
> If you get error saying **"Error response from daemon: This node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again."**
> This means you have docker working but the node is not a `Docker Swarm` Node, to make it run `docker swarm init`.
Go to **Developer Docs** for more info. [Developer Docs](https://github.com/meltred/meltcd/tree/main/docs/dev)

## Contributing
Expand Down
8 changes: 5 additions & 3 deletions cmd/meltcd/meltcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,14 @@ func NewCLI() *cobra.Command {

// meltcd repo add https://github.com/... --username "" --password ""
repoAddCmd := &cobra.Command{
Use: "add REPO_URL",
Short: "Add a private git repository",
Use: "add REPO",
Short: "Add a private git repository (--git) or image registry (--image)",
Args: cobra.ExactArgs(1), // the git repo url
RunE: addPrivateGitRepository,
RunE: addPrivateRepository,
}

repoAddCmd.Flags().Bool("git", false, "if private repo is a git repository")
repoAddCmd.Flags().Bool("image", false, "if private repo is a docker image")
repoAddCmd.Flags().String("username", "", "username for basic auth")
repoAddCmd.MarkFlagRequired("username")
repoAddCmd.Flags().String("password", "", "password for basic auth")
Expand Down
38 changes: 29 additions & 9 deletions cmd/meltcd/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,27 @@ import (
"github.com/spf13/cobra"
)

func addPrivateGitRepository(cmd *cobra.Command, args []string) error {
repoURL := args[0]
repoURL, _ = strings.CutSuffix(repoURL, "/")
func addPrivateRepository(cmd *cobra.Command, args []string) error {
repoName := args[0]

git, _ := cmd.Flags().GetBool("git")
image, _ := cmd.Flags().GetBool("image")
username, _ := cmd.Flags().GetString("username")
password, _ := cmd.Flags().GetString("password")

payload := repo.PrivateRepoDetails{
URL: repoURL,
Username: username,
Password: password,
}

// if not git then if image is also false, then default is git
if git || (!git && !image) {
repoName, _ = strings.CutSuffix(repoName, "/")
payload.URL = repoName
} else if image {
payload.ImageRef = repoName
}

buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(payload); err != nil {
return err
Expand Down Expand Up @@ -99,14 +107,26 @@ func getAllRepoAdded(_ *cobra.Command, _ []string) error {
return err
}

table := table.New("S.NO", "Repository URL", "Reachable")
table.WithHeaderFormatter(util.HeaderFmt).WithFirstColumnFormatter(util.ColumnFmt)
tableRepositoryGit := table.New("S.NO", "Repository URL", "Reachable")
tableRepositoryGit.WithHeaderFormatter(util.HeaderFmt).WithFirstColumnFormatter(util.ColumnFmt)

tableContainerImage := table.New("S.NO", "Container Image", "Reachable")
tableContainerImage.WithHeaderFormatter(util.HeaderFmt).WithFirstColumnFormatter(util.ColumnFmt)

for idx, repo := range resData.Data {
table.AddRow(idx, repo.URL, repo.Reachable)
i, j := 1, 1
for _, repo := range resData.Data {
if repo.URL != "" {
tableRepositoryGit.AddRow(i, repo.URL, repo.Reachable)
i++
} else {
tableContainerImage.AddRow(j, repo.ImageRef, repo.Reachable)
j++
}
}

table.Print()
tableRepositoryGit.Print()
fmt.Println()
tableContainerImage.Print()
return nil
}

Expand Down
39 changes: 36 additions & 3 deletions internal/core/application/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ func (app *Application) Run() {
ticker := time.NewTicker(time.Minute * 3)
defer ticker.Stop()

// updateTicker is for updating the refresh timer, since app settings
// can be changed at run time (Run() function) so we have to update the timer in every loop.
if err := updateTicker(app.RefreshTimer, ticker); err != nil {
slog.Error(err.Error())
app.Health = Suspended
Expand Down Expand Up @@ -174,7 +176,7 @@ func (app *Application) GetState() (string, error) {
// defer clear storage, i (kunal singh) think that when storage goes out-of-scope
// it is cleared

username, password := repository.Find(app.Source.RepoURL)
username, password := repository.FindCreds(app.Source.RepoURL)

// TODO: Improvement
// GET the name and commit also
Expand Down Expand Up @@ -272,10 +274,39 @@ func (app *Application) Apply(targetState string) error {
}

for _, service := range services {
repo, found := repository.FindRepo(service.TaskTemplate.ContainerSpec.Image)
auth := ""

if !found {
slog.Error("Repository not found in private image registries")
} else {
authString, err := repo.GetRegistryAuth()
if err != nil {
slog.Error(err.Error())
} else {
auth = authString
}
}

// Checking if docker image is pullabel, if not then making the app health degraded.
go func(cli *client.Client, a *Application) {
// docker will not work if image is not reacheble\
_, err = cli.ImagePull(context.TODO(), service.TaskTemplate.ContainerSpec.Image, types.ImagePullOptions{
RegistryAuth: auth,
})

if err != nil {
slog.Error("Failed to pull docker image, registry auth is required")
a.Health = Degraded
}
}(cli, app)

// check if already exists then only update
if svc, exists := checkServiceAlreadyExist(service.Name, &allServicesRunning); exists {
slog.Info("Service already running", "name", service.Name)
res, err := cli.ServiceUpdate(context.Background(), svc.ID, svc.Version, service, types.ServiceUpdateOptions{})
res, err := cli.ServiceUpdate(context.Background(), svc.ID, svc.Version, service, types.ServiceUpdateOptions{
EncodedRegistryAuth: auth,
})
if err != nil {
app.Health = Degraded
slog.Error("Not able to update a running service", "error", err.Error())
Expand All @@ -290,7 +321,9 @@ func (app *Application) Apply(targetState string) error {
}

slog.Info("Creating new service")
res, err := cli.ServiceCreate(context.Background(), service, types.ServiceCreateOptions{})
res, err := cli.ServiceCreate(context.Background(), service, types.ServiceCreateOptions{
EncodedRegistryAuth: auth,
})
if err != nil {
app.Health = Degraded
slog.Error("Not able to create a new service", "error", err.Error())
Expand Down
91 changes: 72 additions & 19 deletions internal/core/repository/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,25 @@ limitations under the License.
package repository

import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"strings"

"log/slog"

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/storage/memory"
)

type Repository struct {
URL, Secret string
Reachable bool
URL, ImageRef, Secret string
Reachable bool
}

var repositories []*Repository
Expand All @@ -41,6 +44,22 @@ func (r *Repository) saveCredential(username, password string) {
r.Secret = base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
}

func (r *Repository) GetRegistryAuth() (string, error) {
username, password := r.getCredential()

payload := map[string]string{
"username": username,
"password": password,
}

d, err := json.Marshal(payload)
if err != nil {
return "", err
}

return base64.URLEncoding.EncodeToString(d), nil
}

func (r *Repository) getCredential() (username, password string) {
d, err := base64.StdEncoding.DecodeString(r.Secret)
if err != nil {
Expand All @@ -61,31 +80,65 @@ func (r *Repository) getCredential() (username, password string) {
}

func (r *Repository) checkReachability(username, password string) {
fs := memfs.New()
storage := memory.NewStorage()

_, err := git.Clone(storage, fs, &git.CloneOptions{
URL: r.URL,
SingleBranch: true,
Depth: 1,
Auth: &http.BasicAuth{
Username: username,
Password: password,
},
})

if err != nil {
r.Reachable = false
if r.URL != "" {

Check failure on line 83 in internal/core/repository/add.go

View workflow job for this annotation

GitHub Actions / lint

ifElseChain: rewrite if-else to switch statement (gocritic)
fs := memfs.New()
storage := memory.NewStorage()

_, err := git.Clone(storage, fs, &git.CloneOptions{
URL: r.URL,
SingleBranch: true,
Depth: 1,
Auth: &http.BasicAuth{
Username: username,
Password: password,
},
})

if err != nil {
r.Reachable = false
}
} else if r.ImageRef != "" {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
slog.Error(err.Error())
r.Reachable = false
return
}

auth, err := r.GetRegistryAuth()
if err != nil {
slog.Error(err.Error())
r.Reachable = false
return
}

_, err = cli.ImagePull(context.Background(), r.ImageRef, types.ImagePullOptions{
RegistryAuth: auth,
})

if err != nil {
slog.Error(err.Error())
r.Reachable = false
return
}

r.Reachable = true
} else {
slog.Error("Both url and image ref is empty")
}
}

func Add(url, username, password string) error {
repo, found := findRepo(url)
// url is git url or container image name
func Add(url, imageRef, username, password string) error {
// since eight url is there of imageRef,
name := url + imageRef
repo, found := FindRepo(name)
if found {
return errors.New("repository with same url already exists")
}

repo.URL = url
repo.ImageRef = imageRef
repo.saveCredential(username, password)
repo.Reachable = true

Expand Down
19 changes: 11 additions & 8 deletions internal/core/repository/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@ limitations under the License.
package repository

import (
"strings"

"log/slog"
"strings"
)

func Find(repoURL string) (string, string) {
repoURL, _ = strings.CutSuffix(repoURL, "/")

repo, found := findRepo(repoURL)
func FindCreds(repoName string) (string, string) {
repo, found := FindRepo(repoName)
if !found {
return "", ""
}
Expand All @@ -40,9 +37,15 @@ func Find(repoURL string) (string, string) {
return username, password
}

func findRepo(url string) (*Repository, bool) {
func FindRepo(name string) (*Repository, bool) {
for _, x := range repositories {
if x.URL == url || x.URL+".git" == url || x.URL == url+".git" {
var image string
imageWithOutTags := strings.Split(name, ":")
if len(imageWithOutTags) >= 1 {
image = imageWithOutTags[0]
}

if x.URL == name || x.URL+".git" == name || x.URL == name+".git" || x.ImageRef == image {
return x, true
}
}
Expand Down
2 changes: 2 additions & 0 deletions internal/core/repository/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package repository

type RepoData struct {
ImageRef string `json:"image_ref"`
URL string `json:"url"`
Reachable bool `json:"reachable"`
}
Expand All @@ -27,6 +28,7 @@ func List() []RepoData {
for _, repo := range repositories {
res = append(res, RepoData{
URL: repo.URL,
ImageRef: repo.ImageRef,
Reachable: repo.Reachable,
})
}
Expand Down
8 changes: 2 additions & 6 deletions internal/core/repository/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,11 @@ limitations under the License.

package repository

import "strings"

func Remove(repoURL string) error {
repoURL, _ = strings.CutSuffix(repoURL, "/")

func Remove(repoName string) error {
tmp := make([]*Repository, 0)

for _, repo := range repositories {
if repo.URL != repoURL {
if repo.URL != repoName && repo.ImageRef != repoName {
tmp = append(tmp, repo)
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/core/repository/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
)

func Update(url, username, password string) error {
repo, found := findRepo(url)
repo, found := FindRepo(url)
if !found {
return errors.New("repository does not exists")
}
Expand Down
Loading

0 comments on commit 17ef92c

Please sign in to comment.