github.com/aychain/blockbook@v0.1.1-0.20181121092459-6d1fc7e07c5b/build/tools/templates.go (about)

     1  package build
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"text/template"
    12  	"time"
    13  )
    14  
    15  type Config struct {
    16  	Meta struct {
    17  		BuildDatetime          string // generated field
    18  		PackageMaintainer      string `json:"package_maintainer"`
    19  		PackageMaintainerEmail string `json:"package_maintainer_email"`
    20  	}
    21  	Env struct {
    22  		Version              string `json:"version"`
    23  		BackendInstallPath   string `json:"backend_install_path"`
    24  		BackendDataPath      string `json:"backend_data_path"`
    25  		BlockbookInstallPath string `json:"blockbook_install_path"`
    26  		BlockbookDataPath    string `json:"blockbook_data_path"`
    27  	} `json:"env"`
    28  	Coin struct {
    29  		Name     string `json:"name"`
    30  		Shortcut string `json:"shortcut"`
    31  		Label    string `json:"label"`
    32  		Alias    string `json:"alias"`
    33  	} `json:"coin"`
    34  	Ports struct {
    35  		BackendRPC          int `json:"backend_rpc"`
    36  		BackendMessageQueue int `json:"backend_message_queue"`
    37  		BlockbookInternal   int `json:"blockbook_internal"`
    38  		BlockbookPublic     int `json:"blockbook_public"`
    39  	} `json:"ports"`
    40  	IPC struct {
    41  		RPCURLTemplate              string `json:"rpc_url_template"`
    42  		RPCUser                     string `json:"rpc_user"`
    43  		RPCPass                     string `json:"rpc_pass"`
    44  		RPCTimeout                  int    `json:"rpc_timeout"`
    45  		MessageQueueBindingTemplate string `json:"message_queue_binding_template"`
    46  	} `json:"ipc"`
    47  	Backend struct {
    48  		PackageName                     string      `json:"package_name"`
    49  		PackageRevision                 string      `json:"package_revision"`
    50  		SystemUser                      string      `json:"system_user"`
    51  		Version                         string      `json:"version"`
    52  		BinaryURL                       string      `json:"binary_url"`
    53  		VerificationType                string      `json:"verification_type"`
    54  		VerificationSource              string      `json:"verification_source"`
    55  		ExtractCommand                  string      `json:"extract_command"`
    56  		ExcludeFiles                    []string    `json:"exclude_files"`
    57  		ExecCommandTemplate             string      `json:"exec_command_template"`
    58  		LogrotateFilesTemplate          string      `json:"logrotate_files_template"`
    59  		PostinstScriptTemplate          string      `json:"postinst_script_template"`
    60  		ServiceType                     string      `json:"service_type"`
    61  		ServiceAdditionalParamsTemplate string      `json:"service_additional_params_template"`
    62  		ProtectMemory                   bool        `json:"protect_memory"`
    63  		Mainnet                         bool        `json:"mainnet"`
    64  		ServerConfigFile                string      `json:"server_config_file"`
    65  		ClientConfigFile                string      `json:"client_config_file"`
    66  		AdditionalParams                interface{} `json:"additional_params"`
    67  	} `json:"backend"`
    68  	Blockbook struct {
    69  		PackageName             string `json:"package_name"`
    70  		SystemUser              string `json:"system_user"`
    71  		InternalBindingTemplate string `json:"internal_binding_template"`
    72  		PublicBindingTemplate   string `json:"public_binding_template"`
    73  		ExplorerURL             string `json:"explorer_url"`
    74  		AdditionalParams        string `json:"additional_params"`
    75  		BlockChain              struct {
    76  			Parse                bool                       `json:"parse"`
    77  			Subversion           string                     `json:"subversion"`
    78  			AddressFormat        string                     `json:"address_format"`
    79  			MempoolWorkers       int                        `json:"mempool_workers"`
    80  			MempoolSubWorkers    int                        `json:"mempool_sub_workers"`
    81  			BlockAddressesToKeep int                        `json:"block_addresses_to_keep"`
    82  			AdditionalParams     map[string]json.RawMessage `json:"additional_params"`
    83  		} `json:"block_chain"`
    84  	} `json:"blockbook"`
    85  	IntegrationTests map[string][]string `json:"integration_tests"`
    86  }
    87  
    88  func jsonToString(msg json.RawMessage) (string, error) {
    89  	d, err := msg.MarshalJSON()
    90  	if err != nil {
    91  		return "", err
    92  	}
    93  	return string(d), nil
    94  }
    95  
    96  func generateRPCAuth(user, pass string) (string, error) {
    97  	cmd := exec.Command("/bin/bash", "-c", "build/scripts/rpcauth.py \"$0\" \"$1\" | sed -n -e 2p", user, pass)
    98  	var out bytes.Buffer
    99  	cmd.Stdout = &out
   100  	err := cmd.Run()
   101  	if err != nil {
   102  		return "", err
   103  	}
   104  	return out.String(), nil
   105  }
   106  
   107  func (c *Config) ParseTemplate() *template.Template {
   108  	templates := map[string]string{
   109  		"IPC.RPCURLTemplate":                      c.IPC.RPCURLTemplate,
   110  		"IPC.MessageQueueBindingTemplate":         c.IPC.MessageQueueBindingTemplate,
   111  		"Backend.ExecCommandTemplate":             c.Backend.ExecCommandTemplate,
   112  		"Backend.LogrotateFilesTemplate":          c.Backend.LogrotateFilesTemplate,
   113  		"Backend.PostinstScriptTemplate":          c.Backend.PostinstScriptTemplate,
   114  		"Backend.ServiceAdditionalParamsTemplate": c.Backend.ServiceAdditionalParamsTemplate,
   115  		"Blockbook.InternalBindingTemplate":       c.Blockbook.InternalBindingTemplate,
   116  		"Blockbook.PublicBindingTemplate":         c.Blockbook.PublicBindingTemplate,
   117  	}
   118  
   119  	funcMap := template.FuncMap{
   120  		"jsonToString":    jsonToString,
   121  		"generateRPCAuth": generateRPCAuth,
   122  	}
   123  
   124  	t := template.New("").Funcs(funcMap)
   125  
   126  	for name, def := range templates {
   127  		t = template.Must(t.Parse(fmt.Sprintf(`{{define "%s"}}%s{{end}}`, name, def)))
   128  	}
   129  
   130  	return t
   131  }
   132  
   133  func LoadConfig(configsDir, coin string) (*Config, error) {
   134  	config := new(Config)
   135  
   136  	f, err := os.Open(filepath.Join(configsDir, "coins", coin+".json"))
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	d := json.NewDecoder(f)
   141  	err = d.Decode(config)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	f, err = os.Open(filepath.Join(configsDir, "environ.json"))
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	d = json.NewDecoder(f)
   151  	err = d.Decode(&config.Env)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	config.Meta.BuildDatetime = time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700")
   157  
   158  	if !isEmpty(config, "backend") {
   159  		switch config.Backend.ServiceType {
   160  		case "forking":
   161  		case "simple":
   162  		default:
   163  			return nil, fmt.Errorf("Invalid service type: %s", config.Backend.ServiceType)
   164  		}
   165  
   166  		switch config.Backend.VerificationType {
   167  		case "":
   168  		case "gpg":
   169  		case "sha256":
   170  		case "gpg-sha256":
   171  		default:
   172  			return nil, fmt.Errorf("Invalid verification type: %s", config.Backend.VerificationType)
   173  		}
   174  	}
   175  
   176  	return config, nil
   177  }
   178  
   179  func isEmpty(config *Config, target string) bool {
   180  	switch target {
   181  	case "backend":
   182  		return config.Backend.PackageName == ""
   183  	case "blockbook":
   184  		return config.Blockbook.PackageName == ""
   185  	default:
   186  		panic("Invalid target name: " + target)
   187  	}
   188  }
   189  
   190  func GeneratePackageDefinitions(config *Config, templateDir, outputDir string) error {
   191  	templ := config.ParseTemplate()
   192  
   193  	err := makeOutputDir(outputDir)
   194  	if err != nil {
   195  		return err
   196  	}
   197  
   198  	for _, subdir := range []string{"backend", "blockbook"} {
   199  		if isEmpty(config, subdir) {
   200  			continue
   201  		}
   202  
   203  		root := filepath.Join(templateDir, subdir)
   204  
   205  		err = os.Mkdir(filepath.Join(outputDir, subdir), 0755)
   206  		if err != nil {
   207  			return err
   208  		}
   209  
   210  		err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
   211  			if err != nil {
   212  				return fmt.Errorf("%s: %s", path, err)
   213  			}
   214  
   215  			if path == root {
   216  				return nil
   217  			}
   218  			if filepath.Base(path)[0] == '.' {
   219  				return nil
   220  			}
   221  
   222  			subpath := path[len(root)-len(subdir):]
   223  
   224  			if info.IsDir() {
   225  				err = os.Mkdir(filepath.Join(outputDir, subpath), info.Mode())
   226  				if err != nil {
   227  					return fmt.Errorf("%s: %s", path, err)
   228  				}
   229  				return nil
   230  			}
   231  
   232  			t := template.Must(templ.Clone())
   233  			t = template.Must(t.ParseFiles(path))
   234  
   235  			err = writeTemplate(filepath.Join(outputDir, subpath), info, t, config)
   236  			if err != nil {
   237  				return fmt.Errorf("%s: %s", path, err)
   238  			}
   239  
   240  			return nil
   241  		})
   242  		if err != nil {
   243  			return err
   244  		}
   245  	}
   246  
   247  	if !isEmpty(config, "backend") {
   248  		err = writeBackendServerConfigFile(config, outputDir)
   249  		if err == nil {
   250  			err = writeBackendClientConfigFile(config, outputDir)
   251  		}
   252  		if err != nil {
   253  			return err
   254  		}
   255  	}
   256  
   257  	return nil
   258  }
   259  
   260  func makeOutputDir(path string) error {
   261  	err := os.RemoveAll(path)
   262  	if err == nil {
   263  		err = os.Mkdir(path, 0755)
   264  	}
   265  	return err
   266  }
   267  
   268  func writeTemplate(path string, info os.FileInfo, templ *template.Template, config *Config) error {
   269  	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, info.Mode())
   270  	if err != nil {
   271  		return err
   272  	}
   273  	defer f.Close()
   274  
   275  	err = templ.ExecuteTemplate(f, "main", config)
   276  	if err != nil {
   277  		return err
   278  	}
   279  
   280  	return nil
   281  }
   282  
   283  func writeBackendServerConfigFile(config *Config, outputDir string) error {
   284  	out, err := os.OpenFile(filepath.Join(outputDir, "backend/server.conf"), os.O_CREATE|os.O_WRONLY, 0644)
   285  	if err != nil {
   286  		return err
   287  	}
   288  	defer out.Close()
   289  
   290  	if config.Backend.ServerConfigFile == "" {
   291  		return nil
   292  	} else {
   293  		in, err := os.Open(filepath.Join(outputDir, "backend/config", config.Backend.ServerConfigFile))
   294  		if err != nil {
   295  			return err
   296  		}
   297  		defer in.Close()
   298  
   299  		_, err = io.Copy(out, in)
   300  		if err != nil {
   301  			return err
   302  		}
   303  	}
   304  
   305  	return nil
   306  }
   307  
   308  func writeBackendClientConfigFile(config *Config, outputDir string) error {
   309  	out, err := os.OpenFile(filepath.Join(outputDir, "backend/client.conf"), os.O_CREATE|os.O_WRONLY, 0644)
   310  	if err != nil {
   311  		return err
   312  	}
   313  	defer out.Close()
   314  
   315  	if config.Backend.ClientConfigFile == "" {
   316  		return nil
   317  	} else {
   318  		in, err := os.Open(filepath.Join(outputDir, "backend/config", config.Backend.ClientConfigFile))
   319  		if err != nil {
   320  			return err
   321  		}
   322  		defer in.Close()
   323  
   324  		_, err = io.Copy(out, in)
   325  		if err != nil {
   326  			return err
   327  		}
   328  	}
   329  
   330  	return nil
   331  }