├── .gitignore ├── License.md ├── Readme.md ├── cleanup-example └── cleanup-example.sh ├── helper-scripts └── vultr-helper.sh ├── marketplace-builder ├── Readme.md ├── debian10.pkr.hcl ├── debian10.sh ├── marketplace-builder.sh ├── ubuntu2004.pkr.hcl └── ubuntu2004.sh ├── packer-example ├── Readme.md ├── packer-example.pkr.hcl └── packer-example.sh ├── sample-app ├── Readme.md ├── sample-app.pkr.hcl ├── sample-app.sh ├── setup-per-boot.sh └── setup-per-instance.sh └── vultr-imageless ├── README.md ├── build.sh ├── image.yml ├── imageless_tools.sh ├── install.sh └── rootfs ├── etc └── myapp │ └── myconfig.conf ├── opt └── myapp │ ├── myapp │ └── run_myapp.sh ├── root └── app_binaries │ └── install.sh └── usr └── sbin └── a-command-binary /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 The Constant Company, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Vultr Marketplace Vendor Tools 2 | 3 | This repository has helper scripts and examples for Vultr Marketplace vendors. You'll also find the Marketplace Builder, an example automated build script. To learn more about the Vultr Marketplace, please see [our documentation library](https://www.vultr.com/docs/vultr-marketplace). 4 | 5 | ## Apply to Become a Vendor 6 | 7 | If you have an application that you'd like to publish in the Vultr Marketplace, [please apply to become a vendor](https://www.vultr.com/marketplace/become-a-verified-vendor/). 8 | 9 | ## Vultr Marketplace Process Overview 10 | 11 | This is a high-level overview of the Vultr Marketplace build process, with links to documentation. If you are new to the Vultr Marketplace, please start with the [Vultr Marketplace Introduction](https://www.vultr.com/docs/vultr-marketplace). 12 | 13 | 1. [Create a vendor account and update your contact information](https://www.vultr.com/docs/marketplace-vendor-settings). 14 | 1. [Create a new application profile](https://www.vultr.com/docs/marketplace-applications). 15 | 1. [Update the general information](https://www.vultr.com/docs/vultr-marketplace-general-information). 16 | 1. [Update the support information](https://www.vultr.com/docs/vultr-marketplace-support-information). 17 | 1. [Create application variables if you need auto-generated passwords](https://www.vultr.com/docs/vultr-marketplace-application-variables). 18 | 1. [Create your application instructions](https://www.vultr.com/docs/vultr-marketplace-application-instructions). See our [formatting tips](https://www.vultr.com/docs/vultr-marketplace-tips-for-readme-and-app-instructions). 19 | 1. [Upload your application screenshots and featured images](https://www.vultr.com/docs/vultr-marketplace-gallery). 20 | 1. [Verify your application has the required software installed](https://www.vultr.com/docs/vultr-marketplace-requirements). This GitHub repository has helper scripts and examples. 21 | 1. [Create your provisioning scripts](https://www.vultr.com/docs/vultr-marketplace-variables-and-provisioning-scripts). 22 | 1. [Create a snapshot of your application](https://www.vultr.com/docs/vultr-marketplace-snapshots). 23 | 1. [Assign your snapshot as a Vultr Marketplace build](https://www.vultr.com/docs/vultr-marketplace-builds). 24 | 1. [Publish your application in the Vultr Marketplace](https://www.vultr.com/docs/vultr-marketplace-publication-settings). 25 | 26 | ## Vultr Helper Script 27 | 28 | The scripts in this repository use helper functions in [vultr-helper.sh](/helper-scripts/vultr-helper.sh). You can adapt these as needed in your scripts. 29 | 30 | ## Marketplace Builder 31 | 32 | For quick prototyping or to learn how the Vultr Marketplace works, you can use the [Vultr Marketplace Builder](/marketplace-builder). 33 | 34 | ## Example Automation with Packer 35 | 36 | The [packer-example](/packer-example) directory is a template for creating an automated Vultr Marketplace build pipeline. 37 | 38 | ## Sample Application 39 | 40 | The [sample application](/sample-app) installs Nginx, MariaDB, and uses the Vultr application variable feature. It creates a database user and sets up basic authentication for the web server. We have a [complete walkthrough of this sample app in our documentation library](https://www.vultr.com/docs/how-to-build-an-example-vultr-marketplace-application). 41 | -------------------------------------------------------------------------------- /cleanup-example/cleanup-example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################ 3 | ## Prerequisites 4 | wget -O vultr-helper.sh https://raw.githubusercontent.com/vultr/vultr-marketplace/main/helper-scripts/vultr-helper.sh 5 | chmod +x vultr-helper.sh 6 | . vultr-helper.sh 7 | 8 | ################################################ 9 | ## Prepare server for Marketplace snapshot 10 | error_detect_on 11 | clean_system 12 | -------------------------------------------------------------------------------- /helper-scripts/vultr-helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################################### 4 | ## Vultr Marketplace Helper Functions 5 | 6 | inherit_errexit() 7 | { 8 | if ! shopt -sq inherit_errexit > /dev/null 2>&1; then 9 | echo "Unable to enable inherit_errexit" 10 | fi 11 | } 12 | 13 | error_detect_on() 14 | { 15 | set -euo pipefail 16 | inherit_errexit 17 | } 18 | 19 | error_detect_off() 20 | { 21 | set +euo pipefail 22 | inherit_errexit 23 | } 24 | 25 | enable_verbose_commands() 26 | { 27 | set -x 28 | } 29 | 30 | disable_verbose_commands() 31 | { 32 | set +x 33 | } 34 | 35 | get_metadata_item() 36 | { 37 | local item_value 38 | item_value="$(curl --fail --silent --header "Metadata-Token: vultr" "http://169.254.169.254/${1:-}")" 39 | 40 | echo "${item_value}" 41 | } 42 | 43 | get_hostname() 44 | { 45 | get_metadata_item "latest/meta-data/hostname" 46 | } 47 | 48 | get_userdata() 49 | { 50 | get_metadata_item "latest/user-data" 51 | } 52 | 53 | get_sshkeys() 54 | { 55 | get_metadata_item "current/ssh-keys" 56 | } 57 | 58 | # shellcheck disable=SC2034 59 | get_var() 60 | { 61 | local var_name="${1:-}" var_path="${2:-}" var_val 62 | var_val="$(get_metadata_item "${var_path:-"v1/internal/app-${var_name}"}" 2> /dev/null)" 63 | 64 | eval "${var_name}=\${var_val}" 65 | } 66 | 67 | # shellcheck disable=SC2034 68 | get_ip() 69 | { 70 | local ip_var="${1:-}" ip_val 71 | ip_val="$(get_var "${ip_var}" "latest/meta-data/public-ipv4")" 72 | 73 | eval "${ip_var}=\${ip_val}" 74 | } 75 | 76 | wait_on_apt_lock() 77 | { 78 | until ! lsof -t /var/cache/apt/archives/lock /var/lib/apt/lists/lock /var/lib/dpkg/lock > /dev/null 2>&1; do 79 | echo "Waiting 3 for apt lock currently held by another process." 80 | sleep 3 81 | done 82 | } 83 | 84 | apt_safe() 85 | { 86 | wait_on_apt_lock 87 | apt install -y "$@" 88 | } 89 | 90 | apt_update_safe() 91 | { 92 | wait_on_apt_lock 93 | apt update -y 94 | } 95 | 96 | apt_upgrade_safe() 97 | { 98 | wait_on_apt_lock 99 | DEBIAN_FRONTEND=noninteractive apt upgrade -y 100 | } 101 | 102 | apt_remove_safe() 103 | { 104 | wait_on_apt_lock 105 | apt remove -y --auto-remove "$@" 106 | } 107 | 108 | apt_clean_safe() 109 | { 110 | wait_on_apt_lock 111 | apt autoremove -y 112 | 113 | wait_on_apt_lock 114 | apt autoclean -y 115 | } 116 | 117 | update_and_clean_packages() 118 | { 119 | # RHEL/CentOS 120 | if [[ -f /etc/redhat-release ]]; then 121 | yum update -y 122 | yum clean all 123 | # Ubuntu / Debian 124 | elif grep -qs "debian" /etc/os-release 2> /dev/null; then 125 | apt_update_safe 126 | apt_upgrade_safe 127 | apt_clean_safe 128 | fi 129 | } 130 | 131 | set_vultr_kernel_option() 132 | { 133 | # RHEL/CentOS 134 | if [[ -f /etc/redhat-release ]]; then 135 | /sbin/grubby --update-kernel=ALL --args vultr 136 | # Ubuntu / Debian 137 | elif grep -qs "debian" /etc/os-release 2> /dev/null; then 138 | sed -i -e "/^GRUB_CMDLINE_LINUX_DEFAULT=/ s/\"$/ vultr\"/" /etc/default/grub 139 | update-grub 140 | fi 141 | } 142 | 143 | install_cloud_init() 144 | { 145 | local cloudinit_exe="" 146 | if cloudinit_exe="$(command -v cloud-init 2> /dev/null)" && [[ -x "${cloudinit_exe}" ]]; then 147 | echo "cloud-init is already installed." 148 | return 149 | fi 150 | 151 | local release_version="${1:-"latest"}" 152 | if [[ "${release_version}" != "latest" && "${release_version}" != "nightly" ]]; then 153 | echo "${release_version} is an invalid release option. Allowed: latest, nightly" 154 | exit 255 155 | fi 156 | 157 | # Lets remove all traces of previously installed cloud-init 158 | # Ubuntu installs have proven problematic with their left over 159 | # configs for the installer in recent versions 160 | cleanup_cloudinit 161 | 162 | update_and_clean_packages 163 | 164 | local build_type 165 | local package_ext 166 | 167 | [[ -e /etc/os-release ]] && . /etc/os-release 168 | case "${ID:-}" in 169 | debian) 170 | build_type="debian" 171 | package_ext="deb" 172 | ;; 173 | fedora) 174 | build_type="rhel" 175 | package_ext="rpm" 176 | ;; 177 | ubuntu) 178 | build_type="universal" 179 | package_ext="deb" 180 | ;; 181 | *) 182 | case "${ID_LIKE:-}" in 183 | *rhel*) 184 | build_type="rhel" 185 | package_ext="rpm" 186 | ;; 187 | *) 188 | echo "Unable to determine OS. Please install from source!" 189 | exit 255 190 | ;; 191 | esac 192 | ;; 193 | esac 194 | 195 | local cloud_init_package="cloud-init_${build_type}_${release_version}.${package_ext}" 196 | wget -O "/tmp/${cloud_init_package}" "https://ewr1.vultrobjects.com/cloud_init_beta/${cloud_init_package}" 197 | 198 | case "${package_ext}" in 199 | rpm) 200 | yum install -y "/tmp/${cloud_init_package}" 201 | ;; 202 | deb) 203 | apt_safe "/tmp/${cloud_init_package}" 204 | ;; 205 | *) 206 | echo "Unable to determine package installation method." 207 | exit 255 208 | ;; 209 | esac 210 | 211 | rm -f "/tmp/${cloud_init_package}" 212 | } 213 | 214 | cleanup_cloudinit() 215 | { 216 | rm -rf \ 217 | /etc/cloud \ 218 | /etc/systemd/system/cloud-init.target.wants/* \ 219 | /lib/systemd/system/cloud* \ 220 | /run/cloud-init \ 221 | /usr/bin/cloud* \ 222 | /usr/lib/cloud* \ 223 | /usr/local/bin/cloud* \ 224 | /usr/src/cloud* \ 225 | /var/log/cloud* 226 | } 227 | 228 | clean_tmp() 229 | { 230 | mkdir -p /tmp 231 | chmod 1777 /tmp 232 | rm -rf /tmp/* /var/tmp/* 233 | } 234 | 235 | clean_keys() 236 | { 237 | rm -f /root/.ssh/authorized_keys /etc/ssh/*key* 238 | touch /etc/ssh/revoked_keys 239 | chmod 600 /etc/ssh/revoked_keys 240 | } 241 | 242 | clean_logs() 243 | { 244 | find /var/log -mtime -1 -type f -exec truncate -s 0 {} \; 245 | rm -rf \ 246 | /var/log/*.[0-9] \ 247 | /var/log/*.gz \ 248 | /var/log/*.log \ 249 | /var/log/lastlog \ 250 | /var/log/wtmp 251 | 252 | : > /var/log/auth.log 253 | } 254 | 255 | clean_history() 256 | { 257 | history -c 258 | : > /root/.bash_history 259 | unset HISTFILE 260 | } 261 | 262 | clean_mloc() 263 | { 264 | /usr/bin/updatedb || true 265 | } 266 | 267 | clean_random() 268 | { 269 | rm -f /var/lib/systemd/random-seed 270 | } 271 | 272 | clean_machine_id() 273 | { 274 | [[ -e /etc/machine-id ]] && : > /etc/machine-id 275 | [[ -e /var/lib/dbus/machine-id ]] && : > /var/lib/dbus/machine-id 276 | } 277 | 278 | clean_free_space() 279 | { 280 | dd if=/dev/zero of=/zerofile || true 281 | sync 282 | rm -f /zerofile 283 | sync 284 | } 285 | 286 | trim_ssd() 287 | { 288 | fstrim / || true 289 | } 290 | 291 | cleanup_marketplace_scripts() 292 | { 293 | rm -f /root/*.sh 294 | } 295 | 296 | disable_network_manager() 297 | { 298 | ## Disable NetworkManager, replace with network-scripts 299 | systemctl disable --now NetworkManager 300 | sed -i \ 301 | -e 's/^ONBOOT.*/ONBOOT=yes/g' \ 302 | -e 's/^NM_CONTROLLED.*/NM_CONTROLLED=no/g' /etc/sysconfig/network-scripts/ifcfg-* 303 | yum install -y network-scripts 304 | } 305 | 306 | clean_system() 307 | { 308 | 309 | update_and_clean_packages 310 | set_vultr_kernel_option 311 | clean_tmp 312 | clean_keys 313 | clean_logs 314 | clean_history 315 | clean_random 316 | clean_machine_id 317 | clean_mloc 318 | clean_free_space 319 | trim_ssd 320 | 321 | cleanup_marketplace_scripts 322 | } 323 | -------------------------------------------------------------------------------- /marketplace-builder/Readme.md: -------------------------------------------------------------------------------- 1 | # Marketplace Builder 2 | 3 | For quick prototyping or to learn how the Vultr Marketplace works, you can use the [Vultr Marketplace Builder](/marketplace-builder). It creates Vultr Marketplace snapshots with the correct version of cloud-init installed. Marketplace Builder can create snapshots for: 4 | 5 | * CentOS 7 6 | * CentOS 8 7 | * Debian 10 8 | * Ubuntu 18.04 LTS 9 | * Ubuntu 20.04 LTS 10 | 11 | ## Requirements 12 | 13 | Marketplace Builder runs in `bash` or `zsh`. It has been tested on macOS, Ubuntu 20.04, and Windows using Ubuntu under WSL. 14 | 15 | 1. Install [HashiCorp Packer](https://learn.hashicorp.com/tutorials/packer/get-started-install-cli). 16 | 1. Verify your workstation's IP address is in your [Vultr API access control list](https://my.vultr.com/settings/#settingsapi). 17 | 1. Publish your API key. 18 | 19 | $ export VULTR_API_KEY= 20 | 21 | 1. If you desire a debug log, export these two variables: 22 | 23 | $ export PACKER_LOG=1 24 | $ export PACKER_LOG_PATH=packer.log 25 | 26 | ## Instructions 27 | 28 | 1. Clone this repository to your workstation. 29 | 30 | $ git clone https://github.com/vultr/vultr-marketplace.git 31 | 32 | 1. Run the Marketplace Builder. 33 | 34 | $ cd vultr-marketplace/marketplace-builder 35 | $ ./marketplace-builder.sh 36 | 37 | The Marketplace Builder prompts you to select one OS or build all snapshots in a batch. When finished, the script creates snapshots named `MKT- ` in your Vultr account, ready to be added to the Vultr Marketplace. 38 | 39 | -------------------------------------------------------------------------------- /marketplace-builder/debian10.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "vultr_api_key" { 2 | type = string 3 | default = "${env("VULTR_API_KEY")}" 4 | sensitive = true 5 | } 6 | 7 | packer { 8 | required_plugins { 9 | vultr = { 10 | version = ">=v2.3.2" 11 | source = "github.com/vultr/vultr" 12 | } 13 | } 14 | } 15 | 16 | source "vultr" "debian10" { 17 | api_key = "${var.vultr_api_key}" 18 | os_id = "352" 19 | plan_id = "vc2-1c-1gb" 20 | region_id = "ewr" 21 | snapshot_description = "MKT-Debian 10 ${formatdate("YYYY-MM-DD hh:mm", timestamp())}" 22 | ssh_username = "root" 23 | state_timeout = "25m" 24 | } 25 | 26 | build { 27 | sources = ["source.vultr.debian10"] 28 | 29 | provisioner "file" { 30 | source = "../helper-scripts/vultr-helper.sh" 31 | destination = "/root/vultr-helper.sh" 32 | } 33 | 34 | provisioner "shell" { 35 | script = "debian10.sh" 36 | remote_folder = "/root" 37 | remote_file = "debian10.sh" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /marketplace-builder/debian10.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################ 3 | ## Build example snapshot for Vultr Marketplace 4 | ## Tested on Debian 10 5 | ## 6 | ## Prerequisites 7 | chmod +x /root/vultr-helper.sh 8 | . /root/vultr-helper.sh 9 | error_detect_on 10 | install_cloud_init latest 11 | 12 | ################################################ 13 | ## Install your app here. 14 | 15 | ################################################ 16 | ## Prepare server snapshot for Marketplace 17 | clean_system 18 | -------------------------------------------------------------------------------- /marketplace-builder/marketplace-builder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "" 4 | echo "Checking dependencies." 5 | echo "" 6 | ################################# 7 | ## Test API access 8 | if [[ -z "${VULTR_API_KEY}" ]]; then 9 | echo "" 10 | echo "Please export your API key." 11 | echo "-------------------------------------------------------------------" 12 | echo "" 13 | echo "# export VULTR_API_KEY={your-Vultr-API-key}" 14 | exit 1 15 | fi 16 | echo "API key found." 17 | 18 | HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://api.vultr.com/v2/account" -X GET -H "Authorization: Bearer ${VULTR_API_KEY}") 19 | if [[ "${HTTP_CODE}" != "200" ]]; then 20 | echo "" 21 | echo "This workstation does not have API access and can not continue." 22 | echo "-------------------------------------------------------------------" 23 | echo "" 24 | echo "Please add IP address " $(host myip.opendns.com resolver1.opendns.com | grep "myip.opendns.com has" | awk '{print $NF}') 25 | echo "to the API Access Control list at:" 26 | echo "https://my.vultr.com/settings/#settingsapi" 27 | exit 1 28 | fi 29 | echo "Workstation has API access." 30 | 31 | ################################# 32 | ## Check for HashiCorp Packer 33 | packer --version &> /dev/null 34 | if [[ $? -ne 0 ]]; then 35 | echo "" 36 | echo "This workstation does not have HashiCorp Packer installed." 37 | echo "-------------------------------------------------------------------" 38 | echo "" 39 | echo "https://learn.hashicorp.com/tutorials/packer/get-started-install-cli" 40 | echo "Please install Packer and run this script again." 41 | exit 0 42 | fi 43 | echo "Packer is installed." 44 | 45 | ################################# 46 | ## Start building snapshots. 47 | echo "" 48 | echo "" 49 | echo "-----------------------------------------------------------------------" 50 | echo "Marketplace Builder creates working Vultr Marketplace snapshots. Use" 51 | echo "these examples as a starting point for your application." 52 | echo "-----------------------------------------------------------------------" 53 | echo "" 54 | echo "Create a snapshot for which OS?" 55 | echo "" 56 | echo "0: Create all" 57 | echo "1: Ubuntu 20.04" 58 | echo "2: Debian 10" 59 | echo "3: CentOS 7" 60 | echo "4: CentOS 8" 61 | echo "5: Ubuntu 18.04" 62 | echo "" 63 | 64 | read -r -p "Enter the OS number, or another key to quit: " OSVER 65 | 66 | VALID_INPUT='012345' 67 | if [[ ${VALID_INPUT} == *${OSVER}* ]]; then 68 | echo "Starting the Marketplace Builder." 69 | else 70 | echo "Exiting." 71 | exit 0 72 | fi 73 | 74 | ################################# 75 | ## Ubuntu 20.04 76 | if [[ "${OSVER}" == "1" || "${OSVER}" == "0" ]]; then 77 | export PACKER_LOG=1 78 | export PACKER_LOG_PATH=packer-Ubuntu2004.log 79 | packer init ubuntu2004.pkr.hcl 80 | packer build ubuntu2004.pkr.hcl 81 | fi 82 | 83 | ################################# 84 | ## Debian 10 85 | if [[ "${OSVER}" == "2" || "${OSVER}" == "0" ]]; then 86 | export PACKER_LOG=1 87 | export PACKER_LOG_PATH=packer-Debian10.log 88 | packer init debian10.pkr.hcl 89 | packer build debian10.pkr.hcl 90 | fi 91 | 92 | ################################# 93 | ## CentOS 7 94 | if [[ "${OSVER}" == "3" || "${OSVER}" == "0" ]]; then 95 | export PACKER_LOG=1 96 | export PACKER_LOG_PATH=packer-CentOS7.log 97 | packer init centos7.pkr.hcl 98 | packer build centos7.pkr.hcl 99 | fi 100 | 101 | ################################# 102 | ## CentOS 8 103 | if [[ "${OSVER}" == "4" || "${OSVER}" == "0" ]]; then 104 | export PACKER_LOG=1 105 | export PACKER_LOG_PATH=packer-CentOS8.log 106 | packer init centos8.pkr.hcl 107 | packer build centos8.pkr.hcl 108 | fi 109 | 110 | ################################# 111 | ## Ubuntu 18.04 112 | if [[ "${OSVER}" == "5" || "${OSVER}" == "0" ]]; then 113 | export PACKER_LOG=1 114 | export PACKER_LOG_PATH=packer-Ubuntu1804.log 115 | packer init ubuntu1804.pkr.hcl 116 | packer build ubuntu1804.pkr.hcl 117 | fi 118 | -------------------------------------------------------------------------------- /marketplace-builder/ubuntu2004.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "vultr_api_key" { 2 | type = string 3 | default = "${env("VULTR_API_KEY")}" 4 | sensitive = true 5 | } 6 | 7 | packer { 8 | required_plugins { 9 | vultr = { 10 | version = ">=v2.3.2" 11 | source = "github.com/vultr/vultr" 12 | } 13 | } 14 | } 15 | 16 | source "vultr" "ubuntu2004" { 17 | api_key = "${var.vultr_api_key}" 18 | os_id = "387" 19 | plan_id = "vc2-1c-1gb" 20 | region_id = "ewr" 21 | snapshot_description = "MKT-Ubuntu 20.04 LTS ${formatdate("YYYY-MM-DD hh:mm", timestamp())}" 22 | ssh_username = "root" 23 | state_timeout = "25m" 24 | } 25 | 26 | build { 27 | sources = ["source.vultr.ubuntu2004"] 28 | 29 | provisioner "file" { 30 | source = "../helper-scripts/vultr-helper.sh" 31 | destination = "/root/vultr-helper.sh" 32 | } 33 | 34 | provisioner "shell" { 35 | script = "ubuntu2004.sh" 36 | remote_folder = "/root" 37 | remote_file = "ubuntu2004.sh" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /marketplace-builder/ubuntu2004.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################ 3 | ## Build example snapshot for Vultr Marketplace 4 | ## Tested on Ubuntu 20.04 5 | ## 6 | ## Prerequisites 7 | chmod +x /root/vultr-helper.sh 8 | . /root/vultr-helper.sh 9 | error_detect_on 10 | install_cloud_init latest 11 | 12 | ################################################ 13 | ## Install your app here. 14 | 15 | ################################################ 16 | ## Prepare server snapshot for Marketplace 17 | clean_system 18 | -------------------------------------------------------------------------------- /packer-example/Readme.md: -------------------------------------------------------------------------------- 1 | # Example Automation with Packer 2 | 3 | This is a template for creating an automated Vultr Marketplace build pipeline. 4 | 5 | 1. Clone the repository to your workstation. 6 | 7 | $ git clone https://github.com/vultr/vultr-marketplace.git 8 | 9 | 1. Install [HashiCorp Packer](https://learn.hashicorp.com/tutorials/packer/get-started-install-cli). 10 | 1. Verify your workstation's IP address is in your [Vultr API access control list](https://my.vultr.com/settings/#settingsapi). 11 | 1. Publish your API key. 12 | 13 | $ export VULTR_API_KEY= 14 | 15 | 1. If you desire a debug log, export these two variables: 16 | 17 | $ export PACKER_LOG=1 18 | $ export PACKER_LOG_PATH=packer.log 19 | 20 | 1. Change to the Packer example directory. 21 | 22 | $ cd vultr-marketplace/packer-example 23 | 24 | 1. Use `packer init` to automatically download the Vultr Packer Plugin. 25 | 26 | $ packer init packer-example.pkr.hcl 27 | 28 | 1. Use `packer build` to automatically deploy a server, prepare it, make a snapshot, and then destroy the original server. 29 | 30 | $ packer build packer-example.pkr.hcl 31 | 32 | The result is an Ubuntu 20.04 LTS snapshot suitable for the Vultr Marketplace. -------------------------------------------------------------------------------- /packer-example/packer-example.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "vultr_api_key" { 2 | type = string 3 | default = "${env("VULTR_API_KEY")}" 4 | sensitive = true 5 | } 6 | 7 | packer { 8 | required_plugins { 9 | vultr = { 10 | version = ">=v2.3.2" 11 | source = "github.com/vultr/vultr" 12 | } 13 | } 14 | } 15 | 16 | source "vultr" "packer-example" { 17 | api_key = "${var.vultr_api_key}" 18 | os_id = "387" 19 | plan_id = "marketplace-2c-2gb" 20 | region_id = "ewr" 21 | snapshot_description = "Packer Example ${formatdate("YYYY-MM-DD hh:mm", timestamp())}" 22 | ssh_username = "root" 23 | state_timeout = "25m" 24 | } 25 | 26 | build { 27 | sources = ["source.vultr.packer-example"] 28 | 29 | provisioner "file" { 30 | source = "../helper-scripts/vultr-helper.sh" 31 | destination = "/root/vultr-helper.sh" 32 | } 33 | 34 | provisioner "shell" { 35 | script = "packer-example.sh" 36 | remote_folder = "/root" 37 | remote_file = "packer-example.sh" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packer-example/packer-example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################ 3 | ## Prerequisites 4 | chmod +x /root/vultr-helper.sh 5 | . /root/vultr-helper.sh 6 | error_detect_on 7 | install_cloud_init latest 8 | 9 | ################################################ 10 | ## Install your app here. 11 | 12 | 13 | ################################################ 14 | ## Prepare server snapshot for Marketplace 15 | clean_system 16 | -------------------------------------------------------------------------------- /sample-app/Readme.md: -------------------------------------------------------------------------------- 1 | # Sample Application 2 | 3 | The [sample application](/sample-app) installs Nginx, MariaDB, and uses the Vultr application variable feature. It creates a database user and sets up basic authentication for the web server. We have a [complete walkthrough of this sample app in our documentation library](https://www.vultr.com/docs/how-to-build-an-example-vultr-marketplace-application). 4 | -------------------------------------------------------------------------------- /sample-app/sample-app.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "vultr_api_key" { 2 | type = string 3 | default = "${env("VULTR_API_KEY")}" 4 | sensitive = true 5 | } 6 | 7 | packer { 8 | required_plugins { 9 | vultr = { 10 | version = ">=v2.3.2" 11 | source = "github.com/vultr/vultr" 12 | } 13 | } 14 | } 15 | 16 | source "vultr" "sample-app" { 17 | api_key = "${var.vultr_api_key}" 18 | os_id = "387" 19 | plan_id = "marketplace-2c-2gb" 20 | region_id = "ewr" 21 | snapshot_description = "Sample App ${formatdate("YYYY-MM-DD hh:mm", timestamp())}" 22 | ssh_username = "root" 23 | state_timeout = "25m" 24 | } 25 | 26 | build { 27 | sources = ["source.vultr.sample-app"] 28 | 29 | provisioner "file" { 30 | source = "../helper-scripts/vultr-helper.sh" 31 | destination = "/root/vultr-helper.sh" 32 | } 33 | 34 | provisioner "file" { 35 | source = "setup-per-boot.sh" 36 | destination = "/root/setup-per-boot.sh" 37 | } 38 | 39 | provisioner "file" { 40 | source = "setup-per-instance.sh" 41 | destination = "/root/setup-per-instance.sh" 42 | } 43 | 44 | provisioner "shell" { 45 | script = "sample-app.sh" 46 | remote_folder = "/root" 47 | remote_file = "sample-app.sh" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sample-app/sample-app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################ 3 | ## Prerequisites 4 | chmod +x /root/vultr-helper.sh 5 | . /root/vultr-helper.sh 6 | error_detect_on 7 | install_cloud_init latest 8 | 9 | ################################################ 10 | # Install Nginx, MariaDB, and the htpassword utility, which is a part of apache2-utils. 11 | # Use apt_safe() from vultr-helper.sh to avoid database locks. 12 | apt_safe nginx mariadb-server apache2-utils 13 | 14 | ################################################ 15 | # Add basic authentication to the default Nginx site. 16 | sed -i'' "/^\tlocation \/ {$/a \ \t\tauth_basic \"Restricted Content\";\n\t\tauth_basic_user_file /etc/nginx/.htpasswd;" /etc/nginx/sites-enabled/default 17 | 18 | ################################################ 19 | ## Install provisioning scripts 20 | mkdir -p /var/lib/cloud/scripts/per-boot/ 21 | mkdir -p /var/lib/cloud/scripts/per-instance/ 22 | 23 | mv /root/setup-per-boot.sh /var/lib/cloud/scripts/per-boot/setup-per-boot.sh 24 | mv /root/setup-per-instance.sh /var/lib/cloud/scripts/per-instance/setup-per-instance.sh 25 | 26 | chmod +x /var/lib/cloud/scripts/per-boot/setup-per-boot.sh 27 | chmod +x /var/lib/cloud/scripts/per-instance/setup-per-instance.sh 28 | 29 | ################################################ 30 | ## Prepare server for Marketplace snapshot 31 | 32 | clean_system 33 | -------------------------------------------------------------------------------- /sample-app/setup-per-boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## Run on every boot. 4 | echo $(date -u) ": System booted." >> /var/log/per-boot.log 5 | -------------------------------------------------------------------------------- /sample-app/setup-per-instance.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## Runs once-and-only-once at first boot per instance. 3 | 4 | ## Report the start time to a logfile. 5 | echo $(date -u) ": System provisioning started." >> /var/log/per-instance.log 6 | 7 | ## Capture the Marketplace Password Variables. 8 | DB_PASS=$(curl -H "METADATA-TOKEN: vultr" http://169.254.169.254/v1/internal/app-db_pass) 9 | WEB_PASS=$(curl -H "METADATA-TOKEN: vultr" http://169.254.169.254/v1/internal/app-web_pass) 10 | 11 | ## Create an example database. 12 | mysql -u root -e "CREATE DATABASE example_db;"; 13 | 14 | ## Create an example user and set the password to Marketplace Password Variable $DB_PASS. 15 | mysql -u root -e "GRANT ALL ON example_db.* TO 'example_user'@'localhost' IDENTIFIED BY '$DB_PASS' WITH GRANT OPTION;"; 16 | 17 | ## Create a username/password for Nginx with the Marketplace Password Variable $WEB_PASS. 18 | htpasswd -cb /etc/nginx/.htpasswd example_user $WEB_PASS 19 | 20 | ## Report the Web Password 21 | echo "The Web password is: $WEB_PASS" > /root/web-password.txt 22 | 23 | ## Report the DB Password 24 | echo "The DB password is: $DB_PASS" > /root/db-password.txt 25 | 26 | ## Report the end time to a logfile. 27 | echo $(date -u) ": System provisioning script is complete." >> /var/log/per-instance.log 28 | -------------------------------------------------------------------------------- /vultr-imageless/README.md: -------------------------------------------------------------------------------- 1 | # vultr-imageless 2 | An imageless app template for making imageless Marketplace app for Vultr. 3 | 4 | imageless_tools.sh currently contains only the tools to generate binaries but will include 5 | other functions to make creating images easier in the future as needed. 6 | 7 | The output sh file can be opened in a text editor and copied and pasted into the imageless 8 | text field on your Marketplace app's builds page. Remember to be careful and use appropriate 9 | text editors as carriage returns may cause issues on linux systems if you are building from 10 | Windows. 11 | -------------------------------------------------------------------------------- /vultr-imageless/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This builds and outputs the imageless string for this app 4 | # This will not produce an image but a sh file to use instead! 5 | 6 | . imageless_tools.sh 7 | 8 | set -eox pipefail 9 | 10 | mkdir dist 11 | 12 | # Generate an entire cloud-config formatted config 13 | # for Cloud-Init from the rootfs folder 14 | BINS="$(generate_binaries "./rootfs")" 15 | 16 | # Use a base template cloud-config format config 17 | cat image.yml > "./dist/myapp.sh" 18 | 19 | # Add the generated binaries from before 20 | echo "${BINS}" >> "./dist/myapp.sh" 21 | 22 | # Use this to seperate files and parts to avoid having 23 | # to use complicated multi-part formats 24 | echo -e '\n#!vultr-vendor-part' >> "./dist/myapp.sh" 25 | 26 | # Lets put a wrapper around the installer so we can have our own log 27 | # files. 28 | cat "./install.sh" >> "./dist/myapp.sh" 29 | 30 | # Lets echo it out for our CI/CD for reference 31 | cat "./dist/myapp.sh" 32 | -------------------------------------------------------------------------------- /vultr-imageless/image.yml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | users: 3 | - name: myapp 4 | 5 | -------------------------------------------------------------------------------- /vultr-imageless/imageless_tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function add_binaries() { 4 | DIR=$1 5 | BASEDIR=$2 6 | for f in ${DIR}/* 7 | do 8 | if [ -d ${f} ]; then 9 | add_binaries ${f} ${BASEDIR} 10 | continue 11 | fi 12 | 13 | LOCATION="/$(realpath --relative-to="${BASEDIR}" "${f}")" 14 | 15 | name=$(basename ${f}) 16 | b64="$(base64 -w 0 "${f}")" 17 | echo "- encoding: b64" 18 | echo " content: ${b64}" 19 | echo " owner: root:root" 20 | echo " path: ${LOCATION}" 21 | 22 | CONTENT="$(cat ${f})" 23 | PERMISSION="0644" 24 | if [[ "${CONTENT}" == "#!"* ]]; then 25 | PERMISSION="0744" 26 | fi 27 | echo " permissions: '${PERMISSION}'" 28 | done 29 | } 30 | 31 | function generate_binaries() { 32 | ROOTFS=$1 33 | 34 | echo "write_files:" 35 | add_binaries ${ROOTFS} ${ROOTFS} 36 | } -------------------------------------------------------------------------------- /vultr-imageless/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Lets execute the real install script in a way that creates 4 | # logs for the process so failures can be diagnosed 5 | /root/app_binaries/install.sh > /var/log/app_install.log 2>&1 6 | -------------------------------------------------------------------------------- /vultr-imageless/rootfs/etc/myapp/myconfig.conf: -------------------------------------------------------------------------------- 1 | Configs or anything can go here -------------------------------------------------------------------------------- /vultr-imageless/rootfs/opt/myapp/myapp: -------------------------------------------------------------------------------- 1 | You can do binaries, but you will need to mark them with chmod +x before they can be executed as the binary 2 | generator will only make files executable if they start with #! -------------------------------------------------------------------------------- /vultr-imageless/rootfs/opt/myapp/run_myapp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Any file starting with #! will be made executable by the binary generator" 4 | -------------------------------------------------------------------------------- /vultr-imageless/rootfs/root/app_binaries/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Make it so all commands are logged to the console 4 | # so they end up in the log file 5 | set -x 6 | 7 | # This is automatically included in every imageless install 8 | # as a vendor-script to make common procedures easier. It 9 | # will appear on all images, Vultr ones and Custom alike. 10 | # URL: https://raw.githubusercontent.com/vultr/vultr-marketplace/main/helper-scripts/vultr-helper.sh 11 | . /var/lib/vultr/vultr_app.sh 12 | 13 | 14 | 15 | echo "You can do anything you like in this bash script, or any other" 16 | echo "There is no limit to the file directory structure of rootfs either" 17 | -------------------------------------------------------------------------------- /vultr-imageless/rootfs/usr/sbin/a-command-binary: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "You can even place binaries directly into paths" 4 | --------------------------------------------------------------------------------