Split document model, add entrypoints for repos

Add NewBundleByPath function, that would return bundle built from
the specified path argument

Add CurrentContextEntryPoint method of the config
object, that would allow easily get kustomize root path based on
clusterType and phase. You can also leave phase arg empty string,
which would try to return bundle for all phases

Introduce changes to config pakage objects:

- Manifest:
  SubPath: this is relative path to the root of the repository that
contains directories with sites (SiteNames)
    PrimaryRepositoryName: this is a string that must correspond to a key
of the Repositories map of manifest object, which is used to derive
primary repository
    Repositories object is a map, map keys correspond to names of the
directories where `document pull` command will download repositories
defined in manifest prepended by manifest.TargetPath.

Introduce new config method CurrentContextEntryPoint(), method takes
TargetPath, PrimaryRepo.URL, SubPath, and clusterType and phase
constructs a path to the entry point out of which the DocumentBundle
should be build, and returns it to the caller. After that caller can
build a bundle out of it, the bundle will contain documents relevant to
particular cluster-type and phase.

All objects that depend on bundle interface are updated to use the
CurrentContextEntryPoint() method of the config object

Relates-To: #99

Closes: #99

Change-Id: I99320c4cb626841d46f4c298b583e9af90b1dce4
This commit is contained in:
Kostiantyn Kalynovskyi 2020-03-06 00:48:50 +00:00
parent 73e2c12aea
commit 147b97048b
46 changed files with 343 additions and 302 deletions

View File

@ -22,7 +22,7 @@ func getDummyAirshipSettings(t *testing.T) *environment.AirshipCTLSettings {
fx := fixtures.Basic().One() fx := fixtures.Basic().One()
mfst.Repository = &config.Repository{ mfst.Repositories = map[string]*config.Repository{"primary": {
URLString: fx.DotGit().Root(), URLString: fx.DotGit().Root(),
CheckoutOptions: &config.RepoCheckout{ CheckoutOptions: &config.RepoCheckout{
Branch: "master", Branch: "master",
@ -31,6 +31,7 @@ func getDummyAirshipSettings(t *testing.T) *environment.AirshipCTLSettings {
Auth: &config.RepoAuth{ Auth: &config.RepoAuth{
Type: "http-basic", Type: "http-basic",
}, },
},
} }
settings.SetConfig(conf) settings.SetConfig(conf)
return settings return settings

View File

@ -2,7 +2,7 @@ apiVersion: metal3.io/v1alpha1
kind: BareMetalHost kind: BareMetalHost
metadata: metadata:
labels: labels:
airshipit.org/ephemeral: "true" airshipit.org/node-role: "control-plane"
name: master-0 name: master-0
spec: spec:
online: true online: true

View File

@ -2,7 +2,7 @@ apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
labels: labels:
airshipit.org/ephemeral: "true" airshipit.org/node-role: "control-plane"
name: node1-bmc-secret name: node1-bmc-secret
type: Opaque type: Opaque
stringData: stringData:

View File

@ -0,0 +1,2 @@
resources:
- ../../../type/test-bootstrap

View File

@ -1,2 +0,0 @@
resources:
- ../../type/test-bootstrap

View File

@ -8,12 +8,10 @@ import (
"sigs.k8s.io/kustomize/v3/pkg/types" "sigs.k8s.io/kustomize/v3/pkg/types"
"opendev.org/airship/airshipctl/pkg/document" "opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/testutil"
) )
func TestGetCloudData(t *testing.T) { func TestGetCloudData(t *testing.T) {
fSys := testutil.SetupTestFs(t, "testdata") bundle, err := document.NewBundleByPath("testdata")
bundle, err := document.NewBundle(fSys, "/", "/")
require.NoError(t, err, "Building Bundle Failed") require.NoError(t, err, "Building Bundle Failed")
tests := []struct { tests := []struct {

View File

@ -0,0 +1,41 @@
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/node-role: "control-plane"
name: node1-bmc-secret
type: Opaque
data:
netconfig: bmV0Y29uZmlnCg==
stringData:
userdata: cloud-init
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/node-role: "worker"
name: node1-bmc-secret1
type: Opaque
---
apiVersion: v1
kind: Secret
metadata:
labels:
test: nodataforcfg
name: node1-bmc-secret2
type: Opaque
data:
foo: bmV0Y29uZmlnCg==
---
apiVersion: v1
kind: Secret
metadata:
labels:
some-data: "True"
name: node1-bmc-in-secret2
type: Opaque
data:
netconfig: bmV0Y29uZmlnCg==
stringData:
userdata: cloud-init

View File

@ -34,19 +34,17 @@ func GenerateBootstrapIso(settings *environment.AirshipCTLSettings) error {
return err return err
} }
var manifest *config.Manifest
manifest, err = globalConf.CurrentContextManifest()
if err != nil {
return err
}
if err = verifyInputs(cfg); err != nil { if err = verifyInputs(cfg); err != nil {
return err return err
} }
// TODO (dukov) replace with the appropriate function once it's available // TODO (dukov) replace with the appropriate function once it's available
// in document module // in document module
docBundle, err := document.NewBundle(document.NewDocumentFs(), manifest.TargetPath, "") root, err := globalConf.CurrentContextEntryPoint(config.Ephemeral, "")
if err != nil {
return err
}
docBundle, err := document.NewBundleByPath(root)
if err != nil { if err != nil {
return err return err
} }

View File

@ -45,8 +45,7 @@ func (mc *mockContainer) GetID() string {
} }
func TestBootstrapIso(t *testing.T) { func TestBootstrapIso(t *testing.T) {
fSys := testutil.SetupTestFs(t, "testdata") bundle, err := document.NewBundleByPath("testdata/primary/site/test-site/ephemeral")
bundle, err := document.NewBundle(fSys, "/", "/")
require.NoError(t, err, "Building Bundle Failed") require.NoError(t, err, "Building Bundle Failed")
tempVol, cleanup := testutil.TempDir(t, "bootstrap-test") tempVol, cleanup := testutil.TempDir(t, "bootstrap-test")

View File

@ -39,7 +39,6 @@ func (infra *Infra) Run() error {
// Deploy method deploys documents // Deploy method deploys documents
func (infra *Infra) Deploy() error { func (infra *Infra) Deploy() error {
kctl := infra.Client.Kubectl() kctl := infra.Client.Kubectl()
var err error
ao, err := kctl.ApplyOptions() ao, err := kctl.ApplyOptions()
if err != nil { if err != nil {
return err return err
@ -56,29 +55,23 @@ func (infra *Infra) Deploy() error {
return err return err
} }
var manifest *config.Manifest kustomizePath, err := globalConf.CurrentContextEntryPoint(infra.ClusterType, config.Initinfra)
manifest, err = globalConf.CurrentContextManifest()
if err != nil { if err != nil {
return err return err
} }
b, err := document.NewBundle(infra.FileSystem, manifest.TargetPath, "") b, err := document.NewBundleByPath(kustomizePath)
if err != nil { if err != nil {
return err return err
} }
ls := document.EphemeralClusterSelector // TODO (kkalynovskyi) Add Selector that would filter by label indicating wether to deploy it to k8s
selector := document.NewSelector().ByLabel(ls) docs, err := b.GetAllDocuments()
// Get documents that are annotated to belong to initinfra
docs, err := b.Select(selector)
if err != nil { if err != nil {
return err return err
} }
if len(docs) == 0 { if len(docs) == 0 {
return document.ErrDocNotFound{ return document.ErrDocNotFound{}
Selector: selector,
}
} }
// Label every document indicating that it was deployed by initinfra module for further reference // Label every document indicating that it was deployed by initinfra module for further reference

View File

@ -28,7 +28,7 @@ func (tc TestClient) Kubectl() kubectl.Interface { return tc.MockKubectl()
const ( const (
kubeconfigPath = "testdata/kubeconfig.yaml" kubeconfigPath = "testdata/kubeconfig.yaml"
filenameRC = "testdata/replicationcontroller.yaml" filenameRC = "testdata/primary/site/test-site/ephemeral/initinfra/replicationcontroller.yaml"
airshipConfigFile = "testdata/config.yaml" airshipConfigFile = "testdata/config.yaml"
) )

View File

@ -13,20 +13,19 @@ current-context: dummy_cluster
kind: Config kind: Config
manifests: manifests:
dummy_manifest: dummy_manifest:
primary-repository-name: primary
repositories: repositories:
dummy: primary:
target-path: dummy_targetpath auth:
url: ssh-key: testdata/test-key.pem
ForceQuery: false type: ssh-key
Fragment: "" checkout:
Host: dummy.url.com branch: ""
Opaque: "" force: false
Path: "" remote-ref: ""
RawPath: "" tag: v1.0.1
RawQuery: "" url: http://dummy.url.com/primary.git
Scheme: http sub-path: primary/site/test-site
User: null
username: dummy_user
target-path: testdata target-path: testdata
modules-config: modules-config:
bootstrapInfo: bootstrapInfo:

View File

@ -4,9 +4,7 @@ metadata:
name: test-rc name: test-rc
namespace: test namespace: test
labels: labels:
airshipit.org/ephemeral: "true" airshipit.org/initinfra: "true"
name: test-rc
airship-component: "initinfra"
spec: spec:
replicas: 1 replicas: 1
template: template:

View File

@ -0,0 +1,2 @@
resources:
- initinfra

View File

@ -21,15 +21,15 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
"sigs.k8s.io/yaml"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/yaml"
"opendev.org/airship/airshipctl/pkg/util" "opendev.org/airship/airshipctl/pkg/util"
) )
@ -634,6 +634,28 @@ func (c *Config) CurrentContextManifest() (*Manifest, error) {
return c.Manifests[currentContext.Manifest], nil return c.Manifests[currentContext.Manifest], nil
} }
// CurrentContextEntryPoint returns path to build bundle based on clusterType and phase
// example CurrentContextEntryPoint("ephemeral", "initinfra")
func (c *Config) CurrentContextEntryPoint(clusterType string, phase string) (string, error) {
err := ValidClusterType(clusterType)
if err != nil {
return "", err
}
ccm, err := c.CurrentContextManifest()
if err != nil {
return "", err
}
_, exists := ccm.Repositories[ccm.PrimaryRepositoryName]
if !exists {
return "", ErrMissingPrimaryRepo{}
}
return path.Join(
ccm.TargetPath,
ccm.SubPath,
clusterType,
phase), nil
}
// Credential or AuthInfo related methods // Credential or AuthInfo related methods
func (c *Config) GetAuthInfo(aiName string) (*AuthInfo, error) { func (c *Config) GetAuthInfo(aiName string) (*AuthInfo, error) {
authinfo, exists := c.AuthInfos[aiName] authinfo, exists := c.AuthInfos[aiName]
@ -845,9 +867,8 @@ func (m *Manifest) Equal(n *Manifest) bool {
if n == nil { if n == nil {
return n == m return n == m
} }
repositoryEq := reflect.DeepEqual(m.Repository, n.Repository) reposEq := reflect.DeepEqual(m.Repositories, n.Repositories)
extraReposEq := reflect.DeepEqual(m.ExtraRepositories, n.ExtraRepositories) return reposEq && m.TargetPath == n.TargetPath && m.SubPath == n.SubPath
return repositoryEq && extraReposEq && m.TargetPath == n.TargetPath
} }
func (m *Manifest) String() string { func (m *Manifest) String() string {

View File

@ -11,6 +11,11 @@ const (
AirshipClusterDefaultType = Target AirshipClusterDefaultType = Target
) )
// Constants related to Phases
const (
Initinfra = "initinfra"
)
// Sorted // Sorted
var AllClusterTypes = [2]string{Ephemeral, Target} var AllClusterTypes = [2]string{Ephemeral, Target}
@ -58,3 +63,8 @@ const (
FlagUsername = "username" FlagUsername = "username"
FlagCurrent = "current" FlagCurrent = "current"
) )
// Constants related to filesystem
const (
SiteDirectory = "site"
)

View File

@ -93,3 +93,11 @@ type ErrMissingCurrentContext struct {
func (e ErrMissingCurrentContext) Error() string { func (e ErrMissingCurrentContext) Error() string {
return "Current context must be set before using --current flag" return "Current context must be set before using --current flag"
} }
// ErrMissingPrimaryRepo returned when Primary Repository is not set in context manifest
type ErrMissingPrimaryRepo struct {
}
func (e ErrMissingPrimaryRepo) Error() string {
return "Current context manifest must have primary repository set"
}

View File

@ -16,7 +16,9 @@ current-context: dummy_context
kind: Config kind: Config
manifests: manifests:
dummy_manifest: dummy_manifest:
repository: primary-repository-name: primary
repositories:
primary:
auth: auth:
ssh-key: testdata/test-key.pem ssh-key: testdata/test-key.pem
type: ssh-key type: ssh-key
@ -25,7 +27,8 @@ manifests:
force: false force: false
remote-ref: "" remote-ref: ""
tag: v1.0.1 tag: v1.0.1
url: http://dummy.url.com url: http://dummy.url.com/manifests.git
sub-path: manifests/site/test-site
target-path: /var/tmp/ target-path: /var/tmp/
modules-config: modules-config:
bootstrapInfo: bootstrapInfo:

View File

@ -1,4 +1,6 @@
repository: primary-repository-name: primary
repositories:
primary:
auth: auth:
ssh-key: testdata/test-key.pem ssh-key: testdata/test-key.pem
type: ssh-key type: ssh-key
@ -7,5 +9,6 @@ repository:
force: false force: false
remote-ref: "" remote-ref: ""
tag: v1.0.1 tag: v1.0.1
url: http://dummy.url.com url: http://dummy.url.com/manifests.git
sub-path: manifests/site/test-site
target-path: /var/tmp/ target-path: /var/tmp/

View File

@ -6,4 +6,4 @@ checkout:
force: false force: false
remote-ref: "" remote-ref: ""
tag: v1.0.1 tag: v1.0.1
url: http://dummy.url.com url: http://dummy.url.com/manifests.git

View File

@ -113,15 +113,23 @@ type AuthInfo struct {
authInfo *kubeconfig.AuthInfo authInfo *kubeconfig.AuthInfo
} }
// Manifests is a tuple of references to a Manifest (how do Identify, collect , // Manifest is a tuple of references to a Manifest (how do Identify, collect ,
// find the yaml manifests that airship uses to perform its operations) // find the yaml manifests that airship uses to perform its operations)
type Manifest struct { type Manifest struct {
// Repositories is the map of repository adddressable by a name // PrimaryRepositoryName is a name of the repo, that contains site/<site-name> directory
Repository *Repository `json:"repository"` // and is a starting point for building document bundle
PrimaryRepositoryName string `json:"primary-repository-name"`
// ExtraRepositories is the map of extra repositories addressable by a name // ExtraRepositories is the map of extra repositories addressable by a name
ExtraRepositories map[string]*Repository `json:"extra-repositories,omitempty"` Repositories map[string]*Repository `json:"repositories,omitempty"`
// TargetPath Local Target path for working or home dirctory for all Manifest Cloned/Returned/Generated // TargetPath Local Target path for working or home dirctory for all Manifest Cloned/Returned/Generated
TargetPath string `json:"target-path"` TargetPath string `json:"target-path"`
// SubPath is a path relative to TargetPath + Path where PrimaryRepository is cloned and contains
// directories with ClusterType and Phase bundles, example:
// Repositories[PrimaryRepositoryName].Url = 'https://github.com/airshipit/treasuremap'
// SubPath = "manifests"
// you would expect that at treasuremap/manifests you would have ephemeral/initinfra and
// ephemera/target directories, containing kustomize.yaml.
SubPath string `json:"sub-path"`
} }
// Repository is a tuple that holds the information for the remote sources of manifest yaml documents. // Repository is a tuple that holds the information for the remote sources of manifest yaml documents.

View File

@ -16,6 +16,10 @@ limitations under the License.
package config package config
const (
DefaultTestPrimaryRepo = "primary"
)
// NewConfig returns a newly initialized Config object // NewConfig returns a newly initialized Config object
func NewConfig() *Config { func NewConfig() *Config {
return &Config{ return &Config{
@ -30,7 +34,8 @@ func NewConfig() *Config {
}, },
Manifests: map[string]*Manifest{ Manifests: map[string]*Manifest{
AirshipDefaultManifest: { AirshipDefaultManifest: {
Repository: &Repository{ Repositories: map[string]*Repository{
DefaultTestPrimaryRepo: {
URLString: AirshipDefaultManifestRepoLocation, URLString: AirshipDefaultManifestRepoLocation,
CheckoutOptions: &RepoCheckout{ CheckoutOptions: &RepoCheckout{
CommitHash: "master", CommitHash: "master",
@ -38,7 +43,10 @@ func NewConfig() *Config {
RemoteRef: "master", RemoteRef: "master",
}, },
}, },
},
TargetPath: "/tmp/" + AirshipDefaultManifest, TargetPath: "/tmp/" + AirshipDefaultManifest,
PrimaryRepositoryName: DefaultTestPrimaryRepo,
SubPath: AirshipDefaultManifestRepo + "/manifests/site",
}, },
}, },
ModulesConfig: &Modules{ ModulesConfig: &Modules{
@ -78,8 +86,8 @@ func NewCluster() *Cluster {
// object with non-nil maps // object with non-nil maps
func NewManifest() *Manifest { func NewManifest() *Manifest {
return &Manifest{ return &Manifest{
Repository: NewRepository(), PrimaryRepositoryName: DefaultTestPrimaryRepo,
ExtraRepositories: make(map[string]*Repository), Repositories: map[string]*Repository{DefaultTestPrimaryRepo: NewRepository()},
} }
} }

View File

@ -57,6 +57,12 @@ type Bundle interface {
GetAllDocuments() ([]Document, error) GetAllDocuments() ([]Document, error)
} }
// NewBundleByPath helper function that returns new document.Bundle interface based on clusterType and
// phase, example: helpers.NewBunde(airConfig, "ephemeral", "initinfra")
func NewBundleByPath(rootPath string) (Bundle, error) {
return NewBundle(NewDocumentFs(), rootPath, "")
}
// NewBundle is a convenience function to create a new bundle // NewBundle is a convenience function to create a new bundle
// Over time, it will evolve to support allowing more control // Over time, it will evolve to support allowing more control
// for kustomize plugins // for kustomize plugins

View File

@ -3,7 +3,7 @@ package document
const ( const (
// Selectors // Selectors
BaseAirshipSelector = "airshipit.org" BaseAirshipSelector = "airshipit.org"
EphemeralClusterSelector = BaseAirshipSelector + "/ephemeral in (True, true)" ControlNodeSelector = BaseAirshipSelector + "/node-role=control-plane"
// Labels // Labels
DeployedByLabel = BaseAirshipSelector + "/deployed" DeployedByLabel = BaseAirshipSelector + "/deployed"

View File

@ -25,19 +25,8 @@ func (s *Settings) cloneRepositories() error {
return err return err
} }
mainRepoConfig := currentManifest.Repository // Clone repositories
repository, err := repo.NewRepository(currentManifest.TargetPath, mainRepoConfig) for _, extraRepoConfig := range currentManifest.Repositories {
if err != nil {
return err
}
err = repository.Download(mainRepoConfig.ToCheckoutOptions(true).Force)
if err != nil {
return err
}
repository.Driver.Close()
// Clone extra repositories
for _, extraRepoConfig := range currentManifest.ExtraRepositories {
repository, err := repo.NewRepository(currentManifest.TargetPath, extraRepoConfig) repository, err := repo.NewRepository(currentManifest.TargetPath, extraRepoConfig)
if err != nil { if err != nil {
return err return err

View File

@ -3,7 +3,6 @@ package pull
import ( import (
"io/ioutil" "io/ioutil"
"path" "path"
"path/filepath"
"strings" "strings"
"testing" "testing"
@ -16,6 +15,7 @@ import (
"opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/environment" "opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/util"
"opendev.org/airship/airshipctl/testutil" "opendev.org/airship/airshipctl/testutil"
) )
@ -42,7 +42,7 @@ func TestPull(t *testing.T) {
fx := fixtures.Basic().One() fx := fixtures.Basic().One()
dummyGitDir := fx.DotGit().Root() dummyGitDir := fx.DotGit().Root()
currentManifest.Repository = &config.Repository{ currentManifest.Repositories = map[string]*config.Repository{currentManifest.PrimaryRepositoryName: {
URLString: dummyGitDir, URLString: dummyGitDir,
CheckoutOptions: &config.RepoCheckout{ CheckoutOptions: &config.RepoCheckout{
Branch: "master", Branch: "master",
@ -51,6 +51,7 @@ func TestPull(t *testing.T) {
Auth: &config.RepoAuth{ Auth: &config.RepoAuth{
Type: "http-basic", Type: "http-basic",
}, },
},
} }
tmpDir, cleanup := testutil.TempDir(t, "airshipctlPullTest-") tmpDir, cleanup := testutil.TempDir(t, "airshipctlPullTest-")
@ -58,13 +59,13 @@ func TestPull(t *testing.T) {
currentManifest.TargetPath = tmpDir currentManifest.TargetPath = tmpDir
_, err = repo2.NewRepository(".", currentManifest.Repository) _, err = repo2.NewRepository(".", currentManifest.Repositories[currentManifest.PrimaryRepositoryName])
require.NoError(err) require.NoError(err)
err = dummyPullSettings.cloneRepositories() err = dummyPullSettings.cloneRepositories()
require.NoError(err) require.NoError(err)
dummyRepoDirName := filepath.Base(dummyGitDir) dummyRepoDirName := util.GitDirNameFromURL(dummyGitDir)
assert.FileExists(path.Join(tmpDir, dummyRepoDirName, "go/example.go")) assert.FileExists(path.Join(tmpDir, dummyRepoDirName, "go/example.go"))
assert.FileExists(path.Join(tmpDir, dummyRepoDirName, ".git/HEAD")) assert.FileExists(path.Join(tmpDir, dummyRepoDirName, ".git/HEAD"))
contents, err := ioutil.ReadFile(path.Join(tmpDir, dummyRepoDirName, ".git/HEAD")) contents, err := ioutil.ReadFile(path.Join(tmpDir, dummyRepoDirName, ".git/HEAD"))
@ -82,7 +83,8 @@ func TestPull(t *testing.T) {
mfst := conf.Manifests["dummy_manifest"] mfst := conf.Manifests["dummy_manifest"]
dummyGitDir := fx.DotGit().Root() dummyGitDir := fx.DotGit().Root()
mfst.Repository = &config.Repository{ mfst.Repositories = map[string]*config.Repository{
mfst.PrimaryRepositoryName: {
URLString: dummyGitDir, URLString: dummyGitDir,
CheckoutOptions: &config.RepoCheckout{ CheckoutOptions: &config.RepoCheckout{
Branch: "master", Branch: "master",
@ -91,6 +93,7 @@ func TestPull(t *testing.T) {
Auth: &config.RepoAuth{ Auth: &config.RepoAuth{
Type: "http-basic", Type: "http-basic",
}, },
},
} }
dummyPullSettings.SetConfig(conf) dummyPullSettings.SetConfig(conf)
@ -103,7 +106,7 @@ func TestPull(t *testing.T) {
err = dummyPullSettings.Pull() err = dummyPullSettings.Pull()
require.NoError(err) require.NoError(err)
dummyRepoDirName := filepath.Base(dummyGitDir) dummyRepoDirName := util.GitDirNameFromURL(dummyGitDir)
assert.FileExists(path.Join(tmpDir, dummyRepoDirName, "go/example.go")) assert.FileExists(path.Join(tmpDir, dummyRepoDirName, "go/example.go"))
assert.FileExists(path.Join(tmpDir, dummyRepoDirName, ".git/HEAD")) assert.FileExists(path.Join(tmpDir, dummyRepoDirName, ".git/HEAD"))
contents, err := ioutil.ReadFile(path.Join(tmpDir, dummyRepoDirName, ".git/HEAD")) contents, err := ioutil.ReadFile(path.Join(tmpDir, dummyRepoDirName, ".git/HEAD"))

View File

@ -28,7 +28,7 @@ type GitDriver struct {
Storer storage.Storer Storer storage.Storer
} }
func NewGitDriver(fs billy.Filesystem, s storage.Storer) *GitDriver { func NewGitDriver(fs billy.Filesystem, s storage.Storer) Adapter {
return &GitDriver{Storer: s, Filesystem: fs} return &GitDriver{Storer: s, Filesystem: fs}
} }

View File

@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"path/filepath" "path/filepath"
"strings"
"gopkg.in/src-d/go-billy.v4" "gopkg.in/src-d/go-billy.v4"
"gopkg.in/src-d/go-billy.v4/osfs" "gopkg.in/src-d/go-billy.v4/osfs"
@ -15,6 +14,7 @@ import (
"gopkg.in/src-d/go-git.v4/storage/filesystem" "gopkg.in/src-d/go-git.v4/storage/filesystem"
"opendev.org/airship/airshipctl/pkg/log" "opendev.org/airship/airshipctl/pkg/log"
"opendev.org/airship/airshipctl/pkg/util"
) )
var ( var (
@ -41,7 +41,7 @@ type Repository struct {
// NewRepository create repository object, with real filesystem on disk // NewRepository create repository object, with real filesystem on disk
// basePath is used to calculate final path where to clone/open the repository // basePath is used to calculate final path where to clone/open the repository
func NewRepository(basePath string, builder OptionsBuilder) (*Repository, error) { func NewRepository(basePath string, builder OptionsBuilder) (*Repository, error) {
dirName := nameFromURL(builder.URL()) dirName := util.GitDirNameFromURL(builder.URL())
if dirName == "" { if dirName == "" {
return nil, fmt.Errorf("URL: %s, original error: %w", builder.URL(), ErrCantParseURL) return nil, fmt.Errorf("URL: %s, original error: %w", builder.URL(), ErrCantParseURL)
} }
@ -60,11 +60,6 @@ func NewRepository(basePath string, builder OptionsBuilder) (*Repository, error)
}, nil }, nil
} }
func nameFromURL(urlString string) string {
_, fileName := filepath.Split(urlString)
return strings.TrimSuffix(fileName, ".git")
}
func storerFromFs(fs billy.Filesystem) (storage.Storer, error) { func storerFromFs(fs billy.Filesystem) (storage.Storer, error) {
dot, err := fs.Chroot(".git") dot, err := fs.Chroot(".git")
if err != nil { if err != nil {

View File

@ -38,19 +38,6 @@ func (md mockBuilder) ToFetchOptions(transport.AuthMethod) *git.FetchOptions {
} }
func (md mockBuilder) URL() string { return md.URLString } func (md mockBuilder) URL() string { return md.URLString }
func TestNewRepositoryNegative(t *testing.T) {
err := fixtures.Init()
require.NoError(t, err)
defer testutil.CleanUpGitFixtures(t)
builder := &mockBuilder{
URLString: "",
}
repo, err := NewRepository(".", builder)
assert.Error(t, err)
assert.Nil(t, repo)
}
func TestDownload(t *testing.T) { func TestDownload(t *testing.T) {
err := fixtures.Init() err := fixtures.Init()
require.NoError(t, err) require.NoError(t, err)
@ -216,47 +203,3 @@ func TestCheckout(t *testing.T) {
err = repo.Checkout(true) err = repo.Checkout(true)
assert.Error(t, err) assert.Error(t, err)
} }
func TestURLtoName(t *testing.T) {
tests := []struct {
input string
expectedOutput string
}{
{
input: "https://github.com/kubernetes/kubectl.git",
expectedOutput: "kubectl",
},
{
input: "git@github.com:kubernetes/kubectl.git",
expectedOutput: "kubectl",
},
{
input: "https://github.com/kubernetes/kube.somepath.ctl.git",
expectedOutput: "kube.somepath.ctl",
},
{
input: "https://github.com/kubernetes/kubectl",
expectedOutput: "kubectl",
},
{
input: "git@github.com:kubernetes/kubectl",
expectedOutput: "kubectl",
},
{
input: "github.com:kubernetes/kubectl.git",
expectedOutput: "kubectl",
},
{
input: "/kubernetes/kubectl.git",
expectedOutput: "kubectl",
},
{
input: "/kubernetes/kubectl.git/",
expectedOutput: "",
},
}
for _, test := range tests {
assert.Equal(t, test.expectedOutput, nameFromURL(test.input))
}
}

View File

@ -63,10 +63,6 @@ func getRemoteDirectClient(remoteConfig *config.RemoteDirect, remoteURL string)
func getRemoteDirectConfig(settings *environment.AirshipCTLSettings) (*config.RemoteDirect, string, error) { func getRemoteDirectConfig(settings *environment.AirshipCTLSettings) (*config.RemoteDirect, string, error) {
cfg := settings.Config() cfg := settings.Config()
manifest, err := cfg.CurrentContextManifest()
if err != nil {
return nil, "", err
}
bootstrapSettings, err := cfg.CurrentContextBootstrapInfo() bootstrapSettings, err := cfg.CurrentContextBootstrapInfo()
if err != nil { if err != nil {
return nil, "", err return nil, "", err
@ -77,14 +73,17 @@ func getRemoteDirectConfig(settings *environment.AirshipCTLSettings) (*config.Re
return nil, "", config.ErrMissingConfig{What: "RemoteDirect options not defined in bootstrap config"} return nil, "", config.ErrMissingConfig{What: "RemoteDirect options not defined in bootstrap config"}
} }
// TODO (dukov) replace with the appropriate function once it's available root, err := cfg.CurrentContextEntryPoint(config.Ephemeral, "")
// in document module
docBundle, err := document.NewBundle(document.NewDocumentFs(), manifest.TargetPath, "")
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
ls := document.EphemeralClusterSelector docBundle, err := document.NewBundleByPath(root)
if err != nil {
return nil, "", err
}
ls := document.ControlNodeSelector
selector := document.NewSelector(). selector := document.NewSelector().
ByGvk("", "", AirshipHostKind). ByGvk("", "", AirshipHostKind).
ByLabel(ls) ByLabel(ls)

View File

@ -14,6 +14,7 @@ import (
) )
func initSettings(t *testing.T, rd *config.RemoteDirect, testdata string) *environment.AirshipCTLSettings { func initSettings(t *testing.T, rd *config.RemoteDirect, testdata string) *environment.AirshipCTLSettings {
t.Helper()
settings := &environment.AirshipCTLSettings{} settings := &environment.AirshipCTLSettings{}
settings.SetConfig(testutil.DummyConfig()) settings.SetConfig(testutil.DummyConfig())
bi, err := settings.Config().CurrentContextBootstrapInfo() bi, err := settings.Config().CurrentContextBootstrapInfo()
@ -36,7 +37,6 @@ func TestUnknownRemoteType(t *testing.T) {
) )
err := DoRemoteDirect(s) err := DoRemoteDirect(s)
_, ok := err.(*GenericError) _, ok := err.(*GenericError)
assert.True(t, ok) assert.True(t, ok)
} }
@ -52,7 +52,6 @@ func TestRedfishRemoteDirectWithEmptyURL(t *testing.T) {
) )
err := DoRemoteDirect(s) err := DoRemoteDirect(s)
_, ok := err.(redfish.ErrRedfishMissingConfig) _, ok := err.(redfish.ErrRedfishMissingConfig)
assert.True(t, ok) assert.True(t, ok)
} }

View File

@ -1,49 +0,0 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/ephemeral: "true"
name: master-0
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/test-node
credentialsName: master-0-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral: "true"
name: master-0-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/target: "true"
name: master-1
spec:
online: "true"
bootMACAddress: 01:3b:8b:0c:ec:8b
bmc:
address: ipmi://192.168.111.2:6230
credentialsName: master-1-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/target: "true"
name: master-1-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
...

View File

@ -0,0 +1,25 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/node-role: "control-plane"
name: master-0
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/test-node
credentialsName: master-0-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/node-role: "control-plane"
name: master-0-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
...

View File

@ -1,49 +0,0 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/ephemeral: "true"
name: master-0
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: ""
credentialsName: master-0-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral: "true"
name: master-0-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/target: "true"
name: master-1
spec:
online: true
bootMACAddress: 01:3b:8b:0c:ec:8b
bmc:
address: ipmi://192.168.111.2:6230
credentialsName: master-1-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/target: "true"
name: master-1-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
...

View File

@ -0,0 +1,25 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/node-role: "control-plane"
name: master-0
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: ""
credentialsName: master-0-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/node-role: "control-plane"
name: master-0-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
...

12
pkg/util/url.go Normal file
View File

@ -0,0 +1,12 @@
package util
import (
"path/filepath"
"strings"
)
// GitDirNameFromURL extract directory name of the repository from URL
func GitDirNameFromURL(urlString string) string {
_, fileName := filepath.Split(urlString)
return strings.TrimSuffix(fileName, ".git")
}

51
pkg/util/url_test.go Normal file
View File

@ -0,0 +1,51 @@
package util
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGitDirNameFromURL(t *testing.T) {
tests := []struct {
input string
expectedOutput string
}{
{
input: "https://github.com/kubernetes/kubectl.git",
expectedOutput: "kubectl",
},
{
input: "git@github.com:kubernetes/kubectl.git",
expectedOutput: "kubectl",
},
{
input: "https://github.com/kubernetes/kube.somepath.ctl.git",
expectedOutput: "kube.somepath.ctl",
},
{
input: "https://github.com/kubernetes/kubectl",
expectedOutput: "kubectl",
},
{
input: "git@github.com:kubernetes/kubectl",
expectedOutput: "kubectl",
},
{
input: "github.com:kubernetes/kubectl.git",
expectedOutput: "kubectl",
},
{
input: "/kubernetes/kubectl.git",
expectedOutput: "kubectl",
},
{
input: "/kubernetes/kubectl.git/",
expectedOutput: "",
},
}
for _, test := range tests {
assert.Equal(t, test.expectedOutput, GitDirNameFromURL(test.input))
}
}

View File

@ -1,7 +1,6 @@
airship_config_action: generate airship_config_action: generate
airship_config_site_name: "test-bootstrap"
airship_config_iso_gen_target_path: "{{ serve_dir }}" airship_config_iso_gen_target_path: "{{ serve_dir }}"
airship_config_manifest_directory: "{{ remote_work_dir | default(zuul.project.src_dir) }}/manifests/site/{{ airship_config_site_name }}" airship_config_manifest_directory: "{{ remote_work_dir | default(zuul.project.src_dir) }}"
airship_config_ephemeral_ip: "{{ airship_gate_ipam.nat_network.ephemeral_ip }}" airship_config_ephemeral_ip: "{{ airship_gate_ipam.nat_network.ephemeral_ip }}"
airship_config_iso_builder_docker_image: "kkalynovskyi/image-builder:latest" airship_config_iso_builder_docker_image: "kkalynovskyi/image-builder:latest"
airship_config_ca_data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= airship_config_ca_data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=

View File

@ -1,8 +1,6 @@
airship_config_iso_gen_target_path: /srv/iso airship_config_iso_gen_target_path: /srv/iso
airship_config_manifest_directory: /tmp/airship airship_config_manifest_directory: /tmp/airship
airship_config_primary_repo_url: dummy.url.com airship_config_primary_repo_url: dummy.url.com
airship_config_primary_repo_auth_type: ssh-key
airship_config_primary_repo_key_path: ""
airship_config_ephemeral_ip: "10.23.25.101" airship_config_ephemeral_ip: "10.23.25.101"
airship_config_iso_builder_docker_image: quay.io/airshipit/isogen:latest airship_config_iso_builder_docker_image: quay.io/airshipit/isogen:latest
airship_config_iso_port: 8099 airship_config_iso_port: 8099

View File

@ -13,16 +13,19 @@ current-context: dummy_cluster
kind: Config kind: Config
manifests: manifests:
dummy_manifest: dummy_manifest:
repository: primary-repository: primary
auth: repositories:
ssh-key: {{ airship_config_primary_repo_key_path | default("") }} primary:
type: {{ airship_config_primary_repo_auth_type }}
checkout: checkout:
branch: "master" branch: "master"
force: false force: false
remote-ref: "" remote-ref: ""
tag: "" tag: ""
url: {{ airship_config_primary_repo_url }} url: {{ airship_config_primary_repo_url }}
## this is temporary hack, as soon as we use `document pull` command in gate process
## this will subpath will be airshipctl/manifests/site/test-bootstrap, as airshipctl
## will be primary repository
sub-path: "manifests/site/test-bootstrap"
target-path: {{ airship_config_manifest_directory }} target-path: {{ airship_config_manifest_directory }}
modules-config: modules-config:
bootstrapInfo: bootstrapInfo:

View File

@ -90,15 +90,17 @@ func DummyCluster() *config.Cluster {
func DummyManifest() *config.Manifest { func DummyManifest() *config.Manifest {
m := config.NewManifest() m := config.NewManifest()
// Repositories is the map of repository adddressable by a name // Repositories is the map of repository adddressable by a name
m.Repository = DummyRepository() m.Repositories = map[string]*config.Repository{"primary": DummyRepository()}
m.PrimaryRepositoryName = "primary"
m.TargetPath = "/var/tmp/" m.TargetPath = "/var/tmp/"
m.SubPath = "manifests/site/test-site"
return m return m
} }
// DummyRepository, utility function used for tests // DummyRepository, utility function used for tests
func DummyRepository() *config.Repository { func DummyRepository() *config.Repository {
return &config.Repository{ return &config.Repository{
URLString: "http://dummy.url.com", URLString: "http://dummy.url.com/manifests.git",
CheckoutOptions: &config.RepoCheckout{ CheckoutOptions: &config.RepoCheckout{
Tag: "v1.0.1", Tag: "v1.0.1",
ForceCheckout: false, ForceCheckout: false,