Skip to content

Commit

Permalink
refactor: simplify archivista main cmd
Browse files Browse the repository at this point in the history
Simplifies Archivista main cmd moving all the service logic into the
`pkg/server`.

It allows users to use the Archivista Server instance as an Service
Interface allowing to integrate along to API services.

Signed-off-by: Kairo Araujo <[email protected]>
  • Loading branch information
kairoaraujo committed May 27, 2024
1 parent a5f6d84 commit b638f15
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 111 deletions.
123 changes: 12 additions & 111 deletions cmd/archivista/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ package main

import (
"context"
"fmt"
"net"
"net/http"
"os"
Expand All @@ -32,13 +31,7 @@ import (

nested "github.com/antonfisher/nested-logrus-formatter"
"github.com/gorilla/handlers"
"github.com/in-toto/archivista/internal/artifactstore"
"github.com/in-toto/archivista/internal/config"
"github.com/in-toto/archivista/internal/metadatastorage/sqlstore"
"github.com/in-toto/archivista/internal/objectstorage/blobstore"
"github.com/in-toto/archivista/internal/objectstorage/filestore"
"github.com/in-toto/archivista/internal/server"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/in-toto/archivista/pkg/server"
"github.com/sirupsen/logrus"
)

Expand All @@ -57,76 +50,19 @@ func main() {
defer cancel()

startTime := time.Now()
serverOpts := make([]server.Option, 0)

logrus.Infof("executing phase 1: get config from environment (time since start: %s)", time.Since(startTime))
now := time.Now()

cfg := new(config.Config)
if err := cfg.Process(); err != nil {
logrus.Fatal(err)
}

level, err := logrus.ParseLevel(cfg.LogLevel)
if err != nil {
logrus.Fatalf("invalid log level %s", cfg.LogLevel)
}
logrus.SetLevel(level)

logrus.WithField("duration", time.Since(now)).Infof("completed phase 1: get config from environment")

// ********************************************************************************
logrus.Infof("executing phase 2: initializing storage clients (time since start: %s)", time.Since(startTime))
// ********************************************************************************
now = time.Now()
fileStore, fileStoreCh, err := initObjectStore(ctx, cfg)
if err != nil {
logrus.Fatalf("error initializing storage clients: %+v", err)
}
serverOpts = append(serverOpts, server.WithObjectStore(fileStore))
archivistaService := &server.ArchivistaService{Ctx: ctx, Cfg: nil}

Check warning on line 54 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L54

Added line #L54 was not covered by tests

entClient, err := sqlstore.NewEntClient(
cfg.SQLStoreBackend,
cfg.SQLStoreConnectionString,
sqlstore.ClientWithMaxIdleConns(cfg.SQLStoreMaxIdleConnections),
sqlstore.ClientWithMaxOpenConns(cfg.SQLStoreMaxOpenConnections),
sqlstore.ClientWithConnMaxLifetime(cfg.SQLStoreConnectionMaxLifetime))
server, err := archivistaService.Setup()

Check warning on line 56 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L56

Added line #L56 was not covered by tests
if err != nil {
logrus.Fatalf("could not create ent client: %+v", err)
logrus.Fatalf("unable to setup archivista service: %+v", err)

Check warning on line 58 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L58

Added line #L58 was not covered by tests
}

sqlStore, sqlStoreCh, err := sqlstore.New(ctx, entClient)
if err != nil {
logrus.Fatalf("error initializing mysql client: %+v", err)
}
serverOpts = append(serverOpts, server.WithMetadataStore(sqlStore))

logrus.WithField("duration", time.Since(now)).Infof("completed phase 3: initializing storage clients")

// ********************************************************************************
logrus.Infof("executing phase 3: create and register http service (time since start: %s)", time.Since(startTime))
logrus.Infof("executing phase: create and register http service (time since start: %s)", time.Since(startTime))

Check warning on line 61 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L61

Added line #L61 was not covered by tests
// ********************************************************************************
now = time.Now()

// initialize the artifact store
if cfg.EnableArtifactStore {
wds, err := artifactstore.New(artifactstore.WithConfigFile(cfg.ArtifactStoreConfig))
if err != nil {
logrus.Fatalf("could not create the artifact store: %+v", err)
}

serverOpts = append(serverOpts, server.WithArtifactStore(wds))
}

// initialize the server
sqlClient := sqlStore.GetClient()
serverOpts = append(serverOpts, server.WithEntSqlClient(sqlClient))
server, err := server.New(cfg, serverOpts...)
if err != nil {
logrus.Fatalf("could not create archivista server: %+v", err)
}
now := time.Now()

Check warning on line 63 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L63

Added line #L63 was not covered by tests

listenAddress := cfg.ListenOn
listenAddress := archivistaService.Cfg.ListenOn

Check warning on line 65 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L65

Added line #L65 was not covered by tests
listenAddress = strings.ToLower(strings.TrimSpace(listenAddress))
proto := ""
if strings.HasPrefix(listenAddress, "tcp://") {
Expand All @@ -143,10 +79,10 @@ func main() {
}
srv := &http.Server{
Handler: handlers.CORS(
handlers.AllowedOrigins(cfg.CORSAllowOrigins),
handlers.AllowedOrigins(archivistaService.Cfg.CORSAllowOrigins),

Check warning on line 82 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L82

Added line #L82 was not covered by tests
handlers.AllowedMethods([]string{"GET", "POST", "OPTIONS"}),
handlers.AllowedHeaders([]string{"Accept", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization"}),
)(server.Router()),
)(server.Router()),

Check warning on line 85 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L85

Added line #L85 was not covered by tests
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
Expand All @@ -156,47 +92,12 @@ func main() {
}
}()

logrus.WithField("duration", time.Since(now)).Infof("completed phase 5: create and register http service")
logrus.WithField("duration", time.Since(now)).Infof("completed phase: create and register http service")

Check warning on line 95 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L95

Added line #L95 was not covered by tests
logrus.Infof("startup complete (time since start: %s)", time.Since(startTime))

<-ctx.Done()
<-fileStoreCh
<-sqlStoreCh
<-archivistaService.GetFileStoreCh()
<-archivistaService.GetSQLStoreCh()

Check warning on line 100 in cmd/archivista/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/archivista/main.go#L99-L100

Added lines #L99 - L100 were not covered by tests

logrus.Infof("exiting, uptime: %v", time.Since(startTime))
}

func initObjectStore(ctx context.Context, cfg *config.Config) (server.StorerGetter, <-chan error, error) {
switch strings.ToUpper(cfg.StorageBackend) {
case "FILE":
return filestore.New(ctx, cfg.FileDir, cfg.FileServeOn)

case "BLOB":
var creds *credentials.Credentials
if cfg.BlobStoreCredentialType == "IAM" {
creds = credentials.NewIAM("")
} else if cfg.BlobStoreCredentialType == "ACCESS_KEY" {
creds = credentials.NewStaticV4(cfg.BlobStoreAccessKeyId, cfg.BlobStoreSecretAccessKeyId, "")
} else {
logrus.Fatalln("invalid blob store credential type: ", cfg.BlobStoreCredentialType)
}
return blobstore.New(
ctx,
cfg.BlobStoreEndpoint,
creds,
cfg.BlobStoreBucketName,
cfg.BlobStoreUseTLS,
)

case "":
errCh := make(chan error)
go func() {
<-ctx.Done()
close(errCh)
}()
return nil, errCh, nil

default:
return nil, nil, fmt.Errorf("unknown storage backend: %s", cfg.StorageBackend)
}
}
191 changes: 191 additions & 0 deletions pkg/server/services.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Copyright 2024 The Archivista Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A note: this follows a pattern followed by network service mesh.
// The pattern was copied from the Network Service Mesh Project
// and modified for use here. The original code was published under the
// Apache License V2.

package server

import (
"context"
"fmt"
"strings"
"time"

"github.com/in-toto/archivista/pkg/artifactstore"
"github.com/in-toto/archivista/pkg/config"
"github.com/in-toto/archivista/pkg/metadatastorage/sqlstore"
"github.com/in-toto/archivista/pkg/objectstorage/blobstore"
"github.com/in-toto/archivista/pkg/objectstorage/filestore"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/sirupsen/logrus"
)

// Service is the interface for the Archivista service
type Service interface {
Setup() (Server, error)
GetConfig() *config.Config
GetFileStoreCh() chan error
GetSQLStoreCh() chan error
}

// ArchivistaService is the implementation of the Archivista service
type ArchivistaService struct {
Ctx context.Context // context for the service
Cfg *config.Config // configuration for the service (if none it uses environment variables)
fileStoreCh <-chan error
sqlStoreCh <-chan error
}

// Setup Archivista Service
func (a *ArchivistaService) Setup() (*Server, error) {
var (
level logrus.Level
err error
sqlStore *sqlstore.Store
fileStore StorerGetter
)
serverOpts := make([]Option, 0)

Check warning on line 61 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L54-L61

Added lines #L54 - L61 were not covered by tests

startTime := time.Now()
now := time.Now()
if a.Cfg == nil {

Check warning on line 65 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L63-L65

Added lines #L63 - L65 were not covered by tests

logrus.Infof("executing: get config from environment (time since start: %s)", time.Since(startTime))

Check warning on line 67 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L67

Added line #L67 was not covered by tests

a.Cfg = new(config.Config)
if err := a.Cfg.Process(); err != nil {
logrus.Fatal(err)

Check warning on line 71 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L69-L71

Added lines #L69 - L71 were not covered by tests
}
level, err = logrus.ParseLevel(a.Cfg.LogLevel)
if err != nil {
logrus.Fatalf("invalid log level %s", a.Cfg.LogLevel)

Check warning on line 75 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L73-L75

Added lines #L73 - L75 were not covered by tests
}
logrus.WithField("duration", time.Since(now)).Infof("completed phase: get config from environment")
} else {
logrus.Infof("executing: load given config (time since start: %s)", time.Since(startTime))
level, err = logrus.ParseLevel(a.Cfg.LogLevel)
if err != nil {
logrus.Fatalf("invalid log level %s", a.Cfg.LogLevel)

Check warning on line 82 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L77-L82

Added lines #L77 - L82 were not covered by tests
}
logrus.WithField("duration", time.Since(now)).Infof("completed phase: load given config")

Check warning on line 84 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L84

Added line #L84 was not covered by tests
}
logrus.SetLevel(level)

Check warning on line 86 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L86

Added line #L86 was not covered by tests

// ********************************************************************************
logrus.Infof("executing phase: initializing storage clients (time since start: %s)", time.Since(startTime))

Check warning on line 89 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L89

Added line #L89 was not covered by tests
// ********************************************************************************
now = time.Now()
fileStore, a.fileStoreCh, err = a.initObjectStore()
if err != nil {
logrus.Fatalf("could not create object store: %+v", err)

Check warning on line 94 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L91-L94

Added lines #L91 - L94 were not covered by tests
}
serverOpts = append(serverOpts, WithObjectStore(fileStore))

Check warning on line 96 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L96

Added line #L96 was not covered by tests

entClient, err := sqlstore.NewEntClient(
a.Cfg.SQLStoreBackend,
a.Cfg.SQLStoreConnectionString,
sqlstore.ClientWithMaxIdleConns(a.Cfg.SQLStoreMaxIdleConnections),
sqlstore.ClientWithMaxOpenConns(a.Cfg.SQLStoreMaxOpenConnections),
sqlstore.ClientWithConnMaxLifetime(a.Cfg.SQLStoreConnectionMaxLifetime),
)

Check warning on line 104 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L98-L104

Added lines #L98 - L104 were not covered by tests

if err != nil {
logrus.Fatalf("could not create ent client: %+v", err)

Check warning on line 107 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L106-L107

Added lines #L106 - L107 were not covered by tests
}

// Continue with the existing setup code for the SQLStore
sqlStore, a.sqlStoreCh, err = sqlstore.New(context.Background(), entClient)
if err != nil {
logrus.Fatalf("error initializing new SQLStore: %+v", err)

Check warning on line 113 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L111-L113

Added lines #L111 - L113 were not covered by tests
}
serverOpts = append(serverOpts, WithMetadataStore(sqlStore))

Check warning on line 115 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L115

Added line #L115 was not covered by tests

// Add SQL client for ent
sqlClient := sqlStore.GetClient()
serverOpts = append(serverOpts, WithEntSqlClient(sqlClient))

Check warning on line 119 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L118-L119

Added lines #L118 - L119 were not covered by tests

// initialize the artifact store
if a.Cfg.EnableArtifactStore {
wds, err := artifactstore.New(artifactstore.WithConfigFile(a.Cfg.ArtifactStoreConfig))
if err != nil {
logrus.Fatalf("could not create the artifact store: %+v", err)

Check warning on line 125 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L122-L125

Added lines #L122 - L125 were not covered by tests
}

serverOpts = append(serverOpts, WithArtifactStore(wds))

Check warning on line 128 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L128

Added line #L128 was not covered by tests
}

// Create the Archivista server with all options
server, err := New(a.Cfg, serverOpts...)
if err != nil {
logrus.Fatalf("could not create archivista server: %+v", err)

Check warning on line 134 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L132-L134

Added lines #L132 - L134 were not covered by tests
}

// Ensure background processes are managed
go func() {
<-a.sqlStoreCh
<-a.fileStoreCh
}()

Check warning on line 141 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L138-L141

Added lines #L138 - L141 were not covered by tests

logrus.WithField("duration", time.Since(now)).Infof("completed phase: initializing storage clients")

Check warning on line 143 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L143

Added line #L143 was not covered by tests

return &server, nil

Check warning on line 145 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L145

Added line #L145 was not covered by tests
}

// GetFileStoreCh returns the file store channel
func (a *ArchivistaService) GetFileStoreCh() <-chan error {
return a.fileStoreCh

Check warning on line 150 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L149-L150

Added lines #L149 - L150 were not covered by tests
}

// GetSQLStoreCh returns the SQL store channel
func (a *ArchivistaService) GetSQLStoreCh() <-chan error {
return a.sqlStoreCh

Check warning on line 155 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L154-L155

Added lines #L154 - L155 were not covered by tests
}

func (a *ArchivistaService) initObjectStore() (StorerGetter, <-chan error, error) {
switch strings.ToUpper(a.Cfg.StorageBackend) {
case "FILE":
return filestore.New(a.Ctx, a.Cfg.FileDir, a.Cfg.FileServeOn)

Check warning on line 161 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L158-L161

Added lines #L158 - L161 were not covered by tests

case "BLOB":
var creds *credentials.Credentials
if a.Cfg.BlobStoreCredentialType == "IAM" {
creds = credentials.NewIAM("")
} else if a.Cfg.BlobStoreCredentialType == "ACCESS_KEY" {
creds = credentials.NewStaticV4(a.Cfg.BlobStoreAccessKeyId, a.Cfg.BlobStoreSecretAccessKeyId, "")
} else {
logrus.Fatalf("invalid blob store credential type: %s", a.Cfg.BlobStoreCredentialType)

Check warning on line 170 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L163-L170

Added lines #L163 - L170 were not covered by tests
}
return blobstore.New(
a.Ctx,
a.Cfg.BlobStoreEndpoint,
creds,
a.Cfg.BlobStoreBucketName,
a.Cfg.BlobStoreUseTLS,
)

Check warning on line 178 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L172-L178

Added lines #L172 - L178 were not covered by tests

case "":
errCh := make(chan error)
go func() {
<-a.Ctx.Done()
close(errCh)
}()
return nil, errCh, nil

Check warning on line 186 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L180-L186

Added lines #L180 - L186 were not covered by tests

default:
return nil, nil, fmt.Errorf("unknown storage backend: %s", a.Cfg.StorageBackend)

Check warning on line 189 in pkg/server/services.go

View check run for this annotation

Codecov / codecov/patch

pkg/server/services.go#L188-L189

Added lines #L188 - L189 were not covered by tests
}
}

0 comments on commit b638f15

Please sign in to comment.