diff --git a/Dockerfile b/Dockerfile
index e4d864a..f6bc620 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,6 +18,8 @@ COPY ./test_data/apache/example2.com.conf ${apacheAvailableSitesDir}
COPY ./test_data/apache/example-ssl.com.conf ${apacheAvailableSitesDir}
COPY ./test_data/apache/example3.com.conf ${apacheAvailableSitesDir}
COPY ./test_data/apache/example3-ssl.com.conf ${apacheAvailableSitesDir}
-RUN a2ensite example3.com.conf && a2ensite example3-ssl.com.conf && a2ensite example2.com.conf && a2ensite example-ssl.com.conf && a2dissite 000-default.conf
+COPY ./test_data/apache/example4-ssl.com.conf ${apacheAvailableSitesDir}
+COPY ./test_data/apache/example5.com.conf ${apacheAvailableSitesDir}
+RUN a2ensite example3.com.conf && a2ensite example3-ssl.com.conf && a2ensite example2.com.conf && a2ensite example-ssl.com.conf && a2ensite example4-ssl.com.conf && a2ensite example5.com.conf && a2dissite 000-default.conf
ENTRYPOINT ["/bin/sh", "./testcmd.sh"]
diff --git a/configurator.go b/configurator.go
index 2e02bb8..f255198 100644
--- a/configurator.go
+++ b/configurator.go
@@ -31,8 +31,8 @@ type ApacheConfigurator interface {
PrepareHTTPSModules(temp bool) error
EnableModule(module string, temp bool) error
EnsurePortIsListening(port string, https bool) error
- GetSuitableVhost(serverName string, createIfNoSsl bool) (*entity.VirtualHost, error)
- FindSuitableVhost(serverName string) (*entity.VirtualHost, error)
+ GetSuitableVhosts(serverName string, createIfNoSsl bool) ([]*entity.VirtualHost, error)
+ FindSuitableVhosts(serverName string) ([]*entity.VirtualHost, error)
CheckConfiguration() bool
RestartWebServer() error
SetLogger(logger logger.Logger)
@@ -41,15 +41,14 @@ type ApacheConfigurator interface {
}
type apacheConfigurator struct {
- parser *Parser
- reverter *Reverter
- ctl *apache.Ctl
- site *apache.Site
- logger logger.Logger
- version string
- vhosts []*entity.VirtualHost
- suitableVhosts map[string]*entity.VirtualHost
- options map[string]string
+ parser *Parser
+ reverter *Reverter
+ ctl *apache.Ctl
+ site *apache.Site
+ logger logger.Logger
+ version string
+ vhosts []*entity.VirtualHost
+ options map[string]string
}
type vhsotNames struct {
@@ -178,9 +177,9 @@ func (ac *apacheConfigurator) Rollback() error {
// DeployCertificate installs certificate to a domain
func (ac *apacheConfigurator) DeployCertificate(serverName, certPath, certKeyPath, chainPath, fullChainPath string) error {
var err error
- var vhost *entity.VirtualHost
+ var vhosts []*entity.VirtualHost
- if vhost, err = ac.GetSuitableVhost(serverName, true); err != nil {
+ if vhosts, err = ac.GetSuitableVhosts(serverName, true); err != nil {
return err
}
@@ -192,60 +191,63 @@ func (ac *apacheConfigurator) DeployCertificate(serverName, certPath, certKeyPat
return errors.New("could not find ssl_module")
}
- if err = ac.addDummySSLDirectives(vhost.AugPath); err != nil {
- return err
- }
-
- if err = ac.cleanSSLVhost(vhost); err != nil {
- return err
- }
-
- augCertPath, err := ac.parser.FindDirective("SSLCertificateFile", "", vhost.AugPath, true)
- if err != nil {
- return fmt.Errorf("error while searching directive 'SSLCertificateFile': %v", err)
- }
+ for _, vhost := range vhosts {
+ if err = ac.cleanSSLVhost(vhost); err != nil {
+ return err
+ }
- augCertKeyPath, err := ac.parser.FindDirective("SSLCertificateKeyFile", "", vhost.AugPath, true)
- if err != nil {
- return fmt.Errorf("error while searching directive 'SSLCertificateKeyFile': %v", err)
- }
+ if err = ac.addDummySSLDirectives(vhost.AugPath); err != nil {
+ return err
+ }
- res, err := utils.CheckMinVersion(ac.version, "2.4.8")
- if err != nil {
- return err
- }
+ augCertPath, err := ac.parser.FindDirective("SSLCertificateFile", "", vhost.AugPath, true)
+ if err != nil {
+ return fmt.Errorf("error while searching directive 'SSLCertificateFile': %v", err)
+ }
- if !res || (chainPath != "" && fullChainPath == "") {
- if err = ac.parser.Augeas.Set(augCertPath[len(augCertPath)-1], certPath); err != nil {
- return fmt.Errorf("could not set certificate path for vhost '%s': %v", serverName, err)
+ augCertKeyPath, err := ac.parser.FindDirective("SSLCertificateKeyFile", "", vhost.AugPath, true)
+ if err != nil {
+ return fmt.Errorf("error while searching directive 'SSLCertificateKeyFile': %v", err)
}
- if err = ac.parser.Augeas.Set(augCertKeyPath[len(augCertKeyPath)-1], certKeyPath); err != nil {
- return fmt.Errorf("could not set certificate key path for vhost '%s': %v", serverName, err)
+
+ res, err := utils.CheckMinVersion(ac.version, "2.4.8")
+ if err != nil {
+ return err
}
- if chainPath != "" {
- if err = ac.parser.AddDirective(vhost.AugPath, "SSLCertificateChainFile", []string{chainPath}); err != nil {
- return fmt.Errorf("could not add 'SSLCertificateChainFile' directive to vhost '%s': %v", serverName, err)
+ if !res || (chainPath != "" && fullChainPath == "") {
+ if err = ac.parser.Augeas.Set(augCertPath[len(augCertPath)-1], certPath); err != nil {
+ return fmt.Errorf("could not set certificate path for vhost '%s': %v", serverName, err)
+ }
+
+ if err = ac.parser.Augeas.Set(augCertKeyPath[len(augCertKeyPath)-1], certKeyPath); err != nil {
+ return fmt.Errorf("could not set certificate key path for vhost '%s': %v", serverName, err)
+ }
+
+ if chainPath != "" {
+ if err = ac.parser.AddDirective(vhost.AugPath, "SSLCertificateChainFile", []string{chainPath}); err != nil {
+ return fmt.Errorf("could not add 'SSLCertificateChainFile' directive to vhost '%s': %v", serverName, err)
+ }
+ } else {
+ return fmt.Errorf("SSL certificate chain path is required for the current Apache version '%s', but is not specified", ac.version)
}
} else {
- return fmt.Errorf("SSL certificate chain path is required for the current Apache version '%s', but is not specified", ac.version)
- }
- } else {
- if fullChainPath == "" {
- return errors.New("SSL certificate fullchain path is required, but is not specified")
- }
+ if fullChainPath == "" {
+ return errors.New("SSL certificate fullchain path is required, but is not specified")
+ }
- if err = ac.parser.Augeas.Set(augCertPath[len(augCertPath)-1], fullChainPath); err != nil {
- return fmt.Errorf("could not set certificate path for vhost '%s': %v", serverName, err)
- }
- if err = ac.parser.Augeas.Set(augCertKeyPath[len(augCertKeyPath)-1], certKeyPath); err != nil {
- return fmt.Errorf("could not set certificate key path for vhost '%s': %v", serverName, err)
+ if err = ac.parser.Augeas.Set(augCertPath[len(augCertPath)-1], fullChainPath); err != nil {
+ return fmt.Errorf("could not set certificate path for vhost '%s': %v", serverName, err)
+ }
+ if err = ac.parser.Augeas.Set(augCertKeyPath[len(augCertKeyPath)-1], certKeyPath); err != nil {
+ return fmt.Errorf("could not set certificate key path for vhost '%s': %v", serverName, err)
+ }
}
- }
- if !vhost.Enabled {
- if err = ac.EnableSite(vhost); err != nil {
- return err
+ if !vhost.Enabled {
+ if err = ac.EnableSite(vhost); err != nil {
+ return err
+ }
}
}
@@ -414,38 +416,13 @@ func (ac *apacheConfigurator) addDummySSLDirectives(vhPath string) error {
}
func (ac *apacheConfigurator) cleanSSLVhost(vhost *entity.VirtualHost) error {
- if err := ac.deduplicateDirectives(vhost.AugPath, []string{"SSLEngine", "SSLCertificateFile", "SSLCertificateKeyFile"}); err != nil {
- return err
- }
-
- if err := ac.removeDirectives(vhost.AugPath, []string{"SSLCertificateChainFile"}); err != nil {
+ if err := ac.removeDirectives(vhost.AugPath, []string{"SSLEngine", "SSLCertificateFile", "SSLCertificateKeyFile", "SSLCertificateChainFile"}); err != nil {
return err
}
return nil
}
-func (ac *apacheConfigurator) deduplicateDirectives(vhPath string, directives []string) error {
- for _, directive := range directives {
- directivePaths, err := ac.parser.FindDirective(directive, "", vhPath, false)
-
- if err != nil {
- return err
- }
-
- reg := regexp.MustCompile(`/\w*$`)
-
- if len(directivePaths) > 1 {
- dps := directivePaths[:len(directivePaths)-1]
- for _, dp := range dps {
- ac.parser.Augeas.Remove(reg.ReplaceAllString(dp, ""))
- }
- }
- }
-
- return nil
-}
-
func (ac *apacheConfigurator) removeDirectives(vhPath string, directives []string) error {
for _, directive := range directives {
directivePaths, err := ac.parser.FindDirective(directive, "", vhPath, false)
@@ -509,50 +486,36 @@ func (ac *apacheConfigurator) addListensForHTTPS(listens []string, listensOrigin
return nil
}
-// GetSuitableVhost returns suitable virtual hosts for provided serverName.
+// GetSuitableVhosts returns suitable virtual hosts for provided serverName.
// If createIfNoSsl is true then ssl part will be created if neccessary.
-func (ac *apacheConfigurator) GetSuitableVhost(serverName string, createIfNoSsl bool) (*entity.VirtualHost, error) {
- if vhost, ok := ac.suitableVhosts[serverName]; ok {
- return vhost, nil
- }
-
- vhost, err := ac.FindSuitableVhost(serverName)
-
+func (ac *apacheConfigurator) GetSuitableVhosts(serverName string, createIfNoSsl bool) ([]*entity.VirtualHost, error) {
+ var suitableVhosts []*entity.VirtualHost
+ suitableVhosts, err := ac.FindSuitableVhosts(serverName)
if err != nil {
return nil, err
}
- if vhost == nil {
- return nil, fmt.Errorf("could not find suitable virtual host with ServerName: %s", serverName)
+ if len(suitableVhosts) == 0 {
+ return nil, fmt.Errorf("could not find suitable virtual hosts with ServerName: %s", serverName)
}
if !createIfNoSsl {
- return vhost, nil
- }
-
- if !vhost.Ssl {
- serverName := vhost.ServerName
- vhost, err = ac.makeVhostSsl(vhost)
-
- if err != nil {
- return nil, fmt.Errorf("could not create ssl virtual host for '%s': %v", serverName, err)
- }
+ return suitableVhosts, nil
}
- ac.suitableVhosts[serverName] = vhost
-
- return vhost, nil
+ return ac.makeSslVhosts(suitableVhosts)
}
-// FindSuitableVhost tries to find a suitable virtual host for provided serverName.
-func (ac *apacheConfigurator) FindSuitableVhost(serverName string) (*entity.VirtualHost, error) {
+// FindSuitableVhosts tries to find a suitable virtual host for provided serverName.
+func (ac *apacheConfigurator) FindSuitableVhosts(serverName string) ([]*entity.VirtualHost, error) {
vhosts, err := ac.GetVhosts()
-
if err != nil {
return nil, err
}
- var suitableVhost *entity.VirtualHost
+ var suitableVhosts []*entity.VirtualHost
+ var suitableNonSslVhosts []*entity.VirtualHost
+ var sslVostsAddresses []string
for _, vhost := range vhosts {
if vhost.ModMacro {
@@ -561,79 +524,95 @@ func (ac *apacheConfigurator) FindSuitableVhost(serverName string) (*entity.Virt
}
// Prefer virtual host with ssl
- if vhost.ServerName == serverName && vhost.Ssl {
- return vhost, nil
- }
-
if vhost.ServerName == serverName {
- suitableVhost = vhost
+ if vhost.Ssl {
+ suitableVhosts = append(suitableVhosts, vhost)
+ sslVostsAddresses = append(sslVostsAddresses, vhost.GetAddressesString(true))
+ } else {
+ suitableNonSslVhosts = append(suitableNonSslVhosts, vhost)
+ }
}
}
- return suitableVhost, nil
-}
-
-// makeVhostSsl makes an ssl virtual host version of a nonssl virtual host
-func (ac *apacheConfigurator) makeVhostSsl(noSslVhost *entity.VirtualHost) (*entity.VirtualHost, error) {
- noSslFilePath := noSslVhost.FilePath
- sslFilePath, err := ac.getSslVhostFilePath(noSslFilePath)
-
- if err != nil {
- return nil, fmt.Errorf("could not get config file path for ssl virtual host: %v", err)
+ for _, vhost := range suitableNonSslVhosts {
+ // skip non ssl vhosts if there is already ssl vhost with the same address
+ if !com.IsSliceContainsStr(sslVostsAddresses, vhost.GetAddressesString(true)) {
+ suitableVhosts = append(suitableVhosts, vhost)
+ }
}
- originMatches, err := ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath)))
+ return suitableVhosts, nil
+}
- if err != nil {
- return nil, err
- }
+// makeSslVhosts makes an ssl virtual host version of a nonssl virtual host
+func (ac *apacheConfigurator) makeSslVhosts(vhosts []*entity.VirtualHost) ([]*entity.VirtualHost, error) {
+ var totalVhosts []*entity.VirtualHost
+ var newSslVhosts []*entity.VirtualHost
+ var newMatches []string
- err = ac.copyCreateSslVhostSkeleton(noSslVhost, sslFilePath)
+ for _, vhost := range vhosts {
+ if vhost.Ssl {
+ totalVhosts = append(totalVhosts, vhost)
+ continue
+ }
- if err != nil {
- return nil, fmt.Errorf("could not create config for ssl virtual host: %v", err)
- }
+ noSslFilePath := vhost.FilePath
+ sslFilePath, err := ac.getSslVhostFilePath(noSslFilePath)
- ac.parser.Augeas.Load()
- newMatches, err := ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath)))
+ if err != nil {
+ return nil, fmt.Errorf("could not get config file path for ssl virtual host: %v", err)
+ }
- if err != nil {
- return nil, err
- }
+ originMatches, err := ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath)))
+ if err != nil {
+ return nil, err
+ }
- sslVhostPath := getNewVhostPathFromAugesMatches(originMatches, newMatches)
+ if err = ac.copyCreateSslVhostSkeleton(vhost, sslFilePath); err != nil {
+ return nil, fmt.Errorf("could not create config for ssl virtual host: %v", err)
+ }
- if sslVhostPath == "" {
+ // Reload augeas to take into account the new vhost
+ ac.parser.Augeas.Load()
newMatches, err = ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath)))
if err != nil {
return nil, err
}
- sslVhostPath = getNewVhostPathFromAugesMatches(originMatches, newMatches)
-
+ sslVhostPath := getNewVhostPathFromAugesMatches(originMatches, newMatches)
if sslVhostPath == "" {
- return nil, errors.New("could not reverse map the HTTPS VirtualHost to the original")
- }
- }
+ newMatches, err = ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath)))
- ac.updateSslVhostAddresses(sslVhostPath)
- err = ac.Save()
+ if err != nil {
+ return nil, err
+ }
- if err != nil {
- return nil, err
- }
+ sslVhostPath = getNewVhostPathFromAugesMatches(originMatches, newMatches)
- sslVhost, err := ac.createVhost(sslVhostPath)
+ if sslVhostPath == "" {
+ return nil, errors.New("could not reverse map the HTTPS VirtualHost to the original")
+ }
+ }
- if err != nil {
- return nil, err
+ ac.updateSslVhostAddresses(sslVhostPath)
+ if err := ac.Save(); err != nil {
+ return nil, err
+ }
+
+ sslVhost, err := ac.createVhost(sslVhostPath)
+ if err != nil {
+ return nil, err
+ }
+
+ sslVhost.Ancestor = vhost
+ newSslVhosts = append(newSslVhosts, sslVhost)
}
- sslVhost.Ancestor = noSslVhost
- ac.vhosts = append(ac.vhosts, sslVhost)
+ updateVhostsAugPath(newSslVhosts, newMatches)
+ totalVhosts = append(totalVhosts, newSslVhosts...)
- return sslVhost, nil
+ return totalVhosts, nil
}
// CheckConfiguration checks if apache configuration is correct
@@ -864,7 +843,7 @@ func (ac *apacheConfigurator) createVhost(path string) (*entity.VirtualHost, err
var macro bool
- if strings.Index(strings.ToLower(path), "/macro/") != -1 {
+ if strings.Contains(strings.ToLower(path), "/macro/") {
macro = true
}
@@ -1007,14 +986,13 @@ func GetApacheConfigurator(options map[string]string) (ApacheConfigurator, error
}
configurator := apacheConfigurator{
- parser: parser,
- reverter: &Reverter{apacheSite: apache.GetApacheSite(options), logger: &log},
- ctl: ctl,
- site: &apache.Site{},
- logger: &log,
- options: options,
- version: version,
- suitableVhosts: make(map[string]*entity.VirtualHost),
+ parser: parser,
+ reverter: &Reverter{apacheSite: apache.GetApacheSite(options), logger: &log},
+ ctl: ctl,
+ site: &apache.Site{},
+ logger: &log,
+ options: options,
+ version: version,
}
return &configurator, nil
@@ -1184,3 +1162,15 @@ func getNewVhostPathFromAugesMatches(originMatches []string, newMatches []string
return ""
}
+
+func updateVhostsAugPath(vhosts []*entity.VirtualHost, newMatches []string) {
+ for _, newMatch := range newMatches {
+ mNewMatch := strings.Replace(newMatch, "[1]", "", -1)
+
+ for _, vhost := range vhosts {
+ if vhost.AugPath == mNewMatch {
+ vhost.AugPath = newMatch
+ }
+ }
+ }
+}
diff --git a/configurator_test.go b/configurator_test.go
index a4d9eea..414dc94 100644
--- a/configurator_test.go
+++ b/configurator_test.go
@@ -273,14 +273,14 @@ func TestGetVhosts(t *testing.T) {
func TestFindSuitableVhost(t *testing.T) {
configurator := getConfigurator(t)
- vhost := getVhost(t, configurator, "example2.com")
- assert.Equal(t, "example2.com", vhost.ServerName)
+ vhosts := getVhosts(t, configurator, "example2.com")
+ assert.Equal(t, "example2.com", vhosts[0].ServerName)
}
func TestGetVhostBlockContent(t *testing.T) {
configurator := getConfigurator(t)
- vhost := getVhost(t, configurator, "example2.com")
- content, err := configurator.getVhostBlockContent(vhost)
+ vhosts := getVhosts(t, configurator, "example2.com")
+ content, err := configurator.getVhostBlockContent(vhosts[0])
assert.Nilf(t, err, "could not get vhost block content: %v", err)
expectedContent := getVhostConfigContent(t, "example2.com.conf")
expectedContent = prepareStringToCompare(expectedContent)
@@ -326,7 +326,7 @@ func TestEnsurePortIsListening(t *testing.T) {
}
}
-func TestGetSuitableVhost(t *testing.T) {
+func TestGetSuitableVhostsSingle(t *testing.T) {
type vhostItem struct {
serverName, sslConfigFilePath, docRoot string
ssl, enabled bool
@@ -339,8 +339,10 @@ func TestGetSuitableVhost(t *testing.T) {
}
for _, vhostItem := range vhostItems {
- sslVhost, err := configurator.GetSuitableVhost(vhostItem.serverName, true)
+ sslVhosts, err := configurator.GetSuitableVhosts(vhostItem.serverName, true)
assert.Nilf(t, err, "could not get ssl vhost: %v", err)
+ assert.Equal(t, 1, len(sslVhosts))
+ sslVhost := sslVhosts[0]
assert.Equal(t, vhostItem.sslConfigFilePath, sslVhost.FilePath)
// Check that ssl config file realy exists
assert.Equal(t, true, com.IsFile(vhostItem.sslConfigFilePath))
@@ -361,6 +363,51 @@ func TestGetSuitableVhost(t *testing.T) {
}
}
+func TestGetSuitableVhostsMultiple(t *testing.T) {
+ configurator := getConfigurator(t)
+ sslVhosts, err := configurator.GetSuitableVhosts("example4.com", true)
+ assert.Nilf(t, err, "could not get ssl vhost: %v", err)
+ assert.Equal(t, 2, len(sslVhosts))
+ addresses := []string{"[2002:5bcc:18fd:c:10:52:43:96]", "10.52.43.96"}
+
+ for _, sslVhost := range sslVhosts {
+ assert.Equal(t, "/etc/apache2/sites-enabled/example4-ssl.com.conf", sslVhost.FilePath)
+ // Check that ssl config file realy exists
+ assert.Equal(t, true, com.IsFile("/etc/apache2/sites-enabled/example4-ssl.com.conf"))
+ assert.Equal(t, "example4.com", sslVhost.ServerName)
+ assert.Equal(t, "/var/www/html", sslVhost.DocRoot)
+ assert.Equal(t, true, sslVhost.Ssl)
+ assert.Equal(t, true, sslVhost.Enabled)
+ assert.Equal(t, false, sslVhost.ModMacro)
+ assert.Equal(t, 1, len(sslVhost.Addresses))
+ assert.Equal(t, true, com.IsSliceContainsStr(addresses, sslVhost.GetAddressesString(true)))
+ }
+}
+
+func TestDeployCertificate(t *testing.T) {
+ configurator := getConfigurator(t)
+ err := configurator.DeployCertificate("example5.com", "/opt/a2conf/test_data/apache/certificate/example.com.crt", "/opt/a2conf/test_data/apache/certificate/example.com.key", "", "/opt/a2conf/test_data/apache/certificate/example.com.crt")
+ assert.Nilf(t, err, "could not deploy certificate to vhost: %v", err)
+ err = configurator.Save()
+ assert.Nilf(t, err, "could not save changes after certificate deploy: %v", err)
+ assert.Equal(t, true, configurator.CheckConfiguration())
+ err = configurator.Commit()
+ assert.Nilf(t, err, "could not commit changes after certificate deploy: %v", err)
+ err = configurator.RestartWebServer()
+ assert.Nilf(t, err, "could not restart webserver after certificate deploy: %v", err)
+ // Check that ssl config file realy exists
+ sslConfigFilePath := "/etc/apache2/sites-enabled/example5.com-ssl.conf"
+ assert.Equal(t, true, com.IsFile(sslConfigFilePath))
+
+ sslConfigContent, err := ioutil.ReadFile(sslConfigFilePath)
+ assert.Nilf(t, err, "could not read apache vhost ssl config file '%s' content: %v", sslConfigFilePath, err)
+ directives := []string{"SSLCertificateKeyFile /opt/a2conf/test_data/apache/certificate/example.com.key", "SSLEngine on", "SSLCertificateFile /opt/a2conf/test_data/apache/certificate/example.com.crt"}
+
+ for _, directive := range directives {
+ assert.Containsf(t, string(sslConfigContent), directive, "ssl config does not contain directive '%s'", directive)
+ }
+}
+
func getVhostsJSON(t *testing.T) string {
vhostsPath := apacheDir + "/vhosts.json"
assert.FileExists(t, vhostsPath, "could not open vhosts file")
@@ -377,12 +424,12 @@ func getConfigurator(t *testing.T) *apacheConfigurator {
return configurator.(*apacheConfigurator)
}
-func getVhost(t *testing.T, configurator ApacheConfigurator, serverName string) *entity.VirtualHost {
- vhost, err := configurator.FindSuitableVhost(serverName)
+func getVhosts(t *testing.T, configurator ApacheConfigurator, serverName string) []*entity.VirtualHost {
+ vhosts, err := configurator.FindSuitableVhosts(serverName)
assert.Nilf(t, err, "could not find suitable vhost: %v", err)
- assert.NotNilf(t, vhost, "could not find suitable vhost for '%s' servername", serverName)
+ assert.NotEmptyf(t, vhosts, "could not find suitable vhost for '%s' servername", serverName)
- return vhost
+ return vhosts
}
func getVhostConfigContent(t *testing.T, name string) string {
diff --git a/entity/virtualhost.go b/entity/virtualhost.go
index ecb125e..dfb3291 100644
--- a/entity/virtualhost.go
+++ b/entity/virtualhost.go
@@ -3,6 +3,7 @@ package entity
import (
"path/filepath"
"regexp"
+ "strings"
)
const (
@@ -53,3 +54,17 @@ func (vh *VirtualHost) GetNames() ([]string, error) {
func (vh *VirtualHost) GetConfigName() string {
return filepath.Base(vh.FilePath)
}
+
+// GetAddressesString return address as a string: "172.10.52.2:80 172.10.52.3:8080"
+func (vh *VirtualHost) GetAddressesString(hostsOnly bool) string {
+ var addresses []string
+ for _, address := range vh.Addresses {
+ if hostsOnly {
+ addresses = append(addresses, address.Host)
+ } else {
+ addresses = append(addresses, address.ToString())
+ }
+ }
+
+ return strings.Join(addresses, " ")
+}
diff --git a/go.sum b/go.sum
index b0a75cc..36389c6 100644
--- a/go.sum
+++ b/go.sum
@@ -14,12 +14,12 @@ github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PX
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
-github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/parser.go b/parser.go
index 171ed80..14c1614 100644
--- a/parser.go
+++ b/parser.go
@@ -78,13 +78,11 @@ func GetParser(apachectl *apache.Ctl, version, serverRoot, vhostRoot string) (*P
if err = parser.setLocations(); err != nil {
parser.Close()
-
return nil, err
}
if err = parser.ParseFile(parser.ConfigRoot); err != nil {
parser.Close()
-
return nil, fmt.Errorf("could not parse apache config: %v", err)
}
diff --git a/test_data/apache/example4-ssl.com.conf b/test_data/apache/example4-ssl.com.conf
new file mode 100644
index 0000000..a41f702
--- /dev/null
+++ b/test_data/apache/example4-ssl.com.conf
@@ -0,0 +1,73 @@
+
+ ServerName example4.com
+ ServerAlias www.example4.com
+ DocumentRoot /var/www/html
+
+
+ Options Indexes FollowSymlinks
+ AllowOverride All
+ Require all granted
+
+
+ DirectoryIndex index.html
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+
+
+ ServerName example4.com
+ ServerAlias www.example4.com
+ DocumentRoot /var/www/html
+
+
+ Options Indexes FollowSymlinks
+ AllowOverride All
+ Require all granted
+
+
+ DirectoryIndex index.html
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+
+
+ ServerName example4.com
+ ServerAlias www.example4.com
+ DocumentRoot /var/www/html
+ SSLEngine on
+ SSLCertificateFile /opt/a2conf/test_data/apache/certificate/example.com.crt
+ SSLCertificateKeyFile /opt/a2conf/test_data/apache/certificate/example.com.key
+
+
+ Options Indexes FollowSymlinks
+ AllowOverride All
+ Require all granted
+
+
+ DirectoryIndex index.html
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+
+
+ ServerName example4.com
+ ServerAlias www.example4.com
+ DocumentRoot /var/www/html
+ SSLEngine on
+ SSLCertificateFile /opt/a2conf/test_data/apache/certificate/example.com.crt
+ SSLCertificateKeyFile /opt/a2conf/test_data/apache/certificate/example.com.key
+
+
+ Options Indexes FollowSymlinks
+ AllowOverride All
+ Require all granted
+
+
+ DirectoryIndex index.html
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
diff --git a/test_data/apache/example5.com.conf b/test_data/apache/example5.com.conf
new file mode 100644
index 0000000..8989d06
--- /dev/null
+++ b/test_data/apache/example5.com.conf
@@ -0,0 +1,16 @@
+
+ ServerName example5.com
+ ServerAlias www.example5.com
+ DocumentRoot /var/www/html
+
+
+ Options Indexes FollowSymlinks
+ AllowOverride All
+ Require all granted
+
+
+ DirectoryIndex index.html
+
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
diff --git a/test_data/apache/vhosts.json b/test_data/apache/vhosts.json
index e5827a0..4f764a9 100644
--- a/test_data/apache/vhosts.json
+++ b/test_data/apache/vhosts.json
@@ -1,4 +1,22 @@
[
+ {
+ "FilePath":"/etc/apache2/sites-enabled/example5.com.conf",
+ "ServerName":"example5.com",
+ "DocRoot":"/var/www/html",
+ "AugPath":"/files/etc/apache2/sites-enabled/example5.com.conf/VirtualHost",
+ "Addresses":{
+ "Kjo4MA==":{
+ "IsIpv6":false,
+ "Host":"*",
+ "Port": "80"
+ }
+ },
+ "Aliases":["www.example5.com"],
+ "Ssl":false,
+ "Enabled":true,
+ "ModMacro":false,
+ "Ancestor":null
+ },
{
"FilePath":"/etc/apache2/sites-enabled/example3.com.conf",
"ServerName":"example3.com",
@@ -17,6 +35,78 @@
"ModMacro":false,
"Ancestor":null
},
+ {
+ "FilePath":"/etc/apache2/sites-enabled/example4-ssl.com.conf",
+ "ServerName":"example4.com",
+ "DocRoot":"/var/www/html",
+ "AugPath":"/files/etc/apache2/sites-enabled/example4-ssl.com.conf/VirtualHost[1]",
+ "Addresses":{
+ "MTAuNTIuNDMuOTY6ODA=":{
+ "IsIpv6":false,
+ "Host":"10.52.43.96",
+ "Port": "80"
+ }
+ },
+ "Aliases":["www.example4.com"],
+ "Ssl":false,
+ "Enabled":true,
+ "ModMacro":false,
+ "Ancestor":null
+ },
+ {
+ "FilePath":"/etc/apache2/sites-enabled/example4-ssl.com.conf",
+ "ServerName":"example4.com",
+ "DocRoot":"/var/www/html",
+ "AugPath":"/files/etc/apache2/sites-enabled/example4-ssl.com.conf/VirtualHost[2]",
+ "Addresses":{
+ "WzIwMDI6NWJjYzoxOGZkOmM6MTA6NTI6NDM6OTZdOjgw":{
+ "IsIpv6":true,
+ "Host":"[2002:5bcc:18fd:c:10:52:43:96]",
+ "Port": "80"
+ }
+ },
+ "Aliases":["www.example4.com"],
+ "Ssl":false,
+ "Enabled":true,
+ "ModMacro":false,
+ "Ancestor":null
+ },
+ {
+ "FilePath":"/etc/apache2/sites-enabled/example4-ssl.com.conf",
+ "ServerName":"example4.com",
+ "DocRoot":"/var/www/html",
+ "AugPath":"/files/etc/apache2/sites-enabled/example4-ssl.com.conf/VirtualHost[3]",
+ "Addresses":{
+ "MTAuNTIuNDMuOTY6NDQz":{
+ "IsIpv6":false,
+ "Host":"10.52.43.96",
+ "Port": "443"
+ }
+ },
+ "Aliases":["www.example4.com"],
+ "Ssl":true,
+ "Enabled":true,
+ "ModMacro":false,
+ "Ancestor":null
+ },
+ {
+ "FilePath":"/etc/apache2/sites-enabled/example4-ssl.com.conf",
+ "ServerName":"example4.com",
+ "DocRoot":"/var/www/html",
+ "AugPath":"/files/etc/apache2/sites-enabled/example4-ssl.com.conf/VirtualHost[4]",
+ "Addresses":{
+ "WzIwMDI6NWJjYzoxOGZkOmM6MTA6NTI6NDM6OTZdOjQ0Mw==":{
+ "IsIpv6":true,
+ "Host":"[2002:5bcc:18fd:c:10:52:43:96]",
+ "Port": "443"
+ }
+ },
+ "Aliases":["www.example4.com"],
+ "Ssl":true,
+ "Enabled":true,
+ "ModMacro":false,
+ "Ancestor":null
+ },
{
"FilePath":"/etc/apache2/sites-enabled/example-ssl.com.conf",
"ServerName":"example.com",
diff --git a/utils/common_test.go b/utils/common_test.go
index 76cb7f1..7fe9123 100644
--- a/utils/common_test.go
+++ b/utils/common_test.go
@@ -35,3 +35,10 @@ func TestCheckMinVersion(t *testing.T) {
assert.Nilf(t, err, "failed to check min version %v:", err)
assert.Equal(t, false, result)
}
+
+func TestIsCommandExist(t *testing.T) {
+ result := IsCommandExist("a2ensite")
+ assert.Equal(t, true, result)
+ result = IsCommandExist("fakeCommand")
+ assert.Equal(t, false, result)
+}