refactor: move app.json extraction up to core-post-extract

This commit is contained in:
Jose Diaz-Gonzalez
2022-12-29 01:24:40 -05:00
parent 173434ef34
commit 09eb0b2c8f
4 changed files with 118 additions and 32 deletions

View File

@@ -1,5 +1,9 @@
# 0.30.0 Migration Guide
## Changes
- The `app.json` file is now extracted from the source code instead of the built image. For deploys via `git:from-image`, the file is extracted from the built image.
## Removals
- Support for [SPDY](https://en.wikipedia.org/wiki/SPDY) has been removed. No major browser supports it as of 2021. Custom `nginx.conf.sigil` templates referencing spdy-related variables will continue to build until the 1.0.0 release.

View File

@@ -48,7 +48,7 @@ func GetAppjsonDirectory(appName string) string {
return common.GetAppDataDirectory("app-json", appName)
}
// GetAppjsonPath returns the path to a given app's extracted app.json file
// GetAppjsonPath returns the path to a given app's extracted app.json file for use by other plugins
func GetAppjsonPath(appName string) string {
return filepath.Join(GetAppjsonDirectory(appName), "app.json")
return getProcessSpecificAppJSONPath(appName)
}

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
@@ -80,11 +81,11 @@ func constructScript(command string, shell string, isHerokuishImage bool, isCnbI
}
func getAppJSON(appName string) (AppJSON, error) {
if !common.FileExists(GetAppjsonPath(appName)) {
if !hasAppJSON(appName) {
return AppJSON{}, nil
}
b, err := ioutil.ReadFile(GetAppjsonPath(appName))
b, err := ioutil.ReadFile(getProcessSpecificAppJSONPath(appName))
if err != nil {
return AppJSON{}, fmt.Errorf("Cannot read app.json file: %v", err)
}
@@ -101,6 +102,21 @@ func getAppJSON(appName string) (AppJSON, error) {
return appJSON, nil
}
func getAppJSONPath(appName string) string {
directory := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "app-json", appName)
return filepath.Join(directory, "app.json")
}
func getProcessSpecificAppJSONPath(appName string) string {
existingAppJSON := getAppJSONPath(appName)
processSpecificAppJSON := fmt.Sprintf("%s.%s", existingAppJSON, os.Getenv("DOKKU_PID"))
if common.FileExists(processSpecificAppJSON) {
return processSpecificAppJSON
}
return existingAppJSON
}
// getPhaseScript extracts app.json from app image and returns the appropriate json key/value
func getPhaseScript(appName string, phase string) (string, error) {
appJSON, err := getAppJSON(appName)
@@ -155,6 +171,19 @@ func getDokkuAppShell(appName string) string {
return shell
}
func hasAppJSON(appName string) bool {
appJSONPath := getAppJSONPath(appName)
if common.FileExists(fmt.Sprintf("%s.%s.missing", appJSONPath, os.Getenv("DOKKU_PID"))) {
return false
}
if common.FileExists(fmt.Sprintf("%s.%s", appJSONPath, os.Getenv("DOKKU_PID"))) {
return true
}
return common.FileExists(appJSONPath)
}
func cleanupDeploymentContainer(appName string, containerID string, phase string) error {
if phase != "predeploy" {
os.Setenv("DOKKU_SKIP_IMAGE_RETIRE", "true")
@@ -453,30 +482,6 @@ func createdContainerID(appName string, dockerArgs []string, image string, comma
return containerID, err
}
func refreshAppJSON(appName string, image string) error {
baseDirectory := common.GetDataDirectory("app-json")
if !common.DirectoryExists(baseDirectory) {
return errors.New("Run 'dokku plugin:install' to ensure the correct directories exist")
}
directory := common.GetAppDataDirectory("app-json", appName)
if !common.DirectoryExists(directory) {
if err := os.MkdirAll(directory, 0755); err != nil {
return err
}
}
appjsonPath := GetAppjsonPath(appName)
if common.FileExists(appjsonPath) {
if err := os.Remove(appjsonPath); err != nil {
return errors.New("Unable to remove previous app.json file")
}
}
common.CopyFromImage(appName, image, reportComputedAppjsonpath(appName), appjsonPath)
return nil
}
func setScale(appName string, image string) error {
appJSON, err := getAppJSON(appName)
if err != nil {

View File

@@ -3,8 +3,12 @@ package appjson
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/dokku/dokku/plugins/common"
"github.com/otiai10/copy"
)
// TriggerAppJSONProcessDeployParallelism returns the max number of processes to deploy in parallel
@@ -33,6 +37,83 @@ func TriggerAppJSONProcessDeployParallelism(appName string, processType string)
return nil
}
// TriggerCorePostDeploy sets a property to
// allow the app to be restored on boot
func TriggerCorePostDeploy(appName string) error {
existingAppJSON := getAppJSONPath(appName)
processSpecificAppJSON := fmt.Sprintf("%s.%s", existingAppJSON, os.Getenv("DOKKU_PID"))
if common.FileExists(processSpecificAppJSON) {
if err := os.Rename(processSpecificAppJSON, existingAppJSON); err != nil {
return err
}
} else if common.FileExists(fmt.Sprintf("%s.missing", processSpecificAppJSON)) {
if err := os.Remove(fmt.Sprintf("%s.missing", processSpecificAppJSON)); err != nil {
return err
}
if common.FileExists(existingAppJSON) {
if err := os.Remove(existingAppJSON); err != nil {
return err
}
}
}
return nil
}
// TriggerCorePostExtract ensures that the main app.json is the one specified by app-json-path
func TriggerCorePostExtract(appName string, sourceWorkDir string) error {
appJSONPath := strings.Trim(reportComputedAppjsonpath(appName), "/")
if appJSONPath == "" {
appJSONPath = "app.json"
}
existingAppJSON := getAppJSONPath(appName)
files, err := filepath.Glob(fmt.Sprintf("%s.*", existingAppJSON))
if err != nil {
return err
}
for _, f := range files {
if err := os.Remove(f); err != nil {
return err
}
}
processSpecificAppJSON := fmt.Sprintf("%s.%s", existingAppJSON, os.Getenv("DOKKU_PID"))
b, _ := common.PlugnTriggerOutput("git-get-property", []string{appName, "source-image"}...)
appSourceImage := strings.TrimSpace(string(b[:]))
repoDefaultAppJSONPath := path.Join(sourceWorkDir, "app.json")
if appSourceImage == "" {
repoAppJSONPath := path.Join(sourceWorkDir, appJSONPath)
if !common.FileExists(repoAppJSONPath) {
if appJSONPath != "app.json" && common.FileExists(repoDefaultAppJSONPath) {
if err := os.Remove(repoDefaultAppJSONPath); err != nil {
return err
}
}
return common.TouchFile(fmt.Sprintf("%s.missing", processSpecificAppJSON))
}
if err := copy.Copy(repoAppJSONPath, processSpecificAppJSON); err != nil {
return fmt.Errorf("Unable to extract app.json: %v", err.Error())
}
if appJSONPath != "app.json" {
if err := copy.Copy(repoAppJSONPath, repoDefaultAppJSONPath); err != nil {
return fmt.Errorf("Unable to move app.json into place: %v", err.Error())
}
}
} else {
if err := common.CopyFromImage(appName, appSourceImage, appJSONPath, processSpecificAppJSON); err != nil {
return common.TouchFile(fmt.Sprintf("%s.missing", processSpecificAppJSON))
}
}
// TODO: add validation to app.json file by ensuring it can be deserialized
return nil
}
// TriggerInstall initializes app-json directory structures
func TriggerInstall() error {
if err := common.PropertySetup("app-json"); err != nil {
@@ -104,10 +185,6 @@ func TriggerPostDeploy(appName string, imageTag string) error {
// TriggerPreDeploy is a trigger to execute predeploy and release deployment tasks
func TriggerPreDeploy(appName string, imageTag string) error {
image := common.GetAppImageName(appName, imageTag, "")
if err := refreshAppJSON(appName, image); err != nil {
return err
}
if err := executeScript(appName, image, imageTag, "predeploy"); err != nil {
return err
}