diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 8af03a9c..e95c79ef 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -31,6 +31,7 @@ import ( "github.com/nephio-project/porch/pkg/engine" "github.com/nephio-project/porch/pkg/meta" "github.com/nephio-project/porch/pkg/registry/porch" + repoimpltypes "github.com/nephio-project/porch/pkg/repoimpl/types" "google.golang.org/api/option" "google.golang.org/api/sts/v1" corev1 "k8s.io/api/core/v1" @@ -75,13 +76,13 @@ func init() { // ExtraConfig holds custom apiserver config type ExtraConfig struct { - CoreAPIKubeconfigPath string - CacheDirectory string - FunctionRunnerAddress string - DefaultImagePrefix string - RepoSyncFrequency time.Duration - UseGitCaBundle bool - MaxGrpcMessageSize int + CoreAPIKubeconfigPath string + CacheDirectory string + FunctionRunnerAddress string + DefaultImagePrefix string + RepoSyncFrequency time.Duration + UseUserDefinedCaBundle bool + MaxGrpcMessageSize int } // Config defines the config for the apiserver @@ -228,11 +229,14 @@ func (c completedConfig) New() (*PorchServer, error) { watcherMgr := engine.NewWatcherManager() - memoryCache := memorycache.NewCache(c.ExtraConfig.CacheDirectory, c.ExtraConfig.RepoSyncFrequency, c.ExtraConfig.UseGitCaBundle, memorycache.CacheOptions{ - CredentialResolver: credentialResolver, - UserInfoProvider: userInfoProvider, - MetadataStore: metadataStore, - ObjectNotifier: watcherMgr, + memoryCache := memorycache.NewCache(repoimpltypes.RepoImplOptions{ + LocalDirectory: c.ExtraConfig.CacheDirectory, + RepoSyncFrequency: c.ExtraConfig.RepoSyncFrequency, + UseUserDefinedCaBundle: c.ExtraConfig.UseUserDefinedCaBundle, + CredentialResolver: credentialResolver, + UserInfoProvider: userInfoProvider, + MetadataStore: metadataStore, + ObjectNotifier: watcherMgr, }) runnerOptionsResolver := func(namespace string) fnruntime.RunnerOptions { diff --git a/pkg/cache/memory/cache.go b/pkg/cache/memory/cache.go index 86ab88cc..8c1aa336 100644 --- a/pkg/cache/memory/cache.go +++ b/pkg/cache/memory/cache.go @@ -16,21 +16,14 @@ package memory import ( "context" - "errors" - "fmt" - "path/filepath" "sync" - "time" - kptoci "github.com/GoogleContainerTools/kpt/pkg/oci" configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" "github.com/nephio-project/porch/pkg/cache" - "github.com/nephio-project/porch/pkg/git" - "github.com/nephio-project/porch/pkg/meta" - "github.com/nephio-project/porch/pkg/oci" + "github.com/nephio-project/porch/pkg/repoimpl" + repoimpltypes "github.com/nephio-project/porch/pkg/repoimpl/types" "github.com/nephio-project/porch/pkg/repository" "go.opentelemetry.io/otel/trace" - "k8s.io/apimachinery/pkg/watch" ) // Cache allows us to keep state for repositories, rather than querying them every time. @@ -44,64 +37,17 @@ import ( // * We Cache flattened tar files in /oci/ (so we don't need to pull to read resources) // * We poll the repositories (every minute) and Cache the discovered images in memory. type Cache struct { - mutex sync.Mutex - repositories map[string]*cachedRepository - cacheDir string - credentialResolver repository.CredentialResolver - userInfoProvider repository.UserInfoProvider - metadataStore meta.MetadataStore - repoSyncFrequency time.Duration - objectNotifier objectNotifier - useGitCaBundle bool + mutex sync.Mutex + repositories map[string]*cachedRepository + options repoimpltypes.RepoImplOptions } var _ cache.Cache = &Cache{} -type objectNotifier interface { - NotifyPackageRevisionChange(eventType watch.EventType, obj repository.PackageRevision) int -} - -type CacheOptions struct { - CredentialResolver repository.CredentialResolver - UserInfoProvider repository.UserInfoProvider - MetadataStore meta.MetadataStore - ObjectNotifier objectNotifier -} - -func NewCache(cacheDir string, repoSyncFrequency time.Duration, useGitCaBundle bool, opts CacheOptions) *Cache { +func NewCache(options repoimpltypes.RepoImplOptions) *Cache { return &Cache{ - repositories: make(map[string]*cachedRepository), - cacheDir: cacheDir, - credentialResolver: opts.CredentialResolver, - userInfoProvider: opts.UserInfoProvider, - metadataStore: opts.MetadataStore, - objectNotifier: opts.ObjectNotifier, - repoSyncFrequency: repoSyncFrequency, - useGitCaBundle: useGitCaBundle, - } -} - -func getCacheKey(repositorySpec *configapi.Repository) (string, error) { - switch repositoryType := repositorySpec.Spec.Type; repositoryType { - case configapi.RepositoryTypeOCI: - ociSpec := repositorySpec.Spec.Oci - if ociSpec == nil { - return "", fmt.Errorf("oci not configured") - } - return "oci://" + ociSpec.Registry, nil - - case configapi.RepositoryTypeGit: - gitSpec := repositorySpec.Spec.Git - if gitSpec == nil { - return "", errors.New("git property is required") - } - if gitSpec.Repo == "" { - return "", errors.New("git.repo property is required") - } - return fmt.Sprintf("git://%s/%s@%s/%s", gitSpec.Repo, gitSpec.Directory, repositorySpec.Namespace, repositorySpec.Name), nil - - default: - return "", fmt.Errorf("repository type %q not supported", repositoryType) + repositories: make(map[string]*cachedRepository), + options: options, } } @@ -109,73 +55,39 @@ func (c *Cache) OpenRepository(ctx context.Context, repositorySpec *configapi.Re ctx, span := tracer.Start(ctx, "Cache::OpenRepository", trace.WithAttributes()) defer span.End() - key, err := getCacheKey(repositorySpec) + key, err := repoimpl.RepositoryKey(repositorySpec) if err != nil { return nil, err } + c.mutex.Lock() defer c.mutex.Unlock() - cachedRepo := c.repositories[key] - - switch repositoryType := repositorySpec.Spec.Type; repositoryType { - case configapi.RepositoryTypeOCI: - ociSpec := repositorySpec.Spec.Oci - if cachedRepo == nil { - cacheDir := filepath.Join(c.cacheDir, "oci") - storage, err := kptoci.NewStorage(cacheDir) - if err != nil { - return nil, err - } - - r, err := oci.OpenRepository(repositorySpec.Name, repositorySpec.Namespace, ociSpec, repositorySpec.Spec.Deployment, storage) - if err != nil { - return nil, err - } - cachedRepo = newRepository(key, repositorySpec, r, c.objectNotifier, c.metadataStore, c.repoSyncFrequency) - c.repositories[key] = cachedRepo - } - return cachedRepo, nil - - case configapi.RepositoryTypeGit: - gitSpec := repositorySpec.Spec.Git - if cachedRepo == nil { - var mbs git.MainBranchStrategy - if gitSpec.CreateBranch { - mbs = git.CreateIfMissing - } else { - mbs = git.ErrorIfMissing - } - - r, err := git.OpenRepository(ctx, repositorySpec.Name, repositorySpec.Namespace, gitSpec, repositorySpec.Spec.Deployment, filepath.Join(c.cacheDir, "git"), git.GitRepositoryOptions{ - CredentialResolver: c.credentialResolver, - UserInfoProvider: c.userInfoProvider, - MainBranchStrategy: mbs, - UseGitCaBundle: c.useGitCaBundle, - }) - if err != nil { - return nil, err - } - - cachedRepo = newRepository(key, repositorySpec, r, c.objectNotifier, c.metadataStore, c.repoSyncFrequency) - c.repositories[key] = cachedRepo + + if cachedRepo := c.repositories[key]; cachedRepo != nil { + // If there is an error from the background refresh goroutine, return it. + if err := cachedRepo.getRefreshError(); err == nil { + return cachedRepo, nil } else { - // If there is an error from the background refresh goroutine, return it. - if err := cachedRepo.getRefreshError(); err != nil { - return nil, err - } + return nil, err } - return cachedRepo, nil + } - default: - return nil, fmt.Errorf("type %q not supported", repositoryType) + repoImpl, err := repoimpl.RepositoryFactory(ctx, repositorySpec, c.options) + if err != nil { + return nil, err } + + cachedRepo := newRepository(key, repositorySpec, repoImpl, c.options) + c.repositories[key] = cachedRepo + + return cachedRepo, nil } func (c *Cache) CloseRepository(ctx context.Context, repositorySpec *configapi.Repository, allRepos []configapi.Repository) error { _, span := tracer.Start(ctx, "Cache::CloseRepository", trace.WithAttributes()) defer span.End() - key, err := getCacheKey(repositorySpec) + key, err := repoimpl.RepositoryKey(repositorySpec) if err != nil { return err } @@ -185,7 +97,7 @@ func (c *Cache) CloseRepository(ctx context.Context, repositorySpec *configapi.R if r.Name == repositorySpec.Name && r.Namespace == repositorySpec.Namespace { continue } - otherKey, err := getCacheKey(&r) + otherKey, err := repoimpl.RepositoryKey(&r) if err != nil { return err } diff --git a/pkg/cache/memory/cache_test.go b/pkg/cache/memory/cache_test.go index c2523d8d..4df2dd64 100644 --- a/pkg/cache/memory/cache_test.go +++ b/pkg/cache/memory/cache_test.go @@ -27,9 +27,10 @@ import ( "github.com/nephio-project/porch/api/porchconfig/v1alpha1" fakecache "github.com/nephio-project/porch/pkg/cache/fake" - "github.com/nephio-project/porch/pkg/git" "github.com/nephio-project/porch/pkg/meta" fakemeta "github.com/nephio-project/porch/pkg/meta/fake" + "github.com/nephio-project/porch/pkg/repoimpl/git" + repoimpltypes "github.com/nephio-project/porch/pkg/repoimpl/types" "github.com/nephio-project/porch/pkg/repository" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" @@ -37,7 +38,7 @@ import ( func TestLatestPackages(t *testing.T) { ctx := context.Background() - testPath := filepath.Join("..", "..", "git", "testdata") + testPath := filepath.Join("..", "..", "repoimpl", "git", "testdata") cachedRepo := openRepositoryFromArchive(t, ctx, testPath, "nested") @@ -83,7 +84,7 @@ func TestLatestPackages(t *testing.T) { func TestPublishedLatest(t *testing.T) { ctx := context.Background() - testPath := filepath.Join("..", "..", "git", "testdata") + testPath := filepath.Join("..", "..", "repoimpl", "git", "testdata") cachedRepo := openRepositoryFromArchive(t, ctx, testPath, "nested") revisions, err := cachedRepo.ListPackageRevisions(ctx, repository.ListPackageRevisionFilter{ @@ -129,7 +130,7 @@ func TestPublishedLatest(t *testing.T) { func TestDeletePublishedMain(t *testing.T) { ctx := context.Background() - testPath := filepath.Join("../..", "git", "testdata") + testPath := filepath.Join("../..", "repoimpl", "git", "testdata") cachedRepo := openRepositoryFromArchive(t, ctx, testPath, "nested") revisions, err := cachedRepo.ListPackageRevisions(ctx, repository.ListPackageRevisionFilter{ @@ -223,10 +224,13 @@ func openRepositoryFromArchive(t *testing.T, ctx context.Context, testPath, name _, address := git.ServeGitRepository(t, tarfile, tempdir) metadataStore := createMetadataStoreFromArchive(t, fmt.Sprintf("%s-metadata.yaml", name), name) - cache := NewCache(t.TempDir(), 60*time.Second, true, CacheOptions{ - MetadataStore: metadataStore, - ObjectNotifier: &fakecache.ObjectNotifier{}, - CredentialResolver: &fakecache.CredentialResolver{}, + cache := NewCache(repoimpltypes.RepoImplOptions{ + LocalDirectory: t.TempDir(), + RepoSyncFrequency: 60 * time.Second, + UseUserDefinedCaBundle: true, + MetadataStore: metadataStore, + ObjectNotifier: &fakecache.ObjectNotifier{}, + CredentialResolver: &fakecache.CredentialResolver{}, }) apiRepo := &v1alpha1.Repository{ TypeMeta: metav1.TypeMeta{ diff --git a/pkg/cache/memory/repository.go b/pkg/cache/memory/repository.go index add34983..d9ff91ae 100644 --- a/pkg/cache/memory/repository.go +++ b/pkg/cache/memory/repository.go @@ -22,8 +22,7 @@ import ( "github.com/nephio-project/porch/api/porch/v1alpha1" configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" - "github.com/nephio-project/porch/pkg/git" - "github.com/nephio-project/porch/pkg/meta" + repoimpltypes "github.com/nephio-project/porch/pkg/repoimpl/types" "github.com/nephio-project/porch/pkg/repository" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" @@ -61,25 +60,22 @@ type cachedRepository struct { // This is returned back by the cache to the background goroutine when it calls periodicall to resync repositories. refreshRevisionsError error - objectNotifier objectNotifier - - metadataStore meta.MetadataStore + options repoimpltypes.RepoImplOptions } -func newRepository(id string, repoSpec *configapi.Repository, repo repository.Repository, objectNotifier objectNotifier, metadataStore meta.MetadataStore, repoSyncFrequency time.Duration) *cachedRepository { +func newRepository(id string, repoSpec *configapi.Repository, repo repository.Repository, options repoimpltypes.RepoImplOptions) *cachedRepository { ctx, cancel := context.WithCancel(context.Background()) r := &cachedRepository{ - id: id, - repoSpec: repoSpec, - repo: repo, - cancel: cancel, - objectNotifier: objectNotifier, - metadataStore: metadataStore, + id: id, + repoSpec: repoSpec, + repo: repo, + cancel: cancel, + options: options, } // TODO: Should we fetch the packages here? - go r.pollForever(ctx, repoSyncFrequency) + go r.pollForever(ctx, options.RepoSyncFrequency) return r } @@ -150,12 +146,8 @@ func (r *cachedRepository) getCachedPackages(ctx context.Context, forceRefresh b packages = nil packageRevisions = nil - if gitRepo, isGitRepo := r.repo.(git.GitRepository); isGitRepo { - // TODO: Figure out a way to do this without the cache layer - // needing to know what type of repo we are working with. - if err := gitRepo.UpdateDeletionProposedCache(); err != nil { - return nil, nil, err - } + if err := r.repo.Refresh(ctx); err != nil { + return nil, nil, err } } r.mutex.Unlock() @@ -289,13 +281,13 @@ func (r *cachedRepository) createMainPackageRevision(ctx context.Context, update } // Create the package if it doesn't exist - _, err := r.metadataStore.Get(ctx, pkgRevMetaNN) + _, err := r.options.MetadataStore.Get(ctx, pkgRevMetaNN) if errors.IsNotFound(err) { pkgRevMeta := metav1.ObjectMeta{ Name: updatedMain.KubeObjectName(), Namespace: updatedMain.KubeObjectNamespace(), } - _, err := r.metadataStore.Create(ctx, pkgRevMeta, r.repoSpec.Name, updatedMain.UID()) + _, err := r.options.MetadataStore.Create(ctx, pkgRevMeta, r.repoSpec.Name, updatedMain.UID()) if err != nil { klog.Warningf("unable to create PackageRev CR for %s/%s: %v", updatedMain.KubeObjectNamespace(), updatedMain.KubeObjectName(), err) @@ -375,7 +367,7 @@ func (r *cachedRepository) Close() error { // the repository, so we have to just delete the PackageRevision regardless of any // finalizers. klog.Infof("repo %s: deleting packagerev %s/%s because repository is closed", r.id, nn.Namespace, nn.Name) - _, err := r.metadataStore.Delete(context.TODO(), nn, true) + _, err := r.options.MetadataStore.Delete(context.TODO(), nn, true) if err != nil { // There isn't much use in returning an error here, so we just log it // and create a PackageRevisionMeta with just name and namespace. This @@ -383,7 +375,7 @@ func (r *cachedRepository) Close() error { klog.Warningf("repo %s: error deleting packagerev for %s: %v", r.id, nn.Name, err) } klog.Infof("repo %s: successfully deleted packagerev %s/%s", r.id, nn.Namespace, nn.Name) - sent += r.objectNotifier.NotifyPackageRevisionChange(watch.Deleted, pr) + sent += r.options.ObjectNotifier.NotifyPackageRevisionChange(watch.Deleted, pr) } klog.Infof("repo %s: sent %d notifications for %d package revisions during close", r.id, sent, len(r.cachedPackageRevisions)) return r.repo.Close() @@ -452,7 +444,7 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re // Look up all existing PackageRevCRs so we an compare those to the // actual Packagerevisions found in git/oci, and add/prune PackageRevCRs // as necessary. - existingPkgRevCRs, err := r.metadataStore.List(ctx, r.repoSpec) + existingPkgRevCRs, err := r.options.MetadataStore.List(ctx, r.repoSpec) if err != nil { return nil, nil, err } @@ -497,7 +489,7 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re if _, found := newPackageRevisionNames[prm.Name]; !found { klog.Infof("repo %s: deleting PackageRev %s/%s because parent PackageRevision was not found", r.id, prm.Namespace, prm.Name) - if _, err := r.metadataStore.Delete(ctx, types.NamespacedName{ + if _, err := r.options.MetadataStore.Delete(ctx, types.NamespacedName{ Name: prm.Name, Namespace: prm.Namespace, }, true); err != nil { @@ -516,10 +508,10 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re for kname, newPackage := range newPackageRevisionNames { oldPackage := oldPackageRevisionNames[kname] if oldPackage == nil { - addSent += r.objectNotifier.NotifyPackageRevisionChange(watch.Added, newPackage) + addSent += r.options.ObjectNotifier.NotifyPackageRevisionChange(watch.Added, newPackage) } else { if oldPackage.ResourceVersion() != newPackage.ResourceVersion() { - modSent += r.objectNotifier.NotifyPackageRevisionChange(watch.Modified, newPackage) + modSent += r.options.ObjectNotifier.NotifyPackageRevisionChange(watch.Modified, newPackage) } } } @@ -532,7 +524,7 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re Name: pkgRevName, Namespace: r.repoSpec.Namespace, } - if _, err := r.metadataStore.Create(ctx, pkgRevMeta, r.repoSpec.Name, pkgRev.UID()); err != nil { + if _, err := r.options.MetadataStore.Create(ctx, pkgRevMeta, r.repoSpec.Name, pkgRev.UID()); err != nil { // TODO: We should try to find a way to make these errors available through // either the repository CR or the PackageRevision CR. This will be // retried on the next sync. @@ -552,7 +544,7 @@ func (r *cachedRepository) refreshAllCachedPackages(ctx context.Context) (map[re } klog.Infof("repo %s: deleting PackageRev %s/%s because PackageRevision was removed from SoT", r.id, nn.Namespace, nn.Name) - delSent += r.objectNotifier.NotifyPackageRevisionChange(watch.Deleted, oldPackage) + delSent += r.options.ObjectNotifier.NotifyPackageRevisionChange(watch.Deleted, oldPackage) } } klog.Infof("repo %s: addSent %d, modSent %d, delSent for %d old and %d new repo packages", r.id, addSent, modSent, len(oldPackageRevisionNames), len(newPackageRevisionNames)) diff --git a/pkg/cmd/server/start.go b/pkg/cmd/server/start.go index 27ec82b8..e02f77dc 100644 --- a/pkg/cmd/server/start.go +++ b/pkg/cmd/server/start.go @@ -57,7 +57,7 @@ type PorchServerOptions struct { FunctionRunnerAddress string DefaultImagePrefix string RepoSyncFrequency time.Duration - UseGitCaBundle bool + UseUserDefinedCaBundle bool DisableValidatingAdmissionPolicy bool MaxRequestBodySize int @@ -195,13 +195,13 @@ func (o *PorchServerOptions) Config() (*apiserver.Config, error) { config := &apiserver.Config{ GenericConfig: serverConfig, ExtraConfig: apiserver.ExtraConfig{ - CoreAPIKubeconfigPath: o.CoreAPIKubeconfigPath, - CacheDirectory: o.CacheDirectory, - RepoSyncFrequency: o.RepoSyncFrequency, - FunctionRunnerAddress: o.FunctionRunnerAddress, - DefaultImagePrefix: o.DefaultImagePrefix, - UseGitCaBundle: o.UseGitCaBundle, - MaxGrpcMessageSize: o.MaxRequestBodySize, + CoreAPIKubeconfigPath: o.CoreAPIKubeconfigPath, + CacheDirectory: o.CacheDirectory, + RepoSyncFrequency: o.RepoSyncFrequency, + FunctionRunnerAddress: o.FunctionRunnerAddress, + DefaultImagePrefix: o.DefaultImagePrefix, + UseUserDefinedCaBundle: o.UseUserDefinedCaBundle, + MaxGrpcMessageSize: o.MaxRequestBodySize, }, } return config, nil @@ -248,7 +248,7 @@ func (o *PorchServerOptions) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&o.DefaultImagePrefix, "default-image-prefix", fnruntime.GCRImagePrefix, "Default prefix for unqualified function names") fs.StringVar(&o.CacheDirectory, "cache-directory", "", "Directory where Porch server stores repository and package caches.") fs.IntVar(&o.MaxRequestBodySize, "max-request-body-size", 6*1024*1024, "Maximum size of the request body in bytes. Keep this in sync with function-runner's corresponding argument.") - fs.BoolVar(&o.UseGitCaBundle, "use-git-cabundle", false, "Determine whether to use a user-defined CaBundle for TLS towards git.") + fs.BoolVar(&o.UseUserDefinedCaBundle, "use-user-cabundle", false, "Determine whether to use a user-defined CaBundle for TLS towards the repository system.") fs.BoolVar(&o.DisableValidatingAdmissionPolicy, "disable-validating-admissions-policy", true, "Determine whether to (dis|en)able the Validating Admission Policy, which requires k8s version >= v1.30") fs.DurationVar(&o.RepoSyncFrequency, "repo-sync-frequency", 10*time.Minute, "Frequency in seconds at which registered repositories will be synced and the background job repository refresh runs.") } diff --git a/pkg/registry/porch/watch_test.go b/pkg/registry/porch/watch_test.go index fa21fa95..349872c5 100644 --- a/pkg/registry/porch/watch_test.go +++ b/pkg/registry/porch/watch_test.go @@ -22,8 +22,8 @@ import ( "github.com/nephio-project/porch/api/porch/v1alpha1" "github.com/nephio-project/porch/pkg/engine" + "github.com/nephio-project/porch/pkg/repoimpl/fake" "github.com/nephio-project/porch/pkg/repository" - "github.com/nephio-project/porch/pkg/repository/fake" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" diff --git a/pkg/repository/fake/packagerevision.go b/pkg/repoimpl/fake/packagerevision.go similarity index 100% rename from pkg/repository/fake/packagerevision.go rename to pkg/repoimpl/fake/packagerevision.go diff --git a/pkg/repository/fake/repository.go b/pkg/repoimpl/fake/repository.go similarity index 98% rename from pkg/repository/fake/repository.go rename to pkg/repoimpl/fake/repository.go index 864d5e89..37865564 100644 --- a/pkg/repository/fake/repository.go +++ b/pkg/repoimpl/fake/repository.go @@ -88,3 +88,7 @@ func (r *Repository) DeletePackage(_ context.Context, pr repository.Package) err func (r *Repository) Refresh(_ context.Context) error { return nil } + +func (r *Repository) Key() string { + return "" +} diff --git a/pkg/git/annotation.go b/pkg/repoimpl/git/annotation.go similarity index 100% rename from pkg/git/annotation.go rename to pkg/repoimpl/git/annotation.go diff --git a/pkg/git/commit.go b/pkg/repoimpl/git/commit.go similarity index 100% rename from pkg/git/commit.go rename to pkg/repoimpl/git/commit.go diff --git a/pkg/git/commit_test.go b/pkg/repoimpl/git/commit_test.go similarity index 100% rename from pkg/git/commit_test.go rename to pkg/repoimpl/git/commit_test.go diff --git a/pkg/git/dir.go b/pkg/repoimpl/git/dir.go similarity index 100% rename from pkg/git/dir.go rename to pkg/repoimpl/git/dir.go diff --git a/pkg/git/dir_test.go b/pkg/repoimpl/git/dir_test.go similarity index 100% rename from pkg/git/dir_test.go rename to pkg/repoimpl/git/dir_test.go diff --git a/pkg/git/doc.go b/pkg/repoimpl/git/doc.go similarity index 100% rename from pkg/git/doc.go rename to pkg/repoimpl/git/doc.go diff --git a/pkg/git/draft.go b/pkg/repoimpl/git/draft.go similarity index 100% rename from pkg/git/draft.go rename to pkg/repoimpl/git/draft.go diff --git a/pkg/git/git.go b/pkg/repoimpl/git/git.go similarity index 97% rename from pkg/git/git.go rename to pkg/repoimpl/git/git.go index 8929ac52..8a62fd86 100644 --- a/pkg/git/git.go +++ b/pkg/repoimpl/git/git.go @@ -39,6 +39,7 @@ import ( "github.com/nephio-project/porch/api/porch/v1alpha1" configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" kptfilev1 "github.com/nephio-project/porch/pkg/kpt/api/kptfile/v1" + repoimpltypes "github.com/nephio-project/porch/pkg/repoimpl/types" "github.com/nephio-project/porch/pkg/repository" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" @@ -68,10 +69,34 @@ const ( ) type GitRepositoryOptions struct { - CredentialResolver repository.CredentialResolver - UserInfoProvider repository.UserInfoProvider + repoimpltypes.RepoImplOptions MainBranchStrategy MainBranchStrategy - UseGitCaBundle bool +} + +func GetRepositoryImpl(ctx context.Context, repositorySpec *configapi.Repository, options repoimpltypes.RepoImplOptions) (repository.Repository, error) { + if repositorySpec.Spec.Git == nil { + return nil, errors.New("git property is required") + } + if repositorySpec.Spec.Git.Repo == "" { + return nil, errors.New("git.repo property is required") + } + + var mbs MainBranchStrategy + if repositorySpec.Spec.Git.CreateBranch { + mbs = CreateIfMissing + } else { + mbs = ErrorIfMissing + } + + repo, err := OpenRepository(ctx, repositorySpec.Name, repositorySpec.Namespace, repositorySpec.Spec.Git, repositorySpec.Spec.Deployment, filepath.Join(options.LocalDirectory, "git"), GitRepositoryOptions{ + RepoImplOptions: options, + MainBranchStrategy: mbs, + }) + if err != nil { + return nil, err + } + + return repo, nil } func OpenRepository(ctx context.Context, name, namespace string, spec *configapi.GitRepository, deployment bool, root string, opts GitRepositoryOptions) (GitRepository, error) { @@ -136,14 +161,14 @@ func OpenRepository(ctx context.Context, name, namespace string, spec *configapi branch: branch, directory: strings.Trim(spec.Directory, "/"), secret: spec.SecretRef.Name, - credentialResolver: opts.CredentialResolver, - userInfoProvider: opts.UserInfoProvider, + credentialResolver: opts.RepoImplOptions.CredentialResolver, + userInfoProvider: opts.RepoImplOptions.UserInfoProvider, cacheDir: dir, deployment: deployment, } - if opts.UseGitCaBundle { - if caBundle, err := opts.CredentialResolver.ResolveCredential(ctx, namespace, namespace+"-ca-bundle"); err != nil { + if opts.RepoImplOptions.UseUserDefinedCaBundle { + if caBundle, err := opts.RepoImplOptions.CredentialResolver.ResolveCredential(ctx, namespace, namespace+"-ca-bundle"); err != nil { klog.Errorf("failed to obtain caBundle from secret %s/%s: %v", namespace, namespace+"-ca-bundle", err) } else { repository.caBundle = []byte(caBundle.ToString()) @@ -1229,7 +1254,7 @@ func (r *gitRepository) GetResources(hash plumbing.Hash) (map[string]string, err // findLatestPackageCommit returns the latest commit from the history that pertains // to the package given by the packagePath. If no commit is found, it will return nil. -func (r *gitRepository) findLatestPackageCommit(ctx context.Context, startCommit *object.Commit, packagePath string) (*object.Commit, error) { +func (r *gitRepository) findLatestPackageCommit(_ context.Context, startCommit *object.Commit, packagePath string) (*object.Commit, error) { var commit *object.Commit err := r.packageHistoryIterator(startCommit, packagePath, func(c *object.Commit) error { commit = c @@ -1338,7 +1363,7 @@ func (r *gitRepository) GetLifecycle(ctx context.Context, pkgRev *gitPackageRevi return r.getLifecycle(ctx, pkgRev) } -func (r *gitRepository) getLifecycle(ctx context.Context, pkgRev *gitPackageRevision) v1alpha1.PackageRevisionLifecycle { +func (r *gitRepository) getLifecycle(_ context.Context, pkgRev *gitPackageRevision) v1alpha1.PackageRevisionLifecycle { switch ref := pkgRev.ref; { case ref == nil: return r.checkPublishedLifecycle(pkgRev) @@ -1698,7 +1723,11 @@ func (r *gitRepository) discoverPackagesInTree(commit *object.Commit, opt Discov } func (r *gitRepository) Refresh(_ context.Context) error { - return nil + return r.UpdateDeletionProposedCache() +} + +func (r *gitRepository) Key() string { + return fmt.Sprintf("git://%s/%s@%s/%s", r.repo, r.directory, r.namespace, r.name) } // See https://eli.thegreenplace.net/2021/generic-functions-on-slices-with-go-type-parameters/ diff --git a/pkg/git/git_test.go b/pkg/repoimpl/git/git_test.go similarity index 100% rename from pkg/git/git_test.go rename to pkg/repoimpl/git/git_test.go diff --git a/pkg/git/gogit.go b/pkg/repoimpl/git/gogit.go similarity index 100% rename from pkg/git/gogit.go rename to pkg/repoimpl/git/gogit.go diff --git a/pkg/git/mainbranchstrategy_string.go b/pkg/repoimpl/git/mainbranchstrategy_string.go similarity index 100% rename from pkg/git/mainbranchstrategy_string.go rename to pkg/repoimpl/git/mainbranchstrategy_string.go diff --git a/pkg/git/package.go b/pkg/repoimpl/git/package.go similarity index 100% rename from pkg/git/package.go rename to pkg/repoimpl/git/package.go diff --git a/pkg/git/package_test.go b/pkg/repoimpl/git/package_test.go similarity index 100% rename from pkg/git/package_test.go rename to pkg/repoimpl/git/package_test.go diff --git a/pkg/git/package_tree.go b/pkg/repoimpl/git/package_tree.go similarity index 100% rename from pkg/git/package_tree.go rename to pkg/repoimpl/git/package_tree.go diff --git a/pkg/git/primitives_test.go b/pkg/repoimpl/git/primitives_test.go similarity index 99% rename from pkg/git/primitives_test.go rename to pkg/repoimpl/git/primitives_test.go index c33e8355..d2140fe1 100644 --- a/pkg/git/primitives_test.go +++ b/pkg/repoimpl/git/primitives_test.go @@ -438,7 +438,7 @@ func initRepositoryWithRemote(t *testing.T, dir, address string) *git.Repository return repo } -func createTestCommit(t *testing.T, repo *git.Repository, parent plumbing.Hash, message, name, contents string) plumbing.Hash { +func createTestCommit(t *testing.T, repo *git.Repository, parent plumbing.Hash, _, name, contents string) plumbing.Hash { wt, err := repo.Worktree() if err != nil { t.Fatalf("Failed getting worktree: %v", err) diff --git a/pkg/git/push.go b/pkg/repoimpl/git/push.go similarity index 100% rename from pkg/git/push.go rename to pkg/repoimpl/git/push.go diff --git a/pkg/git/ref.go b/pkg/repoimpl/git/ref.go similarity index 100% rename from pkg/git/ref.go rename to pkg/repoimpl/git/ref.go diff --git a/pkg/git/ref_test.go b/pkg/repoimpl/git/ref_test.go similarity index 100% rename from pkg/git/ref_test.go rename to pkg/repoimpl/git/ref_test.go diff --git a/pkg/git/repo.go b/pkg/repoimpl/git/repo.go similarity index 100% rename from pkg/git/repo.go rename to pkg/repoimpl/git/repo.go diff --git a/pkg/git/repos.go b/pkg/repoimpl/git/repos.go similarity index 100% rename from pkg/git/repos.go rename to pkg/repoimpl/git/repos.go diff --git a/pkg/git/testdata/.gitignore b/pkg/repoimpl/git/testdata/.gitignore similarity index 100% rename from pkg/git/testdata/.gitignore rename to pkg/repoimpl/git/testdata/.gitignore diff --git a/pkg/git/testdata/Makefile b/pkg/repoimpl/git/testdata/Makefile similarity index 100% rename from pkg/git/testdata/Makefile rename to pkg/repoimpl/git/testdata/Makefile diff --git a/pkg/git/testdata/README.md b/pkg/repoimpl/git/testdata/README.md similarity index 100% rename from pkg/git/testdata/README.md rename to pkg/repoimpl/git/testdata/README.md diff --git a/pkg/git/testdata/drafts-repository.md b/pkg/repoimpl/git/testdata/drafts-repository.md similarity index 100% rename from pkg/git/testdata/drafts-repository.md rename to pkg/repoimpl/git/testdata/drafts-repository.md diff --git a/pkg/git/testdata/drafts-repository.tar b/pkg/repoimpl/git/testdata/drafts-repository.tar similarity index 100% rename from pkg/git/testdata/drafts-repository.tar rename to pkg/repoimpl/git/testdata/drafts-repository.tar diff --git a/pkg/git/testdata/empty-repository.md b/pkg/repoimpl/git/testdata/empty-repository.md similarity index 100% rename from pkg/git/testdata/empty-repository.md rename to pkg/repoimpl/git/testdata/empty-repository.md diff --git a/pkg/git/testdata/empty-repository.tar b/pkg/repoimpl/git/testdata/empty-repository.tar similarity index 100% rename from pkg/git/testdata/empty-repository.tar rename to pkg/repoimpl/git/testdata/empty-repository.tar diff --git a/pkg/git/testdata/nested-repository.md b/pkg/repoimpl/git/testdata/nested-repository.md similarity index 100% rename from pkg/git/testdata/nested-repository.md rename to pkg/repoimpl/git/testdata/nested-repository.md diff --git a/pkg/git/testdata/nested-repository.tar b/pkg/repoimpl/git/testdata/nested-repository.tar similarity index 100% rename from pkg/git/testdata/nested-repository.tar rename to pkg/repoimpl/git/testdata/nested-repository.tar diff --git a/pkg/git/testdata/publishinfo-repository.md b/pkg/repoimpl/git/testdata/publishinfo-repository.md similarity index 100% rename from pkg/git/testdata/publishinfo-repository.md rename to pkg/repoimpl/git/testdata/publishinfo-repository.md diff --git a/pkg/git/testdata/publishinfo-repository.tar b/pkg/repoimpl/git/testdata/publishinfo-repository.tar similarity index 100% rename from pkg/git/testdata/publishinfo-repository.tar rename to pkg/repoimpl/git/testdata/publishinfo-repository.tar diff --git a/pkg/git/testdata/simple-repository.md b/pkg/repoimpl/git/testdata/simple-repository.md similarity index 100% rename from pkg/git/testdata/simple-repository.md rename to pkg/repoimpl/git/testdata/simple-repository.md diff --git a/pkg/git/testdata/simple-repository.tar b/pkg/repoimpl/git/testdata/simple-repository.tar similarity index 100% rename from pkg/git/testdata/simple-repository.tar rename to pkg/repoimpl/git/testdata/simple-repository.tar diff --git a/pkg/git/testdata/trivial-repository.md b/pkg/repoimpl/git/testdata/trivial-repository.md similarity index 100% rename from pkg/git/testdata/trivial-repository.md rename to pkg/repoimpl/git/testdata/trivial-repository.md diff --git a/pkg/git/testdata/trivial-repository.tar b/pkg/repoimpl/git/testdata/trivial-repository.tar similarity index 100% rename from pkg/git/testdata/trivial-repository.tar rename to pkg/repoimpl/git/testdata/trivial-repository.tar diff --git a/pkg/git/testing.go b/pkg/repoimpl/git/testing.go similarity index 100% rename from pkg/git/testing.go rename to pkg/repoimpl/git/testing.go diff --git a/pkg/git/testing_repo.go b/pkg/repoimpl/git/testing_repo.go similarity index 100% rename from pkg/git/testing_repo.go rename to pkg/repoimpl/git/testing_repo.go diff --git a/pkg/oci/doc.go b/pkg/repoimpl/oci/doc.go similarity index 100% rename from pkg/oci/doc.go rename to pkg/repoimpl/oci/doc.go diff --git a/pkg/oci/loader.go b/pkg/repoimpl/oci/loader.go similarity index 100% rename from pkg/oci/loader.go rename to pkg/repoimpl/oci/loader.go diff --git a/pkg/oci/mutate.go b/pkg/repoimpl/oci/mutate.go similarity index 100% rename from pkg/oci/mutate.go rename to pkg/repoimpl/oci/mutate.go diff --git a/pkg/oci/oci.go b/pkg/repoimpl/oci/oci.go similarity index 94% rename from pkg/oci/oci.go rename to pkg/repoimpl/oci/oci.go index a9c6be4e..2df50ae5 100644 --- a/pkg/oci/oci.go +++ b/pkg/repoimpl/oci/oci.go @@ -21,28 +21,42 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "path/filepath" "strings" "time" "github.com/GoogleContainerTools/kpt/pkg/oci" + kptoci "github.com/GoogleContainerTools/kpt/pkg/oci" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/google" "github.com/nephio-project/porch/api/porch/v1alpha1" configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" "github.com/nephio-project/porch/internal/kpt/pkg" kptfile "github.com/nephio-project/porch/pkg/kpt/api/kptfile/v1" + repoimpltypes "github.com/nephio-project/porch/pkg/repoimpl/types" "github.com/nephio-project/porch/pkg/repository" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" ) -func OpenRepository(name string, namespace string, spec *configapi.OciRepository, deployment bool, storage *oci.Storage) (repository.Repository, error) { +func GetRepositoryImpl(ctx context.Context, repositorySpec *configapi.Repository, options repoimpltypes.RepoImplOptions) (repository.Repository, error) { + if repositorySpec.Spec.Oci == nil { + return nil, fmt.Errorf("oci not configured") + } + + ociSpec := repositorySpec.Spec.Oci + localDir := filepath.Join(options.LocalDirectory, "oci") + storage, err := kptoci.NewStorage(localDir) + if err != nil { + return nil, err + } + return &ociRepository{ - name: name, - namespace: namespace, - spec: *spec.DeepCopy(), - deployment: deployment, + name: repositorySpec.Name, + namespace: repositorySpec.Namespace, + spec: *ociSpec.DeepCopy(), + deployment: repositorySpec.Spec.Deployment, storage: storage, }, nil @@ -248,6 +262,10 @@ func (r *ociRepository) Refresh(_ context.Context) error { return nil } +func (r *ociRepository) Key() string { + return "oci://" + r.spec.Registry +} + // ToMainPackageRevision implements repository.PackageRevision. func (p *ociPackageRevision) ToMainPackageRevision() repository.PackageRevision { panic("unimplemented") diff --git a/pkg/repoimpl/repoimpl.go b/pkg/repoimpl/repoimpl.go new file mode 100644 index 00000000..6e3770fd --- /dev/null +++ b/pkg/repoimpl/repoimpl.go @@ -0,0 +1,71 @@ +// Copyright 2025 The kpt and Nephio Authors +// +// 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. + +package repoimpl + +import ( + "context" + "errors" + "fmt" + + configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" + "github.com/nephio-project/porch/pkg/repoimpl/git" + "github.com/nephio-project/porch/pkg/repoimpl/oci" + repoimpltypes "github.com/nephio-project/porch/pkg/repoimpl/types" + "github.com/nephio-project/porch/pkg/repository" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" +) + +var tracer = otel.Tracer("repoimpl") + +func RepositoryFactory(ctx context.Context, repositorySpec *configapi.Repository, options repoimpltypes.RepoImplOptions) (repository.Repository, error) { + ctx, span := tracer.Start(ctx, "Repository::RepositoryFactory", trace.WithAttributes()) + defer span.End() + + switch repositoryType := repositorySpec.Spec.Type; repositoryType { + case configapi.RepositoryTypeOCI: + return oci.GetRepositoryImpl(ctx, repositorySpec, options) + + case configapi.RepositoryTypeGit: + return git.GetRepositoryImpl(ctx, repositorySpec, options) + + default: + return nil, fmt.Errorf("type %q not supported", repositoryType) + } +} + +func RepositoryKey(repositorySpec *configapi.Repository) (string, error) { + switch repositoryType := repositorySpec.Spec.Type; repositoryType { + case configapi.RepositoryTypeOCI: + ociSpec := repositorySpec.Spec.Oci + if ociSpec == nil { + return "", fmt.Errorf("oci not configured") + } + return "oci://" + ociSpec.Registry, nil + + case configapi.RepositoryTypeGit: + gitSpec := repositorySpec.Spec.Git + if gitSpec == nil { + return "", errors.New("git property is required") + } + if gitSpec.Repo == "" { + return "", errors.New("git.repo property is required") + } + return fmt.Sprintf("git://%s/%s@%s/%s", gitSpec.Repo, gitSpec.Directory, repositorySpec.Namespace, repositorySpec.Name), nil + + default: + return "", fmt.Errorf("repository type %q not supported", repositoryType) + } +} diff --git a/pkg/repoimpl/types/repoimpltypes.go b/pkg/repoimpl/types/repoimpltypes.go new file mode 100644 index 00000000..66094fdc --- /dev/null +++ b/pkg/repoimpl/types/repoimpltypes.go @@ -0,0 +1,37 @@ +// Copyright 2025 The kpt and Nephio Authors +// +// 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. + +package repoimpltypes + +import ( + "time" + + "github.com/nephio-project/porch/pkg/meta" + "github.com/nephio-project/porch/pkg/repository" + "k8s.io/apimachinery/pkg/watch" +) + +type objectNotifier interface { + NotifyPackageRevisionChange(eventType watch.EventType, obj repository.PackageRevision) int +} + +type RepoImplOptions struct { + LocalDirectory string + RepoSyncFrequency time.Duration + UseUserDefinedCaBundle bool + CredentialResolver repository.CredentialResolver + UserInfoProvider repository.UserInfoProvider + MetadataStore meta.MetadataStore + ObjectNotifier objectNotifier +} diff --git a/pkg/task/clone.go b/pkg/task/clone.go index 5e9bbf28..bc6d84e8 100644 --- a/pkg/task/clone.go +++ b/pkg/task/clone.go @@ -23,9 +23,10 @@ import ( api "github.com/nephio-project/porch/api/porch/v1alpha1" configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" "github.com/nephio-project/porch/internal/kpt/builtins" - "github.com/nephio-project/porch/pkg/git" "github.com/nephio-project/porch/pkg/kpt" v1 "github.com/nephio-project/porch/pkg/kpt/api/kptfile/v1" + "github.com/nephio-project/porch/pkg/repoimpl/git" + repoimpltypes "github.com/nephio-project/porch/pkg/repoimpl/types" "github.com/nephio-project/porch/pkg/repository" "go.opentelemetry.io/otel/trace" "k8s.io/klog/v2" @@ -155,7 +156,9 @@ func (m *clonePackageMutation) cloneFromGit(ctx context.Context, gitPackage *api defer os.RemoveAll(dir) r, err := git.OpenRepository(ctx, "", "", &spec, false, dir, git.GitRepositoryOptions{ - CredentialResolver: m.credentialResolver, + RepoImplOptions: repoimpltypes.RepoImplOptions{ + CredentialResolver: m.credentialResolver, + }, MainBranchStrategy: git.SkipVerification, // We are only reading so we don't need the main branch to exist. }) if err != nil { diff --git a/pkg/task/clone_test.go b/pkg/task/clone_test.go index 6856d252..6f41ec22 100644 --- a/pkg/task/clone_test.go +++ b/pkg/task/clone_test.go @@ -36,7 +36,7 @@ import ( githttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/storage/memory" "github.com/nephio-project/porch/api/porch/v1alpha1" - "github.com/nephio-project/porch/pkg/git" + "github.com/nephio-project/porch/pkg/repoimpl/git" "github.com/nephio-project/porch/pkg/repository" ) diff --git a/pkg/task/edit_test.go b/pkg/task/edit_test.go index bedeb4fd..637aee44 100644 --- a/pkg/task/edit_test.go +++ b/pkg/task/edit_test.go @@ -23,8 +23,8 @@ import ( "github.com/nephio-project/porch/api/porch/v1alpha1" configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" kptfile "github.com/nephio-project/porch/pkg/kpt/api/kptfile/v1" + "github.com/nephio-project/porch/pkg/repoimpl/fake" "github.com/nephio-project/porch/pkg/repository" - "github.com/nephio-project/porch/pkg/repository/fake" ) func TestEdit(t *testing.T) { diff --git a/pkg/task/patch_test.go b/pkg/task/patch_test.go index 32961e88..6a2b2e92 100644 --- a/pkg/task/patch_test.go +++ b/pkg/task/patch_test.go @@ -22,8 +22,8 @@ import ( "github.com/google/go-cmp/cmp" api "github.com/nephio-project/porch/api/porch/v1alpha1" kptfile "github.com/nephio-project/porch/pkg/kpt/api/kptfile/v1" + "github.com/nephio-project/porch/pkg/repoimpl/fake" "github.com/nephio-project/porch/pkg/repository" - "github.com/nephio-project/porch/pkg/repository/fake" ) func TestSomething(t *testing.T) { diff --git a/test/e2e/suite.go b/test/e2e/suite.go index db6c8ffb..59004b7a 100644 --- a/test/e2e/suite.go +++ b/test/e2e/suite.go @@ -37,8 +37,8 @@ import ( configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" internalapi "github.com/nephio-project/porch/internal/api/porchinternal/v1alpha1" internalpkg "github.com/nephio-project/porch/internal/kpt/pkg" - "github.com/nephio-project/porch/pkg/git" kptfilev1 "github.com/nephio-project/porch/pkg/kpt/api/kptfile/v1" + "github.com/nephio-project/porch/pkg/repoimpl/git" "github.com/nephio-project/porch/pkg/repository" appsv1 "k8s.io/api/apps/v1" coreapi "k8s.io/api/core/v1" diff --git a/test/git/main.go b/test/git/main.go index cd8f0699..fde5fb3e 100644 --- a/test/git/main.go +++ b/test/git/main.go @@ -23,7 +23,7 @@ import ( "os" "os/signal" - "github.com/nephio-project/porch/pkg/git" + "github.com/nephio-project/porch/pkg/repoimpl/git" "k8s.io/klog/v2" )