Skip to content

Commit

Permalink
check: Unify app bundle integrity check
Browse files Browse the repository at this point in the history
Unify and simplify info about app bundle integrity issues. Use and print
the same format across all use-cases.

Signed-off-by: Mike Sul <[email protected]>
  • Loading branch information
mike-sul committed Dec 10, 2024
1 parent 71c8316 commit 2758a61
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 64 deletions.
60 changes: 16 additions & 44 deletions cmd/composectl/cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
v1 "github.com/foundriesio/composeapp/pkg/compose/v1"
"github.com/opencontainers/go-digest"
"github.com/spf13/cobra"
"io/fs"
"os"
"path"
)
Expand Down Expand Up @@ -50,11 +49,9 @@ type (
}

appInstallCheckResult struct {
AppName string `json:"app_name"`
MissingImages []string `json:"missing_images"`
MissingComposeFiles []string `json:"missing_compose_files"`
InvalidComposeFiles []string `json:"invalid_compose_files"`
ErrorComposeFiles []string `json:"error_compose_files"`
AppName string `json:"app_name"`
MissingImages []string `json:"missing_images"`
BundleErrors compose.AppBundleErrs `json:"bundle_errors"`
}

InstallCheckResult map[string]*appInstallCheckResult
Expand Down Expand Up @@ -127,25 +124,20 @@ func checkAppsCmd(cmd *cobra.Command, args []string, opts *checkOptions) {
cr.print()
if opts.CheckInstall {
for appRef, r := range ir {
if len(r.InvalidComposeFiles) > 0 || len(r.MissingComposeFiles) > 0 || len(r.MissingImages) > 0 || len(r.ErrorComposeFiles) > 0 {
if len(r.MissingImages) > 0 || len(r.BundleErrors) > 0 {
fmt.Printf("%s is not installed (%s)\n", r.AppName, appRef)
for _, issue := range []struct {
issueType string
issueValues []string
}{
{"missing images", r.MissingImages},
{"missing compose files", r.MissingComposeFiles},
{"invalid compose files", r.InvalidComposeFiles},
{"error compose files", r.ErrorComposeFiles},
} {
if len(issue.issueValues) == 0 {
continue
}
fmt.Printf("\t%s:\n", issue.issueType)
for _, val := range issue.issueValues {
if len(r.MissingImages) > 0 {
fmt.Println("\tmissing images:")
for _, val := range r.MissingImages {
fmt.Println("\t\t" + val)
}
}
if len(r.BundleErrors) > 0 {
fmt.Println("\tapp bundle errors:")
for f, e := range r.BundleErrors {
fmt.Printf("\t\t%s:\t%s\n", f, e)
}
}
}
}
}
Expand Down Expand Up @@ -313,30 +305,10 @@ func checkIfInstalled(ctx context.Context, appRefs []string, srcStorePath string
if err != nil {
return nil, err
}
var missingComposeFiles []string
var invalidComposeFiles []string
var errComposeFiles []string
for filePath, checkErr := range errMap {
switch checkErr.(type) {
case *compose.ErrBlobDigestMismatch, *compose.ErrBlobSizeMismatch, *compose.ErrBlobSizeLimitExceed:
{
invalidComposeFiles = append(invalidComposeFiles, filePath)
}
case *fs.PathError:
{
missingComposeFiles = append(missingComposeFiles, filePath)
}
default:
errComposeFiles = append(errComposeFiles, fmt.Sprintf("%s: %s", filePath, checkErr.Error()))
}
}

checkResult[appRef] = &appInstallCheckResult{
AppName: app.Name(),
MissingImages: missingImages,
InvalidComposeFiles: invalidComposeFiles,
MissingComposeFiles: missingComposeFiles,
ErrorComposeFiles: errComposeFiles,
AppName: app.Name(),
MissingImages: missingImages,
BundleErrors: errMap,
}
}
return checkResult, nil
Expand Down
27 changes: 16 additions & 11 deletions cmd/composectl/cmd/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ type (
Health string `json:"health,omitempty"`
}
App struct {
URI string `json:"uri"`
Name string `json:"name"`
State string `json:"state"`
Services []*Service `json:"services"`
InStore bool `json:"in_store"`
URI string `json:"uri"`
Name string `json:"name"`
State string `json:"state"`
Services []*Service `json:"services"`
InStore bool `json:"in_store"`
BundleErrors compose.AppBundleErrs `json:"bundle_errors,omitempty"`
}
ServiceStatus struct {
URI string `json:"uri"`
ID string `json:"id"`
CfgHash string `json:"cfg-hash"`
State string `json:"state"`
State string `json:"statAe"`
Status string `json:"status"`
}
AppStatus []ServiceStatus
Expand Down Expand Up @@ -171,22 +172,26 @@ func getAppsStatus(ctx context.Context, appRefs []string, runningApps map[string
appState = "not running"
}
}

var bundleErrors compose.AppBundleErrs
if appState == "running" {
errMap, err := app.CheckComposeInstallation(ctx, storeBlobProvider, path.Join(config.ComposeRoot, app.Name()))
if err == nil {
if len(errMap) > 0 {
appState = "running invalid bundle"
bundleErrors = errMap
}
} else {
fmt.Printf("failed to check whether app bundle is installed")
}
}
appStatuses[appRef] = &App{
URI: appRef,
Name: app.Name(),
State: appState,
Services: appServices,
InStore: true,
URI: appRef,
Name: app.Name(),
State: appState,
Services: appServices,
InStore: true,
BundleErrors: bundleErrors,
}
}

Expand Down
7 changes: 4 additions & 3 deletions pkg/compose/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ type (
Digest digest.Digest
}

AppTree TreeNode
App interface {
AppBundleErrs map[string]string
AppTree TreeNode
App interface {
Name() string
Ref() *AppRef
HasLayersMeta(arch string) bool
GetBlobRuntimeSize(desc *ocispec.Descriptor, arch string, blockSize int64) int64
GetComposeRoot() *TreeNode
GetCompose(ctx context.Context, provider BlobProvider) (*composetypes.Project, error)
CheckComposeInstallation(ctx context.Context, provider BlobProvider, installationRootDir string) (map[string]error, error)
CheckComposeInstallation(ctx context.Context, provider BlobProvider, installationRootDir string) (AppBundleErrs, error)
}
AppLoader interface {
LoadAppTree(context.Context, BlobProvider, platforms.MatchComparer, string) (App, *AppTree, error)
Expand Down
15 changes: 9 additions & 6 deletions pkg/compose/v1/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,27 +246,30 @@ func (a *appCtx) GetLayersMetadataDescriptor() (*ocispec.Descriptor, error) {
return &desc, nil
}

func (a *appCtx) CheckComposeInstallation(ctx context.Context, provider compose.BlobProvider, installationRootDir string) (map[string]error, error) {
func (a *appCtx) CheckComposeInstallation(ctx context.Context, provider compose.BlobProvider, installationRootDir string) (bundleErrs compose.AppBundleErrs, err error) {
appIndex, err := a.getAppBundleIndex(ctx, provider)
if err != nil && err != ErrAppIndexNotFound {
return nil, err
return
}
errMap := map[string]error{}
bundleErrMap := compose.AppBundleErrs{}
for filePath, fileDigest := range appIndex {
f, err := os.Open(path.Join(installationRootDir, filePath))
if os.IsNotExist(err) {
errMap[filePath] = err
bundleErrMap[filePath] = err.Error()
continue
}
r, err := compose.NewSecureReadCloser(f, compose.WithExpectedDigest(fileDigest), compose.WithReadLimit(AppBundleFileMaxSize))
if err != nil {
return nil, err
}
if _, err := io.ReadAll(r); err != nil {
errMap[filePath] = err
bundleErrMap[filePath] = err.Error()
}
}
return errMap, nil
if len(bundleErrMap) > 0 {
bundleErrs = bundleErrMap
}
return
}

func (a *appCtx) getAppBundleIndex(ctx context.Context, blobProvider compose.BlobProvider) (map[string]digest.Digest, error) {
Expand Down

0 comments on commit 2758a61

Please sign in to comment.