chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
package sources
|
||||
|
||||
import "github.com/portainer/portainer/api/dataservices/source"
|
||||
|
||||
var adminUserContext = source.InsecureNewAdminContext()
|
||||
@@ -0,0 +1,56 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/source"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/portainer/portainer/pkg/fips"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
)
|
||||
|
||||
// RepoConfigInput holds the raw payload fields needed to resolve a git RepoConfig.
|
||||
// Set SourceID to resolve URL/auth from a stored source; otherwise provide the inline fields.
|
||||
type RepoConfigInput struct {
|
||||
SourceID portainer.SourceID
|
||||
ReferenceName string
|
||||
ConfigFilePath string
|
||||
RepositoryURL string
|
||||
TLSSkipVerify bool
|
||||
RepositoryAuthentication bool
|
||||
Username string
|
||||
Password string
|
||||
Provider gittypes.GitProvider
|
||||
AuthorizationType gittypes.GitCredentialAuthType
|
||||
}
|
||||
|
||||
// ResolveRepoConfig builds a RepoConfig from either a SourceID or inline URL/auth fields.
|
||||
func ResolveRepoConfig(tx gitSourceStore, userContext source.UserContext, input RepoConfigInput) (gittypes.RepoConfig, *httperror.HandlerError) {
|
||||
cfg := gittypes.RepoConfig{
|
||||
ReferenceName: input.ReferenceName,
|
||||
ConfigFilePath: input.ConfigFilePath,
|
||||
}
|
||||
|
||||
if input.SourceID != 0 {
|
||||
src, httpErr := ValidateGitSourceAccess(tx, userContext, input.SourceID)
|
||||
if httpErr != nil {
|
||||
return gittypes.RepoConfig{}, httpErr
|
||||
}
|
||||
cfg.URL = src.Git.URL
|
||||
cfg.Authentication = src.Git.Authentication
|
||||
cfg.TLSSkipVerify = src.Git.TLSSkipVerify
|
||||
} else {
|
||||
cfg.URL = input.RepositoryURL
|
||||
cfg.TLSSkipVerify = input.TLSSkipVerify
|
||||
if input.RepositoryAuthentication {
|
||||
cfg.Authentication = &gittypes.GitAuthentication{
|
||||
Username: input.Username,
|
||||
Password: input.Password,
|
||||
Provider: input.Provider,
|
||||
AuthorizationType: input.AuthorizationType,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg.TLSSkipVerify = cfg.TLSSkipVerify && fips.CanTLSSkipVerify()
|
||||
return cfg, nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/portainer/portainer/pkg/fips"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fips.InitFIPS(false)
|
||||
}
|
||||
|
||||
func TestResolveRepoConfig_WithSourceID_ReturnsSourceConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
|
||||
src := &portainer.Source{
|
||||
Type: portainer.SourceTypeGit,
|
||||
Git: &gittypes.GitSource{
|
||||
URL: "https://github.com/org/repo",
|
||||
TLSSkipVerify: true,
|
||||
Authentication: &gittypes.GitAuthentication{
|
||||
Username: "user",
|
||||
Password: "token",
|
||||
},
|
||||
},
|
||||
}
|
||||
require.NoError(t, store.Source().Create(adminUserContext, src))
|
||||
|
||||
cfg, httpErr := ResolveRepoConfig(store, adminUserContext, RepoConfigInput{
|
||||
SourceID: src.ID,
|
||||
ReferenceName: "refs/heads/main",
|
||||
ConfigFilePath: "docker-compose.yml",
|
||||
RepositoryURL: "https://ignored.example.com",
|
||||
})
|
||||
|
||||
require.Nil(t, httpErr)
|
||||
assert.Equal(t, src.Git.URL, cfg.URL)
|
||||
assert.Equal(t, src.Git.Authentication, cfg.Authentication)
|
||||
assert.Equal(t, src.Git.TLSSkipVerify, cfg.TLSSkipVerify)
|
||||
assert.Equal(t, "refs/heads/main", cfg.ReferenceName)
|
||||
assert.Equal(t, "docker-compose.yml", cfg.ConfigFilePath)
|
||||
}
|
||||
|
||||
func TestResolveRepoConfig_WithInlineURL_ReturnsInlineConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
|
||||
cfg, httpErr := ResolveRepoConfig(store, adminUserContext, RepoConfigInput{
|
||||
ReferenceName: "refs/heads/main",
|
||||
ConfigFilePath: "docker-compose.yml",
|
||||
RepositoryURL: "https://github.com/org/repo",
|
||||
TLSSkipVerify: true,
|
||||
RepositoryAuthentication: true,
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
})
|
||||
|
||||
require.Nil(t, httpErr)
|
||||
assert.Equal(t, "https://github.com/org/repo", cfg.URL)
|
||||
assert.True(t, cfg.TLSSkipVerify)
|
||||
require.NotNil(t, cfg.Authentication)
|
||||
assert.Equal(t, "user", cfg.Authentication.Username)
|
||||
assert.Equal(t, "pass", cfg.Authentication.Password)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/dataservices/source"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
)
|
||||
|
||||
// gitSourceStore is the minimal intersection of CE and EE DataStoreTx that these functions need.
|
||||
// Both EE and CE DataStoreTx satisfy it, even though they are incompatible as full interface types.
|
||||
type gitSourceStore interface {
|
||||
Source() dataservices.SourceService
|
||||
IsErrObjectNotFound(err error) bool
|
||||
}
|
||||
|
||||
// ValidateGitSourceAccess checks that the given Source exists and is a git Source, and returns it.
|
||||
func ValidateGitSourceAccess(tx gitSourceStore, userContext source.UserContext, sourceID portainer.SourceID) (*portainer.Source, *httperror.HandlerError) {
|
||||
src, err := tx.Source().Read(userContext, sourceID)
|
||||
if err != nil {
|
||||
if tx.IsErrObjectNotFound(err) {
|
||||
return nil, httperror.NotFound("Source not found", err)
|
||||
}
|
||||
return nil, httperror.InternalServerError("Unable to read source", err)
|
||||
}
|
||||
|
||||
if src.Type != portainer.SourceTypeGit {
|
||||
return nil, httperror.BadRequest(fmt.Sprintf("source %d is not a git source", sourceID), nil)
|
||||
}
|
||||
|
||||
if src.Git == nil {
|
||||
return nil, httperror.BadRequest("Source has no git configuration", nil)
|
||||
}
|
||||
|
||||
return src, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestValidateSourceForStack_ValidGitSource_ReturnsNil(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
|
||||
src := &portainer.Source{
|
||||
Type: portainer.SourceTypeGit,
|
||||
Git: &gittypes.GitSource{URL: "https://github.com/org/repo"},
|
||||
}
|
||||
require.NoError(t, store.Source().Create(adminUserContext, src))
|
||||
|
||||
_, httpErr := ValidateGitSourceAccess(store, adminUserContext, src.ID)
|
||||
assert.Nil(t, httpErr)
|
||||
}
|
||||
|
||||
func TestValidateSourceForStack_SourceNotFound_Returns404(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
|
||||
_, httpErr := ValidateGitSourceAccess(store, adminUserContext, portainer.SourceID(999))
|
||||
require.NotNil(t, httpErr)
|
||||
assert.Equal(t, http.StatusNotFound, httpErr.StatusCode)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package sources
|
||||
|
||||
import portainer "github.com/portainer/portainer/api"
|
||||
|
||||
// Status is the string representation of portainer.Status used in API responses.
|
||||
type Status string
|
||||
|
||||
const (
|
||||
SourceStatusUnknown Status = "unknown"
|
||||
SourceStatusHealthy Status = "healthy"
|
||||
SourceStatusError Status = "error"
|
||||
)
|
||||
|
||||
func StatusString(s portainer.SourceStatus) Status {
|
||||
switch s {
|
||||
case portainer.SourceStatusHealthy:
|
||||
return SourceStatusHealthy
|
||||
case portainer.SourceStatusError:
|
||||
return SourceStatusError
|
||||
default:
|
||||
return SourceStatusUnknown
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user