github.com/drud/ddev@v1.21.5-alpha1.0.20230226034409-94fcc4b94453/pkg/ddevapp/providerAcquia_test.go (about)

     1  package ddevapp_test
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/drud/ddev/pkg/exec"
     6  	"github.com/drud/ddev/pkg/globalconfig"
     7  	"github.com/drud/ddev/pkg/nodeps"
     8  	"github.com/stretchr/testify/require"
     9  	"io"
    10  	"net/http"
    11  	"os"
    12  	"path"
    13  	"path/filepath"
    14  	"strings"
    15  	"testing"
    16  
    17  	. "github.com/drud/ddev/pkg/ddevapp"
    18  	"github.com/drud/ddev/pkg/testcommon"
    19  	asrt "github.com/stretchr/testify/assert"
    20  )
    21  
    22  /**
    23   * A valid site (with backups) must be present which matches the test site and environment name
    24   * defined in the constants below.
    25   */
    26  const acquiaPullTestSite = "ddevdemo.dev"
    27  const acquiaPullDatabase = "ddevdemo"
    28  const acquiaPushTestSite = "ddevdemo.test"
    29  
    30  const acquiaPullSiteURL = "http://ddevdemodev.prod.acquia-sites.com/"
    31  const acquiaSiteExpectation = "Super easy vegetarian pasta"
    32  
    33  // Note that these tests won't run with GitHub actions on a forked PR.
    34  // Thie is a security feature, but means that PRs intended to test this
    35  // must be done in the ddev repo.
    36  
    37  // TestAcquiaPull ensures we can pull backups from Acquia
    38  func TestAcquiaPull(t *testing.T) {
    39  	acquiaKey := ""
    40  	acquiaSecret := ""
    41  	sshkey := ""
    42  	if acquiaKey = os.Getenv("DDEV_ACQUIA_API_KEY"); acquiaKey == "" {
    43  		t.Skipf("No DDEV_ACQUIA_API_KEY env var has been set. Skipping %v", t.Name())
    44  	}
    45  	if acquiaSecret = os.Getenv("DDEV_ACQUIA_API_SECRET"); acquiaSecret == "" {
    46  		t.Skipf("No DDEV_ACQUIA_SECRET env var has been set. Skipping %v", t.Name())
    47  	}
    48  	if sshkey = os.Getenv("DDEV_ACQUIA_SSH_KEY"); sshkey == "" {
    49  		t.Skipf("No DDEV_ACQUIA_SSH_KEY env var has been set. Skipping %v", t.Name())
    50  	}
    51  	sshkey = strings.Replace(sshkey, "<SPLIT>", "\n", -1)
    52  
    53  	require.True(t, isPullSiteValid(acquiaPullSiteURL, acquiaSiteExpectation), "acquiaPullSiteURL %s isn't working right", acquiaPullSiteURL)
    54  	// Set up tests and give ourselves a working directory.
    55  	assert := asrt.New(t)
    56  	origDir, _ := os.Getwd()
    57  
    58  	webEnvSave := globalconfig.DdevGlobalConfig.WebEnvironment
    59  
    60  	globalconfig.DdevGlobalConfig.WebEnvironment = []string{"ACQUIA_API_KEY=" + acquiaKey, "ACQUIA_API_SECRET=" + acquiaSecret}
    61  	err := globalconfig.WriteGlobalConfig(globalconfig.DdevGlobalConfig)
    62  	assert.NoError(err)
    63  
    64  	siteDir := testcommon.CreateTmpDir(t.Name())
    65  	err = os.MkdirAll(filepath.Join(siteDir, "docroot/sites/default"), 0777)
    66  	assert.NoError(err)
    67  	err = os.Chdir(siteDir)
    68  	assert.NoError(err)
    69  
    70  	err = setupSSHKey(t, sshkey, filepath.Join(origDir, "testdata", t.Name()))
    71  	require.NoError(t, err)
    72  
    73  	app, err := NewApp(siteDir, true)
    74  	assert.NoError(err)
    75  	app.PHPVersion = "8.0"
    76  
    77  	t.Cleanup(func() {
    78  		err = app.Stop(true, false)
    79  		assert.NoError(err)
    80  
    81  		globalconfig.DdevGlobalConfig.WebEnvironment = webEnvSave
    82  		err = globalconfig.WriteGlobalConfig(globalconfig.DdevGlobalConfig)
    83  		assert.NoError(err)
    84  
    85  		_ = os.Chdir(origDir)
    86  		err = os.RemoveAll(siteDir)
    87  		assert.NoError(err)
    88  	})
    89  
    90  	app.Name = t.Name()
    91  	app.Type = nodeps.AppTypeDrupal9
    92  
    93  	_ = app.Stop(true, false)
    94  	err = app.WriteConfig()
    95  	assert.NoError(err)
    96  
    97  	testcommon.ClearDockerEnv()
    98  
    99  	err = PopulateExamplesCommandsHomeadditions(app.Name)
   100  	require.NoError(t, err)
   101  
   102  	err = app.Start()
   103  	require.NoError(t, err)
   104  
   105  	// Make sure we have drush
   106  	_, _, err = app.Exec(&ExecOpts{
   107  		Cmd: "composer require --no-interaction drush/drush symfony/http-kernel>/dev/null 2>/dev/null",
   108  	})
   109  	require.NoError(t, err)
   110  
   111  	// Build our acquia.yaml from the example file
   112  	s, err := os.ReadFile(app.GetConfigPath("providers/acquia.yaml.example"))
   113  	require.NoError(t, err)
   114  
   115  	// Replace the project_id and database_name
   116  	x := strings.Replace(string(s), "project_id:", fmt.Sprintf("project_id: %s\n#project_id:", acquiaPullTestSite), -1)
   117  	x = strings.Replace(x, "database_name: ", fmt.Sprintf("database_name: %s\n#database_name:", acquiaPullDatabase), -1)
   118  
   119  	err = os.WriteFile(app.GetConfigPath("providers/acquia.yaml"), []byte(x), 0666)
   120  	assert.NoError(err)
   121  	err = app.WriteConfig()
   122  	require.NoError(t, err)
   123  	err = app.MutagenSyncFlush()
   124  	require.NoError(t, err)
   125  
   126  	provider, err := app.GetProvider("acquia")
   127  	require.NoError(t, err)
   128  	err = app.Pull(provider, false, false, false)
   129  	require.NoError(t, err)
   130  
   131  	assert.FileExists(filepath.Join(app.GetHostUploadDirFullPath(), "chocolate-brownie-umami.jpg"))
   132  	out, err := exec.RunCommand("bash", []string{"-c", fmt.Sprintf(`echo 'select COUNT(*) from users_field_data where mail="randy@example.com";' | %s mysql -N`, DdevBin)})
   133  	assert.NoError(err)
   134  	assert.True(strings.HasPrefix(out, "1\n"))
   135  }
   136  
   137  // TestAcquiaPush ensures we can push to acquia for a configured environment.
   138  func TestAcquiaPush(t *testing.T) {
   139  	acquiaKey := ""
   140  	acquiaSecret := ""
   141  	sshkey := ""
   142  	if os.Getenv("DDEV_ALLOW_ACQUIA_PUSH") != "true" {
   143  		t.Skip("TestAcquiaPush is currently embargoed by DDEV_ALLOW_ACQUIA_PUSH not set to true")
   144  	}
   145  	if acquiaKey = os.Getenv("DDEV_ACQUIA_API_KEY"); acquiaKey == "" {
   146  		t.Skipf("No DDEV_ACQUIA_API_KEY env var has been set. Skipping %v", t.Name())
   147  	}
   148  	if acquiaSecret = os.Getenv("DDEV_ACQUIA_API_SECRET"); acquiaSecret == "" {
   149  		t.Skipf("No DDEV_ACQUIA_API_SECRET env var has been set. Skipping %v", t.Name())
   150  	}
   151  	if sshkey = os.Getenv("DDEV_ACQUIA_SSH_KEY"); sshkey == "" {
   152  		t.Skipf("No DDEV_ACQUIA_SSH_KEY env var has been set. Skipping %v", t.Name())
   153  	}
   154  	sshkey = strings.Replace(sshkey, "<SPLIT>", "\n", -1)
   155  
   156  	// Set up tests and give ourselves a working directory.
   157  	assert := asrt.New(t)
   158  	origDir, _ := os.Getwd()
   159  
   160  	webEnvSave := globalconfig.DdevGlobalConfig.WebEnvironment
   161  
   162  	globalconfig.DdevGlobalConfig.WebEnvironment = []string{"ACQUIA_API_KEY=" + acquiaKey, "ACQUIA_API_SECRET=" + acquiaSecret}
   163  	err := globalconfig.WriteGlobalConfig(globalconfig.DdevGlobalConfig)
   164  	assert.NoError(err)
   165  
   166  	// Use a D9 codebase for drush to work right
   167  	d9code := FullTestSites[8]
   168  	d9code.Name = t.Name()
   169  	err = globalconfig.RemoveProjectInfo(t.Name())
   170  	require.NoError(t, err)
   171  	err = d9code.Prepare()
   172  	require.NoError(t, err)
   173  	app, err := NewApp(d9code.Dir, false)
   174  	require.NoError(t, err)
   175  	_ = app.Stop(true, false)
   176  
   177  	err = os.Chdir(d9code.Dir)
   178  	require.NoError(t, err)
   179  
   180  	err = setupSSHKey(t, sshkey, filepath.Join(origDir, "testdata", t.Name()))
   181  	require.NoError(t, err)
   182  
   183  	t.Cleanup(func() {
   184  		err = app.Stop(true, false)
   185  		assert.NoError(err)
   186  
   187  		globalconfig.DdevGlobalConfig.WebEnvironment = webEnvSave
   188  		err = globalconfig.WriteGlobalConfig(globalconfig.DdevGlobalConfig)
   189  		assert.NoError(err)
   190  
   191  		_ = os.Chdir(origDir)
   192  		err = os.RemoveAll(d9code.Dir)
   193  		assert.NoError(err)
   194  	})
   195  
   196  	app.Hooks = map[string][]YAMLTask{"post-push": {{"exec-host": "touch hello-post-push-" + app.Name}}, "pre-push": {{"exec-host": "touch hello-pre-push-" + app.Name}}}
   197  	_ = app.Stop(true, false)
   198  
   199  	err = app.WriteConfig()
   200  	require.NoError(t, err)
   201  
   202  	testcommon.ClearDockerEnv()
   203  
   204  	err = PopulateExamplesCommandsHomeadditions(app.Name)
   205  	require.NoError(t, err)
   206  
   207  	// Create the uploaddir and a file; it won't have existed in our download
   208  	tval := nodeps.RandomString(10)
   209  	fName := tval + ".txt"
   210  	fContent := []byte(tval)
   211  	err = os.MkdirAll(filepath.Join(app.AppRoot, app.Docroot, "sites/default/files"), 0777)
   212  	require.NoError(t, err)
   213  	err = os.WriteFile(filepath.Join(app.AppRoot, app.Docroot, "sites/default/files", fName), fContent, 0644)
   214  	require.NoError(t, err)
   215  
   216  	err = app.Start()
   217  	require.NoError(t, err)
   218  
   219  	// Since allow-plugins isn't there and you can't even set it with composer...
   220  	_, _, err = app.Exec(&ExecOpts{
   221  		Cmd: `composer config --no-plugins allow-plugins true`,
   222  	})
   223  	require.NoError(t, err)
   224  	// Make sure we have drush
   225  	_, _, err = app.Exec(&ExecOpts{
   226  		Cmd: "composer require --no-interaction drush/drush >/dev/null 2>/dev/null",
   227  	})
   228  	require.NoError(t, err)
   229  
   230  	_, _, err = app.Exec(&ExecOpts{
   231  		Cmd: "time drush si -y minimal",
   232  	})
   233  	require.NoError(t, err)
   234  
   235  	// Create database and files entries that we can verify after push
   236  	writeQuery := fmt.Sprintf(`mysql -e 'CREATE TABLE IF NOT EXISTS %s ( title VARCHAR(255) NOT NULL ); INSERT INTO %s VALUES("%s");'`, t.Name(), t.Name(), tval)
   237  	_, _, err = app.Exec(&ExecOpts{
   238  		Cmd: writeQuery,
   239  	})
   240  	require.NoError(t, err)
   241  
   242  	// Make sure that the file we created exists in the container
   243  	_, _, err = app.Exec(&ExecOpts{
   244  		Cmd: fmt.Sprintf("ls %s", path.Join("/var/www/html", app.Docroot, "sites/default/files", fName)),
   245  	})
   246  	require.NoError(t, err)
   247  
   248  	// Build our PUSH acquia.yaml from the example file
   249  	s, err := os.ReadFile(app.GetConfigPath("providers/acquia.yaml.example"))
   250  	require.NoError(t, err)
   251  	x := strings.Replace(string(s), "project_id:", fmt.Sprintf("project_id: %s\n#project_id:", acquiaPushTestSite), -1)
   252  	err = os.WriteFile(app.GetConfigPath("providers/acquia.yaml"), []byte(x), 0666)
   253  	assert.NoError(err)
   254  	err = app.WriteConfig()
   255  	require.NoError(t, err)
   256  
   257  	provider, err := app.GetProvider("acquia")
   258  	require.NoError(t, err)
   259  
   260  	err = app.Push(provider, false, false)
   261  	require.NoError(t, err)
   262  
   263  	// Test that the database row was added
   264  	readQuery := fmt.Sprintf(`echo 'SELECT title FROM %s WHERE title="%s"' | drush @%s --alias-path=~/.drush sql-cli --extra=-N`, t.Name(), tval, acquiaPushTestSite)
   265  	out, _, err := app.Exec(&ExecOpts{
   266  		Cmd: readQuery,
   267  	})
   268  	require.NoError(t, err)
   269  	assert.Contains(out, tval)
   270  
   271  	// Test that the file arrived there (by rsyncing it back)
   272  	out, _, err = app.Exec(&ExecOpts{
   273  		Cmd: fmt.Sprintf("drush --alias-path=~/.drush rsync -y @%s:%%files/%s /tmp && cat /tmp/%s", acquiaPushTestSite, fName, fName),
   274  	})
   275  	require.NoError(t, err)
   276  	assert.Contains(out, tval)
   277  
   278  	err = app.MutagenSyncFlush()
   279  	assert.NoError(err)
   280  
   281  	assert.FileExists("hello-pre-push-" + app.Name)
   282  	assert.FileExists("hello-post-push-" + app.Name)
   283  	err = os.Remove("hello-pre-push-" + app.Name)
   284  	assert.NoError(err)
   285  	err = os.Remove("hello-post-push-" + app.Name)
   286  	assert.NoError(err)
   287  }
   288  
   289  // isPullSiteValid just checks to make sure the site we're testing against is OK
   290  func isPullSiteValid(siteURL string, siteExpectation string) bool {
   291  	resp, err := http.Get(siteURL)
   292  	if err != nil {
   293  		return false
   294  	}
   295  	//nolint: errcheck
   296  	defer resp.Body.Close()
   297  	if resp.StatusCode != 200 {
   298  		return false
   299  	}
   300  	body, err := io.ReadAll(resp.Body)
   301  	if err != nil {
   302  		return false
   303  	}
   304  	return strings.Contains(string(body), siteExpectation)
   305  }