Skip to content

Commit 138d9c5

Browse files
feat: added oci registry pull and cosign verify mechanism
1 parent fffb13a commit 138d9c5

File tree

11 files changed

+445
-152
lines changed

11 files changed

+445
-152
lines changed

cmd/artifacts/cosign/verifier.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package cosign
2+
3+
import (
4+
"context"
5+
"fmt"
6+
log "github.com/sirupsen/logrus"
7+
"os/exec"
8+
)
9+
10+
func VerifyImage(ctx context.Context) error {
11+
artifactURL := ctx.Value("artifactURL").(string)
12+
//publicKeyPath := ctx.Value("publicKeyPath").(string)
13+
publicKeyPath := "/Users/mahendrabagul/DevEnv/intelops/cosign-keys/cosign.pub"
14+
// Construct the command to verify the image
15+
cmd := exec.Command("cosign", "verify", "--key", publicKeyPath, artifactURL)
16+
17+
// Capture the output and error if any
18+
output, err := cmd.CombinedOutput()
19+
if err != nil {
20+
return fmt.Errorf("verification failed: %w\nOutput: %s", err, output)
21+
}
22+
23+
log.Infof("Verification succeeded:\n%s", output)
24+
return nil
25+
}

cmd/git-checker/git-commands.go renamed to cmd/artifacts/git/commit.go

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package git_checker
1+
package git
22

33
import (
44
"errors"
@@ -7,24 +7,39 @@ import (
77
"github.com/go-git/go-git/v5/config"
88
"github.com/go-git/go-git/v5/storage/memory"
99
log "github.com/sirupsen/logrus"
10+
"os"
1011
)
1112

12-
func CheckIfSHACommitSimilar(repoPath, repoURL, branchName string) (bool, error) {
13+
func GetRepositoryURLByLanguage(language, version string) (string, string, error) {
14+
userHomeDir, err := os.UserHomeDir()
15+
if err != nil {
16+
return "", "", err
17+
}
18+
repositoryPath := userHomeDir + "/.compage/templates/compage-template-" + language + "/" + version
19+
repositoryURL := "https://github.com/intelops/compage-template-" + language + ".git"
20+
if language == "common" {
21+
repositoryPath = userHomeDir + "/.compage/templates/common-templates/" + version
22+
return "https://github.com/intelops/common-templates.git", repositoryPath, nil
23+
}
24+
return repositoryURL, repositoryPath, nil
25+
}
26+
27+
func CheckIfSHACommitSimilar(repositoryURL, repositoryPath, version string) (bool, error) {
1328
// Get local commit SHA
14-
localSHA, err := GetLocalGitCommitSHA(repoPath)
29+
localSHA, err := GetLocalGitCommitSHA(repositoryPath)
1530
if err != nil {
1631
log.Error("Error fetching local Git commit SHA:", err)
1732
return false, err
1833
}
1934

2035
// Get remote commit SHA
21-
remoteSHA, err := GetRemoteGitCommitSHA(repoURL, branchName)
36+
remoteSHA, err := GetRemoteGitCommitSHA(repositoryURL, version)
2237
if err != nil {
2338
log.Errorf("Error fetching remote Git commit SHA: %s", err)
2439
return false, err
2540
}
2641

27-
isDirty, err := IsRepoDirty(repoPath)
42+
isDirty, err := IsRepositoryDirty(repositoryPath)
2843
if err != nil {
2944
log.Errorf("Error checking if repository is dirty: %s", err)
3045
return false, err
@@ -37,10 +52,9 @@ func CheckIfSHACommitSimilar(repoPath, repoURL, branchName string) (bool, error)
3752
return localSHA == remoteSHA, nil
3853
}
3954

40-
func GetLocalGitCommitSHA(repoPath string) (string, error) {
41-
log.Debugf("repoPath: %s", repoPath)
55+
func GetLocalGitCommitSHA(repositoryPath string) (string, error) {
4256
// Open the given repository
43-
r, err := git.PlainOpen(repoPath)
57+
r, err := git.PlainOpen(repositoryPath)
4458
if err != nil {
4559
log.Errorf("Error opening repository: %s", err)
4660
return "", err
@@ -59,11 +73,11 @@ func GetLocalGitCommitSHA(repoPath string) (string, error) {
5973
return commitSHA, nil
6074
}
6175

62-
func GetRemoteGitCommitSHA(repoURL, branchName string) (string, error) {
76+
func GetRemoteGitCommitSHA(repositoryURL, version string) (string, error) {
6377
// List remote references
6478
references, err := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{
6579
Name: "origin",
66-
URLs: []string{repoURL},
80+
URLs: []string{repositoryURL},
6781
}).List(&git.ListOptions{})
6882
if err != nil {
6983
log.Errorf("Error listing remote references: %s", err)
@@ -72,24 +86,24 @@ func GetRemoteGitCommitSHA(repoURL, branchName string) (string, error) {
7286

7387
// Look for the branch reference
7488
for _, ref := range references {
75-
if ref.Name().String() == "refs/heads/"+branchName {
89+
if ref.Name().String() == "refs/tags/"+version {
7690
return ref.Hash().String(), nil
7791
}
7892
}
7993

80-
return "", fmt.Errorf("branch %s not found", branchName)
94+
return "", fmt.Errorf("branch %s not found", version)
8195
}
8296

83-
func IsRepoDirty(repoPath string) (bool, error) {
97+
func IsRepositoryDirty(repositoryPath string) (bool, error) {
8498
// Open the given repository
85-
repo, err := git.PlainOpen(repoPath)
99+
repository, err := git.PlainOpen(repositoryPath)
86100
if err != nil {
87101
log.Errorf("Error opening repository: %s", err)
88102
return false, err
89103
}
90104

91105
// Get the working directory for the repository
92-
worktree, err := repo.Worktree()
106+
worktree, err := repository.Worktree()
93107
if err != nil {
94108
log.Errorf("Error getting working directory: %s", err)
95109
return false, err
@@ -105,3 +119,16 @@ func IsRepoDirty(repoPath string) (bool, error) {
105119
// The repository is dirty if there are any changes in the status
106120
return !status.IsClean(), nil
107121
}
122+
123+
func IsCommitSimilar(repositoryURL, repositoryPath, version string) bool {
124+
commitSimilar, err := CheckIfSHACommitSimilar(repositoryURL, repositoryPath, version)
125+
if err != nil {
126+
log.Errorf("error while checking the commit sha [" + err.Error() + "]")
127+
return false
128+
}
129+
if !commitSimilar {
130+
log.Errorf("the templates are not matching with the latest commit, please pull the latest templates")
131+
return false
132+
}
133+
return true
134+
}

cmd/artifacts/oci-registry/pull.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package oci_registry
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
spec "github.com/opencontainers/image-spec/specs-go/v1"
9+
log "github.com/sirupsen/logrus"
10+
"oras.land/oras-go/v2"
11+
"oras.land/oras-go/v2/content"
12+
"oras.land/oras-go/v2/content/file"
13+
"oras.land/oras-go/v2/registry"
14+
"oras.land/oras-go/v2/registry/remote"
15+
"os"
16+
"path/filepath"
17+
)
18+
19+
func GetOCIArtifactURLAndPathByLanguage(language, version string) (string, string, error) {
20+
userHomeDir, err := os.UserHomeDir()
21+
if err != nil {
22+
return "", "", err
23+
}
24+
artifactPath := userHomeDir + "/.compage/templates/compage-template-" + language + "/" + version
25+
artifactURL := "ghcr.io/intelops/compage-template-" + language + ":" + version
26+
if language == "common" {
27+
artifactPath = userHomeDir + "/.compage/templates/common-templates/" + version
28+
return "ghcr.io/intelops/common-templates:" + version, artifactPath, nil
29+
}
30+
return artifactURL, artifactPath, nil
31+
}
32+
33+
// Pull pulls an OCI image from a registry.
34+
func Pull(requestCtx context.Context, disableTLS bool) (template *string, err error) {
35+
ociName := requestCtx.Value("artifactURL").(string)
36+
ref, err := registry.ParseReference(ociName)
37+
if err != nil {
38+
log.Errorf("parse reference: %v", err)
39+
return nil, fmt.Errorf("parse reference: %w", err)
40+
}
41+
42+
if ref.Reference == ociLatestTag || ref.Reference == "" {
43+
ref.Reference, err = getLatestTagSortedBySemver(ref.Registry+"/"+ref.Repository, disableTLS)
44+
if err != nil {
45+
return nil, fmt.Errorf("get latest tag sorted by semver: %w", err)
46+
}
47+
}
48+
49+
// Connect to a remote repository
50+
ctx := context.Background()
51+
52+
repository, err := remote.NewRepository(ociName)
53+
if err != nil {
54+
log.Errorf("new repository: %v", err)
55+
return nil, fmt.Errorf("new repository: %w", err)
56+
}
57+
58+
if disableTLS {
59+
log.Debugln("TLS connection is disabled")
60+
repository.PlainHTTP = true
61+
}
62+
63+
// Get credentials from the docker credential store
64+
if err = getCredentialsFromDockerStore(repository); err != nil {
65+
log.Errorf("credstore from docker: %v", err)
66+
return nil, fmt.Errorf("credstore from docker: %w", err)
67+
}
68+
69+
// Get remote manifest digest
70+
remoteManifestSpec, remoteManifestReader, err := oras.Fetch(ctx, repository, ref.String(), oras.DefaultFetchOptions)
71+
if err != nil {
72+
log.Errorf("fetch: %v", err)
73+
return nil, fmt.Errorf("fetch: %w", err)
74+
}
75+
76+
remoteManifestData, err := content.ReadAll(remoteManifestReader, remoteManifestSpec)
77+
if err != nil {
78+
log.Errorf("fetch remote content: %v", err)
79+
return nil, fmt.Errorf("fetch remote content: %w", err)
80+
}
81+
82+
// Create the template root directory
83+
templateRootDir := requestCtx.Value("artifactPath").(string)
84+
85+
fs, err := file.New(templateRootDir)
86+
if err != nil {
87+
log.Errorf("create file store: %v", err)
88+
return nil, fmt.Errorf("create file store: %w", err)
89+
}
90+
defer func(fs *file.Store) {
91+
_ = fs.Close()
92+
}(fs)
93+
94+
// Copy from the remote repository to the file store
95+
// Fetch the remote manifest
96+
remoteTemplate, err := getCompageTemplateFromManifestLayers(remoteManifestData, templateRootDir)
97+
98+
template = remoteTemplate
99+
100+
manifestDescriptor, err := oras.Copy(ctx, repository, ref.Reference, fs, ref.Reference, oras.DefaultCopyOptions)
101+
if err != nil {
102+
log.Errorf("copy: %v", err)
103+
return nil, fmt.Errorf("copy: %w", err)
104+
}
105+
106+
manifestData, err := content.FetchAll(ctx, fs, manifestDescriptor)
107+
if err != nil {
108+
log.Errorf("fetch manifest: %v", err)
109+
return nil, fmt.Errorf("fetch manifest: %w", err)
110+
}
111+
112+
if doTemplateFilesExistLocally(templateRootDir, remoteTemplate) {
113+
log.Debugf("Template %q already available in: %s\n", ociName, templateRootDir)
114+
} else {
115+
log.Infof("Pulling compage template %q\n", ociName)
116+
template, err = getCompageTemplateFromManifestLayers(manifestData, templateRootDir)
117+
if err != nil {
118+
return nil, fmt.Errorf("get media types from layers: %w", err)
119+
}
120+
}
121+
log.Debugf("template successfully pulled in %s", templateRootDir)
122+
123+
return template, nil
124+
}
125+
126+
// getCompageTemplateFromManifestLayers returns the list of templates from an OCI manifest
127+
func getCompageTemplateFromManifestLayers(manifestData []byte, templateRootDir string) (
128+
*string, error) {
129+
specification := spec.Manifest{}
130+
err := json.Unmarshal(manifestData, &specification)
131+
if err != nil {
132+
log.Errorf("unmarshal manifest: %v", err)
133+
return nil, fmt.Errorf("unmarshal manifest: %w", err)
134+
}
135+
136+
for _, layer := range specification.Layers {
137+
switch layer.MediaType {
138+
case commonTemplatesMediaType:
139+
fallthrough
140+
case compageTemplateGoMediaType:
141+
if title, ok := layer.Annotations["org.opencontainers.image.title"]; ok && title != "" {
142+
template := filepath.Join(templateRootDir, title)
143+
return &template, nil
144+
}
145+
default:
146+
log.Warningf("unknown media type: %q\n", layer.MediaType)
147+
}
148+
}
149+
150+
return nil, nil
151+
}
152+
153+
/*
154+
doTemplateFilesExistLocally returns true if the template folder/directory is already available locally.
155+
One more check is to see if the template has been tampered locally.
156+
*/
157+
func doTemplateFilesExistLocally(templateRootDir string, template *string) bool {
158+
var errs []error
159+
templateRootDirFileInfo, err := os.Stat(templateRootDir)
160+
// If store path exist and is a directory then we do nothing
161+
if err == nil {
162+
if !templateRootDirFileInfo.IsDir() {
163+
errs = append(errs, fmt.Errorf("template root dir %s already exist and is not a directory", templateRootDir))
164+
}
165+
} else {
166+
if !errors.Is(err, os.ErrNotExist) {
167+
errs = append(errs, fmt.Errorf("getting information about %s: %w", templateRootDir, err))
168+
}
169+
}
170+
171+
isFileExist := func(file *string) {
172+
_, err = os.Stat(*file)
173+
if err == nil {
174+
return
175+
}
176+
if errors.Is(err, os.ErrNotExist) {
177+
errs = append(errs, fmt.Errorf("%s does not exist locally", *file))
178+
} else {
179+
errs = append(errs, fmt.Errorf("something went wrong while checking if %s exist: %w", *file, err))
180+
}
181+
}
182+
183+
isFileExist(template)
184+
185+
if len(errs) > 0 {
186+
for i := range errs {
187+
log.Debugln(errs[i])
188+
}
189+
return false
190+
}
191+
192+
// check if the template has been tampered locally
193+
//todo: implement this
194+
return true
195+
}

0 commit comments

Comments
 (0)