One place for hosting & domains

      May 2019

      How To Back Up Large Directories with Unison On Ubuntu 18.04


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Unison is an open-source file synchronization tool. It is very efficient at backing up large corpuses of data where only a few files have been added or updated. This situation occurs in, for example, a corporate Samba file server or an email server.

      The majority of the files in these servers will remain the same while a small number will be added or modified each day. Unison is able to discover and back up these new files extremely rapidly—even when there are millions of files and terabytes of data. In these situations, traditional tools like rsync can take a longer time to perform the same backup operation.

      In this tutorial, you will install and configure Unison on a pair of servers and use it to back up a directory. You will also configure Unison to use SSH as the secure communication protocol and create a cron job to periodically run Unison.

      Prerequisites

      Before you begin this guide, you’ll need the following:

      This guide will use two servers:

      • primary server: The server that hosts the data that you will back up.
      • backup server: The server that will host the backed up data.

      Step 1 — Creating Additional Non-Root Users

      The Initial Server Setup with Ubuntu 18.04 tutorial guided you through creating a non-root sudo user called sammy on both the primary and backup server. In this step, you will create two new users, one on the primary server and one on the backup server. This prevents confusion as you work through the guide, and an alternative non-root sudo user is required on the backup server if the SSH security configuration is enabled at the end of the guide.

      You will need to log in to both the primary and the backup server as the sammy user over SSH in two terminal windows. The following two SSH commands will log you into these servers:

      • ssh sammy@primary_server_ip
      • ssh sammy@backup_server_ip

      First, on the primary server create a new user called primary_user with this command:

      • sudo adduser primary_user

      Then give them sudo access rights:

      • sudo usermod -aG sudo primary_user

      Finally, change accounts to the primary_user:

      Next, follow the same steps on the backup server, but create a new user called backup_user. Make sure you are logged into the primary and backup servers as these users for the rest of the guide.

      Now that you have created the necessary users on both servers, you can move on to installing the Unison software.

      Step 2 — Installing Unison on Both Servers

      In this step, you will install the Unison package on both of the servers.

      You will use the Ubuntu package manager apt to install Unison on both servers. When using apt for the first time in a while, you should update the local package index with the following command:

      This ensures that you will install the latest version of Unison. This will also help to avoid installation errors.

      Next, install Unison:

      • sudo apt-get install unison

      You have now completed the installation of Unison. In the next step, you will configure SSH so that Unison is able to communicate between the two servers.

      Step 3 — Creating SSH Keys and Configuring SSH

      The first thing you will need to do is to create an SSH key pair on the primary server as you will use key-based authentication for the SSH connection. The advantage of key-based authentication is that a secure connection is possible without entering a password. This is important because you will create an automated backup procedure that must take place without you entering a password every time it occurs.

      Once you have that key pair on the primary server, you will copy the public key to the backup server and then test that Unison is able to communicate between the servers using SSH.

      Start by ensuring that the .ssh directory exists:

      Run the following command from the primary_user home directory on the primary server to generate a SSH key pair:

      • ssh-keygen -t rsa -b 4096 -f .ssh/unison-primary

      When you create an SSH key pair, you usually use a strong password. However, Unison will run automatically, so a password can't be manually entered each time it runs. Hit the ENTER key without entering a password. This will generate a passwordless SSH key pair.

      The options used here mean the following:

      • -t rsa: This sets the type of key that will be created. RSA keys are the most compatible type.
      • -b 4096: This sets the length of the key. The longer a key is, the more secure it is. A key length of 4096 is the current recommended key length for RSA keys.
      • -f .ssh/unison-primary: This sets the name of the key and the location where it will be saved. In this case, you will save the key into SSH's default directory, .ssh, using a name of your choice.

      The preceding command creates the public and private SSH keys in the following two files:

      • .ssh/unison-primary
      • .ssh/unison-primary.pub

      The first is the private SSH key and the second is the public key. You need to copy the contents of the public key file to the backup server. The easiest way to display the contents of the public key file for copying is to use the cat command to print the contents to the terminal:

      • cat .ssh/unison-primary.pub

      On the backup server in the backup_user home directory, open the .ssh/authorized_keys file with a text editor. Here, you will use nano:

      • nano .ssh/authorized_keys

      Paste the public key into the editor, then save and exit.

      You can now test that the SSH configuration is working by logging into the backup from the primary server via SSH. This is important because you will need to accept and save the SSH server's key fingerprint of the backup server or Unison will not work. In your terminal on the primary server, run the following command from the primary_user's home directory:

      • ssh -i .ssh/unison-primary backup_user@backup_server_ip

      The -i .ssh/unison-primary option instructs SSH to use a specific key or identity file. Here you will use the new unison-primary key you created.

      Accept the fingerprint by pressing Y and then ENTER, and log in and out. You just needed to confirm that SSH works between the servers and save the backup server's SSH fingerprint. The fingerprint can only be saved manually, so it has to be done before the process is automated later in the tutorial.

      Next, check that Unison will connect by running the following command from the primary_user home directory on the primary server:

      • ssh -i .ssh/unison-primary backup_user@backup_server_ip unison -version

      In this command, you used the same SSH command you used to test the connection with the addition of the unison command at the end. When a command is placed at the end of an SSH connection, SSH will log in, run the command, and then exit. The unison -version command instructs Unison to print its version number.

      If everything is working you will see a response showing the version of Unison on the backup server:

      Output

      unison version 2.48.3

      Now that you have confirmed that Unison can communicate between the servers using the SSH keys, you are ready to move on to configuring Unison.

      Step 4 — Configuring Unison

      In this step, you will configure Unison to run a simple one-way backup on a directory from the primary server to the backup server.

      To configure Unison, you first need to create the configuration directory under the primary_user's home directory on the primary server:

      Next, you need to open a new file with the name default.prf in a text editor in the .unison directory. This file contains the Unison configuration. Open the file with the following command:

      Then enter the following:

      .unison/default.prf

      force = /home/primary_user/data
      sshargs = -i /home/primary_user/.ssh/unison-primary
      

      The meaning of these lines is as follows

      • force: This ensures that changes are only pushed from the primary server to the backup server. The /home/primary_user/data path is the location of the directory that holds the data that you want to back up.
      • sshargs: This option instructs Unison to use the SSH key you generated.

      If the directory that holds the data that you want to back up is not under the primary_user home directory, then you must make sure that it is readable and writable by the primary_user. If you aren't familiar with Linux ownership and permissions, check out the Introduction to Linux Permissions guide to learn more.

      You've now configured Unison and can move on to testing it by backing up a directory.

      Step 5 — Backing Up a Directory With Unison

      You are ready to back up a directory now that Unison is configured. You will back up the /home/primary_user/data directory on the primary server to the /home/backup_user/data/ directory on the backup server. The directory that contains the data to back up must be the same directory that you put in the .unison/default.prf next to the force option.

      You will need some data to back up to test that Unison is working. Create some empty files on the primary server, and then check if Unison transferred them to the backup server.

      First, create the directory that will hold the data to back up by running the following command from the primary_user home directory:

      • mkdir /home/primary_user/data

      Next, use the touch command to create five empty files:

      • touch /home/primary_user/data/file{1..5}

      The final part of the command, file{1..5}, uses Bash brace expansion to create the five files. When bash is given {1..5}, it automatically fills in the missing numbers, 2, 3, and 4. This technique is useful to quickly enumerate multiple files.

      Now that you have the data directory and some test files to back up, you can run Unison to back up the files to the backup server. The following command will do this:

      • unison -batch -auto /home/primary_user/data ssh://backup_user@backup_server_ip//home/backup_user/data

      These options do the following:

      • batch - Run without asking any questions.
      • auto - Automatically accept any non-conflicting actions.

      As you are using Unison in a simple, one-way sync mode, you will not have to resolve any conflicts. This means that you can safely set these options.

      A conflict can occur only during Unison's other mode of operation, where it syncs in both directions. Such a use case would be syncing a directory on someone's laptop and desktop. When they update a file on the desktop, they want that change pushed to the laptop and vice versa. A conflict occurs if the same file is modified at both ends before a Unison sync occurs, and Unison cannot automatically decide which file to keep and which to overwrite.

      In a one-way push mode, the data on the primary is always retained and the data on the backup is overwritten.

      This command will print a long message the first time that it is run. The message reads as follows:

      Output

      Contacting server... Connected [//primary_server_ip//home/primary_user/data -> //primary_server_ip//home/backup_user/data] Looking for changes Warning: No archive files were found for these roots, whose canonical names are: /home/primary_user/data //backup_server_ip//home/backup_user/data This can happen either because this is the first time you have synchronized these roots, or because you have upgraded Unison to a new version with a different archive format. Update detection may take a while on this run if the replicas are large. Unison will assume that the 'last synchronized state' of both replicas was completely empty. This means that any files that are different will be reported as conflicts, and any files that exist only on one replica will be judged as new and propagated to the other replica. If the two replicas are identical, then no changes will be reported. If you see this message repeatedly, it may be because one of your machines is getting its address from DHCP, which is causing its host name to change between synchronizations. See the documentation for the UNISONLOCALHOSTNAME environment variable for advice on how to correct this. Donations to the Unison project are gratefully accepted: http://www.cis.upenn.edu/~bcpierce/unison Waiting for changes from server Reconciling changes dir ----> / Propagating updates UNISON 2.48.3 started propagating changes at 16:30:43.70 on 03 Apr 2019 [BGN] Copying from /home/primary_user/data to //backup_server_ip//home/backup_user/data [END] Copying UNISON 2.48.3 finished propagating changes at 16:30:43.71 on 03 Apr 2019 Saving synchronizer state Synchronization complete at 16:30:43 (1 item transferred, 0 skipped, 0 failed)

      This information is warning that this is the first synchronization. It also provides tips on how to resolve an issue if you see this message for every synchronization run. The last section tells you what data Unison synced during this run.

      On each subsequent run, it will print much less information. Here is the output when no files have been updated:

      Output

      Contacting server... Connected [//primary_server_ip//home/primary_user/data -> //backup_server_ip//home/backup_user/data] Looking for changes Waiting for changes from server Reconciling changes Nothing to do: replicas have not changed since last sync.

      This is the output when /data/file1 is modified on the primary server:

      Output

      Contacting server... Connected [//primary_server_ip//home/primary_user/data -> //backup_server_ip//home/backup_user/data] Looking for changes Waiting for changes from server Reconciling changes changed ----> file1 Propagating updates UNISON 2.48.3 started propagating changes at 16:38:37.11 on 03 Apr 2019 [BGN] Updating file file1 from /home/primary_user/data to //backup_server_ip//home/backup_user/data [END] Updating file file1 UNISON 2.48.3 finished propagating changes at 16:38:37.16 on 03 Apr 2019 Saving synchronizer state Synchronization complete at 16:38:37 (1 item transferred, 0 skipped, 0 failed)

      After each synchronization run, the backup server will an exact copy of the data directory on the primary server.

      Warning: Any new files or changes in the data directory on the backup server will get lost when you run Unison.

      You are now able to run Unison to back up a directory. In the next step, you will automate the backup process by running Unison with cron.

      Step 6 — Creating a Unison Cron Job

      In this section, you will create a cron job that will run Unison and back up the data directory to the backup server at a specified frequency.

      The crontab is a file that is read by the cron process. The commands it contains are loaded into the cron process and are executed at the specified intervals.

      You can view the contents of the crontab for your current user by running the following command:

      The -l option lists the contents of the current user's crontab. If you have not edited the crontab for this user before, you will see the following error message because no crontab file exists yet:

      Output

      no crontab for primary_user

      Next, run the crontab command on the primary server with the -e flag to open it in edit mode:

      If you don't have a default command line editor configured, you will be asked to select an editor the first time you run the command. Select the editor of your choice to open the crontab.

      Once you have the crontab open, add the following command to the first empty line under the existing text:

      ...
      * */3 * * * /usr/bin/unison -log -logfile /var/log/unison.log -auto -batch -silent /home/primary_user/data ssh://backup_user@backup_server_ip//home/backup_user/data
      

      The command you will use is almost the same as the one you used in Step 5 for the manual backup, but with some additional options. These additional options are as follows:

      • -silent: Disables all output except errors. Normal output is not required when Unison is executed from the crontab as there is no one to read it.
      • -log: Instructs Unison to log its actions.
      • -logfile: Specifies where Unison will log its actions.
        In this example, Unison is run every 3 hours. You can change this to any frequency that better meets your requirements.

      Whenever you edit the crontab, you must always put an empty line at the bottom before you save and exit or cron may not load the crontab file correctly. This could cause the commands to not be executed.

      Once you've made these changes, save and close the file.

      Next, create the log file that Unison will write to on the primary server. The following command will create this file:

      • sudo touch /var/log/unison.log

      Next, make the primary_user the owner of the file.

      • sudo chown primary_user /var/log/unison.log

      You can check the status of the Unison backups by reading the log file at /var/log/unison.log. Unison will only log something when it has either backed up a new or updated file or if it encountered an error.

      Unison is now backing up periodically from the crontab. The last and optional step is to make the SSH configuration more secure.

      Step 7 (Optional) — Securing SSH

      In this guide, you have created and used an SSH key that does not have a password. This is a security concern that you can address by limiting what the backup_user is able to do when they log in via SSH to the backup server.

      You will do this by configuring SSH to only allow the backup_user to execute a single command when logged in over SSH. This means that the SSH key that you created can only be used to execute the Unison backups and nothing else. This has the consequence that you will not be able to SSH into the backup server as the backup_user. This is because logging in requires more than the single permitted command.

      If you need to access the backup server as the backup_user you should log in as the sammy user first, and then change to the backup_user using su - backup_user.

      Edit the SSH configuration file on the backup server at /etc/ssh/sshd_config:

      • sudo nano /etc/ssh/sshd_config

      Then add the following lines to the bottom of the file:

      /etc/ssh/sshd_config

      Match User backup_user
        ForceCommand unison -server
      

      These configuration options mean the following:

      • Match User: When the listed user logs in, SSH will apply the following and indented configuration option.
      • ForceCommand: This restricts the matched user to the following command. In this case, the backup_user can only run the unison -server command.

      Save and exit your text editor. Next, reload the SSH service to enable the new configuration:

      • sudo systemctl reload ssh.service

      You can test this by trying to log in to the backup server as the backup_user over SSH from the primary server.

      $ ssh -i .ssh/unison-primary backup_user@backcup_server_ip
      

      If the /etc/ssh/sshd_config settings are working, you will see the following:

      Output

      Unison 2.48

      The SSH session will hang until the session is killed with with CTRL + C because Unison is waiting for a command.

      This shows that the Unison server was invoked automatically on log in and no other access is possible outside of communicating with the Unison server.

      You now have a working and secure Unison backup system that will back up your data as often as you want it to.

      Conclusion

      In this guide, you installed and configured the Unison file synchronization software to back up a directory over SSH. You also configured cron to automatically run backups at a specified schedule and secured SSH so the passwordless key cannot be abused.

      When determining if you should use Unison, there are a few things that you should consider:

      • Unison may not be the best choice when you have smaller numbers of files or lower amounts of data. In this case, rsync would be a more appropriate choice. You can read more about using rsync in the How To Use Rsync to Sync Local and Remote Directories on a VPS guide.
      • Backing up large amounts of data can take a long time and may use up your bandwidth allocation over public network interfaces. If your primary and backup servers are both DigitalOcean Droplets, then you will be able to complete the Unison backup much more rapidly and securely if you use a private network. For more information on the free DigitalOcean private networks, please see the Private Network Overview documentation.



      Source link

      How To Use Variables and Constants in Go


      Variables are an important programming concept to master. They are symbols that stand in for a value you’re using in a program.

      This tutorial will cover some variable basics and best practices for using them within the Go programs you create.

      Understanding Variables

      In technical terms, a variable is assigning a storage location to a value that is tied to a symbolic name or identifier. We use the variable name to reference that stored value within a computer program.

      We can think of a variable as a label that has a name on it, which you tie onto a value.

      Variables in Go

      Let’s say we have an integer, 1032049348, and we want to store it in a variable rather than continuously retype the long number over and over again. To achieve this, we can use a name that’s easy to remember, like the variable i. To store a value in a variable, we use the following syntax:

      i := 1032049348
      

      We can think of this variable like a label that is tied to the value.

      Go Variable Example

      The label has the variable name i written on it, and is tied to the integer value 1032049348.

      The phrase i := 1032049348 is a declaration and assignment statement that consists of a few parts:

      • the variable name (i)
      • the short variable declaration assignment (:=)
      • the value that is being tied to the variable name (1032049348)
      • the data type inferred by Go (int)

      We’ll see later how to explicitly set the type in the next section.

      Together, these parts make up the statement that sets the variable i equal to the value of the integer 1032049348.

      As soon as we set a variable equal to a value, we initialize or create that variable. Once we have done that, we are ready to use the variable instead of the value.

      Once we’ve set i equal to the value of 1032049348, we can use i in the place of the integer, so let’s print it out:

      package main
      
      import "fmt"
      
      func main() {
          i := 1032049348
          fmt.Println(i)
      }
      

      Output

      1032049348

      We can also quickly and easily do math by using variables. With i := 1032049348, we can subtract the integer value 813 with the following syntax:

      fmt.Println(i - 813)
      

      Output

      1032048535

      In this example, Go does the math for us, subtracting 813 from the variable i to return the sum 1032048535.

      Speaking of math, variables can be set equal to the result of a math equation. You can also add two numbers together and store the value of the sum into the variable x:

      x := 76 + 145
      

      You may have noticed that this example looks similar to algebra. In the same way that we use letters and other symbols to represent numbers and quantities within formulas and equations, variables are symbolic names that represent the value of a data type. For correct Go syntax, you’ll need to make sure that your variable is on the left side of any equations.

      Let’s go ahead and print x:

      package main
      
      import "fmt"
      
      func main() {
          x := 76 + 145
          fmt.Println(x)
      }
      

      Output

      221

      Go returned the value 221 because the variable x was set equal to the sum of 76 and 145.

      Variables can represent any data type, not just integers:

      s := "Hello, World!"
      f := 45.06
      b := 5 > 9 // A Boolean value will return either true or false
      array := [4]string{"item_1", "item_2", "item_3", "item_4"}
      slice := []string{"one", "two", "three"}
      m := map[string]string{"letter": "g", "number": "seven", "symbol": "&"}
      

      If you print any of these variables, Go will return what that variable is equivalent to. Let’s work with the assignment statement for the string slice data type:

      package main
      
      import "fmt"
      
      func main() {
          slice := []string{"one", "two", "three"}
          fmt.Println(slice)
      }
      

      Output

      [one two three]

      We assigned the slice value of []string{"one", "two", "three"} to the variable slice, and then used the fmt.Println function to print out that value by calling slice.

      Variables work by carving out a little area of memory within your computer that accepts specified values that are then associated with that space.

      Declaring Variables

      In Go, there are several ways to declare a variable, and in some cases, more than one way to declare the exact same variable and value.

      We can declare a variable called i of data type int without initialization. This means we will declare a space to put a value, but not give it an initial value:

      var i int
      

      This creates a variable declared as i of data type int.

      We can initialize the value by using the equal (=) operator, like in the following example:

      var i int = 1
      

      In Go, both of these forms of declaration are called long variable declarations.

      We can also use short variable declaration:

      i := 1
      

      In this case, we have a variable called i, and a data type of int. When we don’t specify a data type, Go will infer the data type.

      With the three ways to declare variables, the Go community has adopted the following idioms:

      • Only use long form, var i int, when you’re not initializing the variable.

      • Use short form, i := 1, when declaring and initializing.

      • If you did not desire Go to infer your data type, but you still want to use short variable declaration, you can wrap your value in your desired type, with the following syntax:

      i := int64(1)
      

      It’s not considered idiomatic in Go to use the long variable declaration form when we’re initializing the value:

      var i int = 1
      

      It’s good practice to follow how the Go community typically declares variables so that others can seamlessly read your programs.

      Zero Values

      All built-in types have a zero value. Any allocated variable is usable even if it never has a value assigned. We can see the zero values for the following types:

      package main
      
      import "fmt"
      
      func main() {
          var a int
          var b string
          var c float64
          var d bool
      
          fmt.Printf("var a %T = %+vn", a, a)
          fmt.Printf("var b %T = %qn", b, b)
          fmt.Printf("var c %T = %+vn", c, c)
          fmt.Printf("var d %T = %+vnn", d, d)
      }
      

      Output

      var a int = 0 var b string = "" var c float64 = 0 var d bool = false

      We used the %T verb in the fmt.Printf statement. This tells the function to print the data type for the variable.

      In Go, because all values have a zero value, we can’t have undefined values like some other languages. For instance, a boolean in some languages could be undefined, true, or false, which allows for three states to the variable. In Go, we can’t have more than two states for a boolean value.

      Naming Variables: Rules and Style

      The naming of variables is quite flexible, but there are some rules to keep in mind:

      • Variable names must only be one word (as in no spaces).
      • Variable names must be made up of only letters, numbers, and underscores (_).
      • Variable names cannot begin with a number.

      Following these rules, let’s look at both valid and invalid variable names:

      Valid Invalid Why Invalid
      userName user-name Hyphens are not permitted
      name4 4name Cannot begin with a number
      user $user Cannot use symbols
      userName user name Cannot be more than one word

      Furthermore, keep in mind when naming variables that they are case sensitive. These names userName, USERNAME, UserName, and uSERnAME are all completely different variables. It’s best practice to avoid using similar variable names within a program to ensure that both you and your collaborators—current and future—can keep your variables straight.

      While variables are case sensitive, the case of the first letter of a variable has special meaning in Go. If a variable starts with an uppercase letter, then that variable is accessible outside the package it was declared in (or exported). If a variable starts with a lowercase letter, then it is only available within the package it is declared in.

      var Email string
      var password string
      

      Email starts with an uppercase letter and can be accessed by other packages. password starts with a lowercase letter, and is only accessible inside the package it is declared in.

      It is common in Go to use very terse (or short) variable names. Given the choice between using userName and user for a variable, it would be idiomatic to choose user.

      Scope also plays a role in the terseness of the variable name. The rule is that the smaller the scope the variable exists in, the smaller the variable name:

      names := []string{"Mary", "John", "Bob", "Anna"}
      for i, n := range names {
          fmt.Printf("index: %d = %qn", i, n)
      }
      

      We use the variable names in a larger scope, so it would be common to give it a more meaningful name to help remember what it means in the program. However, we use the i and n variables immediately in the next line of code, and then do not use them again.. Because of this, it won’t confuse someone reading the code about where the variables are used, or what they mean.

      Next, let’s cover some notes about variable style. The style is to use MixedCaps or mixedCaps rather than underscores for multi-word names.

      Conventional Style Unconventional Style Why Unconventional
      userName user_name Underscores are not conventional
      i index prefer i over index as it is shorter
      serveHTTP serveHttp acronyms should be capitalized

      The most important thing about style is to be consistent, and that the team you work on agrees to the style.

      Reassigning Variables

      As the word “variable” implies, we can change Go variables readily. This means that we can connect a different value with a previously assigned variable through reassignment. Being able to reassign is useful because throughout the course of a program we may need to accept user-generated values into already initialized variables. We may also need to change the assignment to something previously defined.

      Knowing that we can readily reassign a variable can be useful when working on a large program that someone else wrote, and it isn’t clear what variables are already defined.

      Let’s assign the value of 76 to a variable called i of type int, then assign it a new value of 42:

      package main
      
      import "fmt"
      
      func main() {
          i := 76
          fmt.Println(i)
      
          i = 42
          fmt.Println(i)
      }
      

      Output

      76 42

      This example shows that we can first assign the variable i with the value of an integer, and then reassign the variable i assigning it this time with the value of 42.

      Note: When you declare and initialize a variable, you can use :=, however, when you want to simply change the value of an already declared variable, you only need to use the equal operator (=).

      Because Go is a typed language, we can’t assign one type to another. For instance, we can’t assign the value "Sammy" to a variable of type int:

      i := 72
      i = "Sammy"
      

      Trying to assign different types to each other will result in a compile-time error:

      Output

      cannot use "Sammy" (type string) as type int in assignment

      Go will not allow us to use a variable name more than once:

      var s string
      var s string
      

      Output

      s redeclared in this block

      If we try to use short variable declaration more than once for the same variable name we’ll also receive a compilation error. This can happen by mistake, so understanding what the error message means is helpful:

      i := 5
      i := 10
      

      Output

      no new variables on left side of :=

      Similarly to variable declaration, giving consideration to the naming of your variables will improve the readability of your program for you, and others, when you revisit it in the future.

      Multiple Assignment

      Go also allows us to assign several values to several variables within the same line. Each of these values can be of a different data type:

      j, k, l := "shark", 2.05, 15
      fmt.Println(j)
      fmt.Println(k)
      fmt.Println(l)
      

      Output

      shark 2.05 15

      In this example, the variable j was assigned to the string "shark", the variable k was assigned to the float 2.05, and the variable l was assigned to the integer 15.

      This approach to assigning multiple variables to multiple values in one line can keep the number of lines in your code down. However, it’s important to not compromise readability for fewer lines of code.

      Global and Local Variables

      When using variables within a program, it is important to keep variable scope in mind. A variable’s scope refers to the particular places it is accessible from within the code of a given program. This is to say that not all variables are accessible from all parts of a given program—some variables will be global and some will be local.

      Global variables exist outside of functions. Local variables exist within functions.

      Let’s take a look at global and local variables in action:

      package main
      
      import "fmt"
      
      
      var g = "global"
      
      func printLocal() {
          l := "local"
          fmt.Println(l)
      }
      
      func main() {
          printLocal()
          fmt.Println(g)
      }
      

      Output

      local global

      Here we use var g = "global" to create a global variable outside of the function. Then we define the function printLocal(). Inside of the function a local variable called l is assigned and then printed out. The program ends by calling printLocal() and then printing the global variable g.

      Because g is a global variable, we can refer to it in printLocal(). Let’s modify the previous program to do that:

      package main
      
      import "fmt"
      
      
      var g = "global"
      
      func printLocal() {
          l := "local"
          fmt.Println(l)
          fmt.Println(g)
      }
      
      func main() {
          printLocal()
          fmt.Println(g)
      }
      

      Output

      local global global

      We start by declaring a global variable g, var g = "global". In the main function, we call the function printLocal, which declares a local variable l and prints it out, fmt.Println(l). Then, printLocal prints out the global variable g, fmt.Println(g). Even though g wasn’t defined within printLocal, it could still be accessed because it was declared in a global scope. Finally, the main function prints out g as well.

      Now let’s try to call the local variable outside of the function:

      package main
      
      import "fmt"
      
      var g = "global"
      
      func printLocal() {
          l := "local"
          fmt.Println(l)
      }
      
      func main() {
          fmt.Println(l)
      }
      
      

      Output

      undefined: l

      We can’t use a local variable outside of the function it is assigned in. If you try to do so, you’ll receive a undefined error when you compile.

      Let’s look at another example where we use the same variable name for a global variable and a local variable:

      package main
      
      import "fmt"
      
      var num1 = 5
      
      func printNumbers() {
          num1 := 10
          num2 := 7  
      
          fmt.Println(num1)
          fmt.Println(num2)
      }
      
      func main() {
          printNumbers()
          fmt.Println(num1)
      }
      

      Output

      10 7 5

      In this program, we declared the num1 variable twice. First, we declared num1 at the global scope, var num1 = 5, and again within the local scope of the printNumbers function, num1 := 10. When we print num1 from the main program, we see the value of 5 printed out. This is because main only sees the global variable declaration. However, when we print out num1 from the printNumbers function, it sees the local declaration, and will print out the value of 10. Even though printNumbers creates a new variable called num1 and assigned it a value of 10, it does not affect the global instance of num1 with the value of 5.

      When working with variables, you also need to consider what parts of your program will need access to each variables; adopting a global or local variable accordingly. Across Go programs, you’ll find that local variables are typically more common.

      Constants

      Constants are like variables, except they can’t be modified once they have been declared. Constants are useful for defining a value that will be used more than once in your program, but shouldn’t be able to change.

      For instance, if we wanted to declare the tax rate for a shopping cart system, we could use a constant and then calculate tax in different areas of our program. At some point in the future, if the tax rate changes, we only have to change that value in one spot in our program. If we used a variable, it is possible that we might accidentally change the value somewhere in our program, which would result in an improper calculation.

      To declare a constant, we can use the following syntax:

      const shark = "Sammy"
      fmt.Println(shark)
      

      Output

      Sammy

      If we try to modify a constant after it was declared, we’ll get a compile-time error:

      Output

      cannot assign to shark

      Constants can be untyped. This can be useful when working with numbers such as integer-type data. If the constant is untyped, it is explicitly converted, where typed constants are not. Let’s see how we can use constants:

      package main
      
      import "fmt"
      
      const (
          year     = 365
          leapYear = int32(366)
      )
      
      func main() {
          hours := 24
          minutes := int32(60)
          fmt.Println(hours * year)    
          fmt.Println(minutes * year)   
          fmt.Println(minutes * leapYear)
      }
      

      Output

      8760 21900 21960

      If you declare a constant with a type, it will be that exact type. Here when we declare the constant leapYear, we define it as data type int32. Therefore it is a typed constant, which means it can only operate with int32 data types. The year constant we declare with no type, so it is considered untyped. Because of this, you can use it with any integer data type.

      When hours was defined, it inferred that it was of type int because we did not explicitly give it a type, hours := 24. When we declared minutes, we explicitly declared it as an int32, minutes := int32(60).

      Now let’s walk through each calculation and why it works:

      hours * year
      

      In this case, hours is an int, and years is untyped. When the program compiles, it explicitly converts years to an int, which allows the multiplication operation to succeed.

      minutes * year
      

      In this case, minutes is an int32, and year is untyped. When the program compiles, it explicitly converts years to an int32, which allows the multiplication operation to succeed.

      minutes * leapYear
      

      In this case, minutes is an int32, and leapYear is a typed constant of int32. There is nothing for the compiler to do this time as both variables are already of the same type.

      If we try to multiply two types that are typed and not compatible, the program will not compile:

      fmt.Println(hours * leapYear)
      

      Output

      invalid operation: hours * leapYear (mismatched types int and int32)

      In this case, hours was inferred as an int, and leapYear was explicitly declared as an int32. Because Go is a typed language, an int and an int32 are not compatible for mathematical operations. To multiply them, you would need to convert one to a int32 or an int.

      Conclusion

      In this tutorial we reviewed some of the common use cases of variables within Go. Variables are an important building block of programming, serving as symbols that stand in for the value of a data type we use in a program.



      Source link

      How To Install Web Apps Using Cloudron on Ubuntu 18.04


      The author selected the Wikimedia Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      Cloudron is a platform that streamlines the installation and management of apps on your server, like WordPress, Rocket.Chat, Nextcloud, GitLab, OpenVPN, and more. A key benefit of using Cloudron is that it performs end-to-end deployment of apps. It automates tasks such as: provisioning databases, DNS setup, certificate management, centralized user management, and automatic backups.

      Apps on Cloudron also receive automatic updates. The Cloudron team tracks upstream app releases and publishes updates for apps. The user experience of installing and updating apps on your server is very similar to how apps are installed and updated on your smartphone: just click to install and start using them without system administration.

      In this tutorial, you’ll install Cloudron on your server then deploy a WordPress site, as an example, to experience how Cloudron performs complete deployment automation. Finally, to ready your Cloudron instance for production use, you will set up Cloudron to back up to DigitalOcean Spaces.

      Prerequisites

      To follow this tutorial, you’ll need:

      • A server running Ubuntu 18.04 with root access and a sudo, non-root account, you can set this up by following this initial server setup guide.

      • A domain name for app installation. If you use a domain like example.com, Cloudron will install its dashboard at my.example.com and the apps will be installed under subdomains that you specify, like git.example.com, chat.example.com, and so on. It is safe to use a domain that is already in use as long as the my subdomain is available.

      • DNS API keys that Cloudron will use to perform DNS automation. Cloudron supports many DNS providers. If your DNS provider is not supported, you can use the wildcard or manual option. In this tutorial, we will use DigitalOcean DNS. You can generate a DigitalOcean DNS API key following How To Create a Personal Access Token.

      • A DigitalOcean Space with API keys (access and secret). To create a DigitalOcean Space and API keys, see How To Create a DigitalOcean Space and API Key.

      Step 1 — Installing Cloudron

      To begin, you’ll install Cloudron to your server using the official installation script. If you wish to skip this setup step, you can use the Cloudron 1-click image in the DigitalOcean Marketplace. Otherwise, for installing Cloudron manually, continue with this step.

      First, as your non-root user, download the setup script by running the following command:

      • wget https://cloudron.io/cloudron-setup

      Next, to make the setup script executable, run the following the command:

      • chmod +x ./cloudron-setup

      Finally, to install Cloudron, run the following command:

      • sudo ./cloudron-setup --provider digitalocean

      The setup will take around 10 minutes. Once setup is complete, the script will ask your permission to reboot the server, select Y to continue:

      Output

      ############################################## Cloudron Setup (latest) ############################################## Follow setup logs in a second terminal with: $ tail -f /var/log/cloudron-setup.log Join us at https://forum.cloudron.io for any questions. => Installing software-properties-common => Ensure required apt sources => Updating apt and installing script dependencies => Checking version => Downloading version 3.5.0 ... => Installing base dependencies and downloading docker images (this takes some time) ... => Installing version 3.5.0 (this takes some time) ... => Waiting for cloudron to be ready (this takes some time) .... Visit https://<IP> and accept the self-signed certificate to finish setup. The server has to be rebooted to apply all the settings. Reboot now ? [Y/n] Y

      You’ve installed Cloudron on your server, now you can set up your domain and administrator account.

      Step 2 — Setting Up Cloudron

      In this step, you’ll configure Cloudron’s primary domain using the setup wizard and set up your Cloudron account for access. You’ll also provide Cloudron with your DigitalOcean API keys to secure your domain with Let’s Encrypt.

      Once the server has rebooted, navigate to https://your-server-ip in your browser and accept the self-signed certificate.

      In Chrome, you can accept the self-signed certificate by clicking on Advanced and then click Proceed to your-server-ip (unsafe). In Firefox, click on Advanced, then Add Exception, and finally Confirm Security Exception.

      Chrome - Accept Self signed Certificate

      At this point, the Cloudron setup wizard will appear. Provide a domain name and select where you host your domain. Although Cloudron supports many DNS providers, like Cloudflare, Gandi, and GoDaddy, in this tutorial, the examples will follow with the domain using DigitalOcean as the DNS provider.

      Setup DNS

      Provide your DigitalOcean API token and click Next.

      Cloudron will use these keys to configure the DNS and obtain Let’s Encrypt certificates for your domain. Cloudron will redirect to https://my.example.com. The browser address bar will show a green lock to indicate that the connection to your Cloudron installation is now secure. Complete the setup by providing a username and password.

      Setup Administrator

      Once you have setup an Administrator account, you’ll see the No apps installed yet! screen. You’ve finished setting up your Cloudron account. Next you can decide which apps to install.

      Step 3 — Installing Your Apps

      You’re now ready to start installing apps.

      When you click on App Store on the interface, Cloudron will prompt you to create a cloudron.io account. You’ll use this account to manage your subscription and billing. Cloudron is free to use for two apps.

      Create cloudron.io account

      Once you sign up, you can install over 50 apps with a single click. Clicking on an app will bring up the installation dialog. As an example, you can click on the WordPress icon and use the blog subdomain. Once you click Install, Cloudron automatically sets up everything necessary for each app, such as: DNS, databases, certificates, and so on.

      Install WordPress

      When installing an app, you can choose whether the app should integrate with Cloudron’s centralized User management. If you opt to use Cloudron User management, you can log in to the app with your Cloudron username and password. You can then add more users and groups on Cloudron and control if they have access to the app. If you opt to instead leave user management to the app, the app will be pre-setup with an admin account and its credentials will display after installation. You can add more users inside the app itself. Note that apps on Cloudron are always accessible externally—the access control setting only determines how login authentication is done.

      Once installed, you can access the app at https://blog.example.com.

      WordPress

      You’ve installed an app to your Cloudron instance; you can use this process to install any of the available apps. To protect your data, you’ll configure backups in the next step.

      Step 4 — Configuring Backups

      Before using Cloudron in a production environment, it is essential to configure Cloudron to periodically save backups to a location that is external to the server. Unlike server snapshots, Cloudron creates backups individually for each app. The primary advantage of this approach is that apps can be individually rolled back, cloned, and even migrated to another new Cloudron installation.

      Cloudron can back up to any provider that supports S3 API or any external volume. In this tutorial, you will use DigitalOcean Spaces as the backup storage.

      With your DigitalOcean Space set up, you can use the associated API keys following this guide. Then, configure Cloudron backups by going to the Backups view and clicking Configure.

      Configure Backup on DO Spaces

      Cloudron will periodically store backups. If your server crashes or you want to move the server to a different DigitalOcean region, you can create a new Droplet, install Cloudron, and restore your server to the exact state from your backup.

      You’ve connected your Cloudron installation to your DigitalOcean Space and have configured backups. The next reviews some of the other features available with Cloudron.

      Step 5 — Exploring Other Features (Optional)

      Cloudron has centralized user management that allows you to invite team members and set up access control for each user.

      To add a new member, go to the Users view and click on New User. Cloudron will send the new user an email with a sign-up link.

      Users View

      A new user can sign up and start using any applications to which you’ve given them access. For the WordPress example in this tutorial, a new user could immediately access it since you installed the application with the User Management option set to Allow all users on this Cloudron.

      Another common task when managing your Cloudron instance, is installing apps on a domain other than the primary domain. To add a domain, go to the Domains view and click Add Domain. Once completed, you can install apps under this newly added domain.

      Domains View

      Security is always paramount and Cloudron provides turnkey security that includes tracking the details of your Cloudron installation. You can look in the Activity view to see all events logged for your Cloudron as well as all configuration changes.

      Conclusion

      In this tutorial, you installed Cloudron on your server. You then installed the WordPress app without having to manually provision databases, configure DNS, set up certificates, and so on. This WordPress installation will get automatic updates for new releases of WordPress. You configured Cloudron to periodically save backups to DigitalOcean Spaces. Finally, you explored other features that can provide additional protection and features to your Cloudron.

      Manually installing apps on a server is an involved process that has more potential for errors and can be time consuming. Modern web applications use a variety of databases, frameworks, and package managers that can complicate the installation. Furthermore, securing and backing up your applications is a separate process. When manually installing apps to keep them up-to-date, it is necessary to diligently track upstream releases and then apply the updates. Cloudron is a solution that helps you easily deploy and maintain apps on your server.

      To learn some more about Cloudron features, you can consult the documentation pages.

      For questions and discussion, visit the forum.



      Source link