From be9954286156e132c1fd91a975070bf1b15a5cdd Mon Sep 17 00:00:00 2001 From: nmelehan-akamai Date: Tue, 17 Dec 2024 13:50:59 -0500 Subject: [PATCH 1/8] [New] Configure Failover for HAProxy on Akamai (#7162) * Configure Failover for HAProxy on Akamai * Tech Edit 1 * Tech Edit 2 * Tech Edit 3 * Tech Edit 4 * Copy edits --------- Co-authored-by: Adam Overa Co-authored-by: Nathan Melehan --- .../index.md | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 docs/guides/uptime/loadbalancing/configure-failover-for-haproxy-on-akamai/index.md diff --git a/docs/guides/uptime/loadbalancing/configure-failover-for-haproxy-on-akamai/index.md b/docs/guides/uptime/loadbalancing/configure-failover-for-haproxy-on-akamai/index.md new file mode 100644 index 00000000000..fbf4f1712f2 --- /dev/null +++ b/docs/guides/uptime/loadbalancing/configure-failover-for-haproxy-on-akamai/index.md @@ -0,0 +1,296 @@ +--- +slug: configure-failover-for-haproxy-on-akamai +title: "Configure Failover for HAProxy on Akamai" +description: "Learn how to set up HAProxy load balancing with IP sharing, FRRouting, and BGP failover in this step-by-step guide." +authors: ["Tom Henderson"] +contributors: ["Tom Henderson"] +published: 2024-12-17 +keywords: ['haproxy','load balancing','failover','ip sharing','frrouting','bgp','high availability'] +license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)' +--- + +[HAProxy](https://www.haproxy.org/) is an HTTP and TCP gateway server that functions as a reverse proxy between a public-facing IP address and a set of backend servers. It manages incoming traffic using frontend rules and distributes it across the backend servers, providing load balancing and repairing HTTP requests when needed. + +However, HAProxy introduces a single point of failure. If the HAProxy server goes down, client connections to backend services are interrupted. This guide demonstrates how to configure HAProxy with a shared IP address and [FRRouting](https://frrouting.org/) to manage failover. The IP Sharing setup enables two HAProxy instances (primary and backup) to operate under the same IP address. In this setup, the primary instance is always active and the backup instance stands ready to take over in case of failure of the primary instance. FRRouting, configured with the Border Gateway Protocol (*BGP*), facilitates this automatic failover and maintains service continuity. + +This guide uses the free and open source (*FOSS*) version of HAProxy to create and demonstrate basic HAProxy failover pairs. With the FOSS version of HAProxy, the backup server doesn't retain the connection states of the primary instance. Therefore, when the backup becomes active, clients may need to reconnect to re-establish sessions. In contrast, the enterprise edition offers state-saving and restoration capabilities, along with other features that restore a failover with configuration data. + +## Before You Begin + +1. Follow the steps in [Getting Started with HAProxy TCP Load Balancing and Health Checks](/docs/guides/getting-started-with-haproxy-tcp-load-balancing-and-health-checks/) to create the example HAProxy instance and WordPress backend servers. + +1. Deploy a second HAProxy instance in the same data center, configured identically to the first HAProxy server. + +1. Disable Network Helper on both HAProxy instances by following the *Individual Compute Instance setting* section of our [Network Helper](https://techdocs.akamai.com/cloud-computing/docs/automatically-configure-networking#individual-compute-instance-setting) guide. + +1. Add an IP address to the first HAProxy instance by following the steps in the *Adding an IP Address* section of [Managing IP Addresses on a Compute Instance](https://techdocs.akamai.com/cloud-computing/docs/managing-ip-addresses-on-a-compute-instance#adding-an-ip-address). + +1. Link the second HAProxy instance to the new IP address on the first instance by following the *Configuring IP Sharing* section of [Managing IP Addresses on a Compute Instance](https://techdocs.akamai.com/cloud-computing/docs/managing-ip-addresses-on-a-compute-instance#configuring-ip-sharing). + +{{< note >}} +The steps in this guide require root privileges. Be sure to run the steps below as `root` or with the `sudo` prefix. For more information on privileges, see our [Users and Groups](/docs/guides/linux-users-and-groups/) guide. +{{< /note >}} + +## Configure the Shared IP Address + +Follow the instructions for your distribution to add the shared IP address to the networking configuration on both the primary and backup HAProxy servers: + +{{< tabs >}} +{{< tab "Ubuntu 24.04 LTS" >}} +Ubuntu 24.04 LTS uses `netplan` to manage network settings. + +1. Open the `/etc/netplan/01-netcfg.yaml` file in a text editor such as `nano`: + + ```command + sudo nano /etc/netplan/01-netcfg.yaml + ``` + + Append the highlighted lines to the end of the file: + + ```file {title="/etc/netplan/01-netcfg.yaml" hl_lines="7-11"} + network: + version: 2 + renderer: networkd + ethernets: + eth0: + dhcp4: yes + lo: + match: + name: lo + addresses: + - YOUR_SHARED_IP_ADDRESS/YOUR_PREFIX + ``` + + Be sure to make substitute your actual values for the following placeholders: + + - {{< placeholder "YOUR_SHARED_IP_ADDRESS" >}}: The shared IP address you set for HAProxy failover. + - {{< placeholder "YOUR_PREFIX" >}}: Use `32` for IPv4 addresses. For IPv6, use either `56` or `64`, depending on the range you're sharing. + + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + +1. Run the following command to apply the changes: + + ```command + sudo netplan apply + ``` +{{< /tab >}} +{{< tab "CentOS Stream 9" >}} +CentOS Stream 9 uses NetworkManager to configure network settings. + +1. Open the network configuration file for editing: + + ```command + sudo nano /etc/sysconfig/network-scripts/ifcfg-lo:1 + ``` + + Add the following configurations to set up the shared IP address: + + ```file {title="/etc/sysconfig/network-scripts/ifcfg-lo:1"} + DEVICE=lo:1 + IPADDR=YOUR_SHARED_IP_ADDRESS + NETMASK=YOUR_NETMASK + ONBOOT=yes + ``` + + Be sure to make substitute your actual values for the following placeholders: + + - {{< placeholder "YOUR_SHARED_IP_ADDRESS" >}}: The shared IP address you set for HAProxy failover. + - {{< placeholder "YOUR_NETMASK" >}}: Use `255.255.255.255` for IPv4 addresses. + + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + +1. Restart `NetworkManager` to apply the settings: + + ```command + sudo systemctl restart NetworkManager + ``` +{{< /tab >}} +{{< tab "openSUSE Leap 15.6" >}} +openSUSE Leap 15.6 uses `wicked` to manage network configurations. + +1. Edit the loopback configuration file: + + ```command + sudo nano /etc/sysconfig/network/ifcfg-lo + ``` + + Append the shared IP address settings to the end of the file: + + ```file {title="/etc/sysconfig/network/ifcfg-lo"} + IPADDR=YOUR_SHARED_IP_ADDRESS + NETMASK=YOUR_NETMASK + LABEL=1 + ``` + + Be sure to make substitute your actual values for the following placeholders: + + - {{< placeholder "YOUR_SHARED_IP_ADDRESS" >}}: The shared IP address you set for HAProxy failover. + - {{< placeholder "YOUR_NETMASK" >}}: Use `255.255.255.255` for IPv4 addresses. + + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + +1. Restart the network to activate the settings: + + ```command + sudo systemctl restart wicked + ``` +{{< /tab >}} +{{< /tabs >}} + +## Duplicate the HAProxy Configuration File + +To prepare for failover, the backup HAProxy instance must have an identical HAProxy configuration file to the primary instance. To ensure this, copy the HAProxy configuration file from the primary server to the backup server. + +Run this `scp` command on the backup server, replacing {{< placeholder "USERNAME" >}} with either `root` or another user with `sudo` access, and {{< placeholder "PRIMARY_SERVER_IP_ADDRESS" >}} with the actual IP address of the primary HAProxy server: + +```command {title="Backup Instance"} +scp {{< placeholder "USERNAME" >}}@{{< placeholder "PRIMARY_SERVER_IP_ADDRESS" >}}:/etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg +``` + +When prompted, enter the primary server's `root` or `sudo` user password to complete the transfer. + +Once the command finishes, verify that the configuration files on both servers are identical. + +{{< note >}} +In this example failover configuration, the primary server does not automatically become active once restored. After a failover event, copy the configuration file back to the primary server from the backup server to restore active status. +{{< /note >}} + +## Install FRRouting + +With IP Sharing properly configured, you're ready to install FRRouting. Follow these instructions on both the primary and backup HAProxy servers: + +1. Install FRRouting: + + {{< tabs >}} + {{< tab "Ubuntu 24.04 LTS" >}} + Install `frr` and `frr-pythontools` using `apt`: + + ```command + sudo apt install frr frr-pythontools + ``` + {{< /tab >}} + {{< tab "CentOS Stream 9" >}} + Install `frr` and `frr-pythontools` using `dnf`: + + ```command + sudo dnf install frr + ``` + {{< /tab >}} + {{< tab "openSUSE Leap 15.6" >}} + Install `frr` using `zypper`: + + ```command + sudo zypper install frr + ``` + {{< /tab >}} + {{< /tabs >}} + +1. Start the FRRouting service using `systemctl`: + + ```command + sudo systemctl start frr + ``` + +1. Enable FRRouting to run on startup: + + ```command + sudo systemctl enable frr + ``` + +## Configure FRRouting + +FRRouting must be configured on both the primary and backup HAProxy instances. Follow these instructions on both instances: + +1. Open the FRRouting `/etc/frr/daemons` file to enable the BGP daemon: + + ```command + sudo nano /etc/frr/daemons + ``` + + Locate the `bgpd` line and change its value to `yes` to activate the BGP daemon: + + ```file {title="/etc/frr/daemons"} + # The watchfrr and zebra daemons are always started. + # + bgpd=yes + ``` + + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + +1. Open the FRRouting configuration file located at `/etc/frr/frr.conf`: + + ```command + sudo nano /etc/frr/frr.conf + ``` + + Append the following content to the end of the file to configure BGP settings: + + ```file {title="/etc/frr/frr.conf" hl_lines="1,11-14,16,17"} + hostname {{< placeholder "YOUR_HOSTNAME" >}} + + router bgp 65001 + no bgp ebgp-requires-policy + coalesce-time 1000 + bgp bestpath as-path multipath-relax + neighbor RS peer-group + neighbor RS remote-as external + neighbor RS ebgp-multihop 10 + neighbor RS capability extended-nexthop + neighbor 2600:3c0f:{{< placeholder "YOUR_DC_ID" >}}:34::1 peer-group RS + neighbor 2600:3c0f:{{< placeholder "YOUR_DC_ID" >}}:34::2 peer-group RS + neighbor 2600:3c0f:{{< placeholder "YOUR_DC_ID" >}}:34::3 peer-group RS + neighbor 2600:3c0f:{{< placeholder "YOUR_DC_ID" >}}:34::4 peer-group RS + + address-family {{< placeholder "YOUR_PROTOCOL" >}} unicast + network {{< placeholder "YOUR_SHARED_IP_ADDRESS" >}}/{{< placeholder "YOUR_PREFIX" >}} route-map {{< placeholder "YOUR_ROLE" >}} + redistribute static + exit-address-family + + route-map primary permit 10 + set community 65000:1 + route-map secondary permit 10 + set community 65000:2 + + ipv6 nht resolve-via-default + ``` + + Substitute the following placeholders for your actual information: + + - {{< placeholder "YOUR_HOSTNAME" >}}: Your instance's hostname. + - {{< placeholder "YOUR_DC_ID" >}}: The data center ID where your instances are located. Reference our [IP Sharing Availability table](https://techdocs.akamai.com/cloud-computing/docs/configure-failover-on-a-compute-instance#ip-sharing-availability) to determine the ID for your data center. + - {{< placeholder "YOUR_PROTOCOL" >}}: Either `ipv4` for IPv4 or `ipv6` for IPv6. + - {{< placeholder "YOUR_SHARED_IP_ADDRESS" >}}: Your shared IP address. + - {{< placeholder "YOUR_PREFIX" >}}: Use `32` for IPv4 addresses. For IPv6, use either `56` or `64`, depending on the range you're sharing. + - {{< placeholder "YOUR_ROLE" >}}: Set as `primary` on the primary instance and `secondary` on the backup instance. + + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + +1. Restart `frr` to apply the configuration changes: + + ```command + sudo systemctl restart frr + ``` + +## Test Failover + +FRRouting on the backup server monitors the primary server's status using a "ping-like" test. If the primary does not respond, the backup automatically takes over, providing continuous access to backend services. + +To test failover, follow these steps: + +1. **Verify Initial Access**: Ensure the primary HAProxy server is actively serving pages. Open the shared IP address in a web browser and refresh until all three backend servers respond successfully. + +1. **Simulate Primary Failure**: Use the Akamai Cloud Manager to power down the primary HAProxy instance, triggering a failover to the backup server. + +1. **Confirm Failover**: Refresh the browser after powering down the primary HAProxy instance. Within a few seconds, the pages should load again, now served through the backup HAProxy instance. This indicates that failover is working as expected. + +### Troubleshooting Test Failures + +If failover does not occur and refreshing the browser does not restore page access through the backup server after several seconds, follow these troubleshooting steps: + +- **Verify IP Sharing Configuration**: Double-check that all steps for configuring IP Sharing were followed correctly. +- **Check Network Connectivity**: Use the ICMP `ping` command to test if the backup server is reachable. If the backup server is not reachable, there may be an issue with the IP Sharing configuration. +- **Review FRRouting Configuration**: Ensure that the FRRouting `frr.config` file is correctly configured, especially the Roles section, which should be set according to the provided instructions. +- **Confirm HAProxy Configuration**: Verify that the backup server's `/etc/haproxy/haproxy.cfg` file matches the configuration of the primary server by comparing the two files directly. +- **Test Backend Server Accessibility**: Try accessing a backend server directly (e.g. `backend1`) by its IP address. If the backend server responds correctly, the issue may lie with the failover configuration rather than the backend services. + +Failover latency should be minimal, ideally within a few seconds. If failover takes longer, the FRRouting daemon's configuration offers several settings to optimize detection and failover speed. \ No newline at end of file From e2ec29f30ac435e86ddb96428fceed6882a25028 Mon Sep 17 00:00:00 2001 From: Nathan Melehan Date: Tue, 17 Dec 2024 14:10:44 -0500 Subject: [PATCH 2/8] Block storage migration azure (#7156) * Import from external editor * Copy edits * Copy edits * Copy edits * Copy edits * Copy edits * Copy edits * Copy edits * Copy edits, add diagram * Update diagram * copy edits * typo fix * Remove failover guide from PR --------- Co-authored-by: John Dutton --- ...azure-block-storage-migration-workflow.svg | 179 +++++++++++ .../index.md | 278 ++++++++++++++++++ 2 files changed, 457 insertions(+) create mode 100644 docs/guides/platform/migrate-to-linode/migrate-from-azure-disk-storage-to-linode-block-storage/azure-block-storage-migration-workflow.svg create mode 100644 docs/guides/platform/migrate-to-linode/migrate-from-azure-disk-storage-to-linode-block-storage/index.md diff --git a/docs/guides/platform/migrate-to-linode/migrate-from-azure-disk-storage-to-linode-block-storage/azure-block-storage-migration-workflow.svg b/docs/guides/platform/migrate-to-linode/migrate-from-azure-disk-storage-to-linode-block-storage/azure-block-storage-migration-workflow.svg new file mode 100644 index 00000000000..a773bf91145 --- /dev/null +++ b/docs/guides/platform/migrate-to-linode/migrate-from-azure-disk-storage-to-linode-block-storage/azure-block-storage-migration-workflow.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/guides/platform/migrate-to-linode/migrate-from-azure-disk-storage-to-linode-block-storage/index.md b/docs/guides/platform/migrate-to-linode/migrate-from-azure-disk-storage-to-linode-block-storage/index.md new file mode 100644 index 00000000000..d578ed9cbe9 --- /dev/null +++ b/docs/guides/platform/migrate-to-linode/migrate-from-azure-disk-storage-to-linode-block-storage/index.md @@ -0,0 +1,278 @@ +--- +slug: migrate-from-azure-disk-storage-to-linode-block-storage +title: "Migrate From Azure Disk Storage to Linode Block Storage" +description: "Two to three sentences describing your guide." +og_description: "Optional two to three sentences describing your guide when shared on social media. If omitted, the `description` parameter is used within social links." +authors: ["Leon Yen","Nathan Melehan"] +contributors: ["Leon Yen","Nathan Melehan"] +published: 2024-11-18 +keywords: ['list','of','keywords','and key phrases'] +license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)' +--- + +This guide describes the process of migrating a single volume from Azure Disk Storage to Linode Block Storage using the rsync file synchronization utility. This guide focuses on migrating data disks from Azure Disk Storage and does not cover migrating operating system disks. + +## Block Storage Migration Workflow Diagram + +![Azure to Linode Block Storage Migration Workflow Diagram](azure-block-storage-migration-workflow.svg?diagram-description-id=azure-linode-bs-migration) + +1. The rsync command is run from a Linode instance and connects to the Azure virtual machine. + +2. The Azure VM sends data on the Azure data disk to a Block Storage Volume attached to the Linode instance via an established rsync connection. + + 2a. Egress costs for the migrated data are measured when the data leaves the Azure platform. These costs are billed by Azure. + +{#azure-linode-bs-migration .large-diagram} + +## Linode Block Storage vs. Azure Disk Storage + +When an Azure Virtual Machine is first created, Azure Disk Storage creates a managed disk for the operating system. One or more managed disks can also be created with the Azure Disk Storage service for temporary files and for persistent data disks. + +Like Azure Disk Storage, Linode Block Storage also provides block-level storage volumes to be used with virtual machines. Unlike Azure Disk Storage, Linode Block Storage is generally used for persistent data rather than operating system, boot disks, or temporary data. These other roles are fulfilled by a Linode instance's bundled disk, which is stored on the same host as the Compute Instance. Linode's bundled disk storage is also more suitable for applications that feature high disk operations, like high-traffic databases. + +## Migration Considerations + +The following are important time, cost, and security considerations to keep in mind when migrating your Azure Disk Storage drives to Linode Block Storage. + +### Migration Time Estimates + +The time it takes to migrate a data disk is a function of the data stored on that disk, which can be substantial for larger migrations. To determine how much data is stored on your disk, run the `df` command from your Azure VM: + +```command {title="SSH session with Azure VM"} +df -h +``` + +Your data disk should appear, and the `Used` column shows how much data is stored on the disk: + +```output +Filesystem Size Used Avail Use% Mounted on +/dev/sdc1 20G 4.4G 16G 23% /datadrive +``` + +Bandwidth for the transfer can vary according to different factors, including: +- Outbound bandwidth limits for your Azure VM +- Geographic distance between the Azure VM and the Linode instance. +- Disk operation limits + +When planning your migration, consider performing a bandwidth test between the two locations first. Then, use the observed bandwidth from the test to calculate the estimated migration time for the data disk. + +Utilities like [iperf](https://en.wikipedia.org/wiki/Iperf) can be useful for performing this type of bandwidth measurement. Alternatively, you can create a test file on the Azure VM, migrate it following the [instructions](#block-storage-migration-instructions) in this guide, and then view the bandwidth reported by rsync's output. + +You can use the `dd` command to generate a sample 1GB test file: + +```command {title="SSH session with Azure VM"} +sudo dd if=/dev/zero of=/datadrive/dummyfile bs=1M count=1024 +``` + +### Migration Egress Costs + +The cost to migrate a data disk is a function of the data stored on that disk, which can be substantial for larger migrations. These costs are incurred as egress fees when the data leaves the Azure platform and are billed by Azure. Review the [Migration Time Estimates](#migration-time-estimates) section for help with determining how much data is stored on the disk, and review [Azure's documentation](https://azure.microsoft.com/en-us/pricing/details/bandwidth/) for assistance with calculating this amount. + +Inbound traffic sent to your Linode instance and Block Storage volume have no fees incurred on the Linode platform. + +### Security and Firewalls + +For data security reasons, files should be migrated over an encrypted connection. Rsync supports using SSH as its transport protocol, which is encrypted by default. + +Both your Azure and Linode firewall settings should be configured to allow SSH traffic between the two instances. After the migration is performed, you may wish to close access to SSH between the Linode instance and Azure virtual machine. + +## Block Storage Migration Instructions + +### Prerequisites and Assumptions + +This guide assumes that you have an Azure virtual machine and an attached data disk on the Azure Disk Storage service. The assumed filesystem path for the Azure data disk is `/datadrive`, and the username for the Azure virtual machine is `azureuser`. + +### Prepare a Linode Block Storage Volume + +1. To transfer data to a Linode Block Storage volume, it must first be attached to a Linode instance. You may create a new Linode instance for the purpose of this migration ([Create a Compute Instance](https://techdocs.akamai.com/cloud-computing/docs/create-a-compute-instance)). Alternatively, you can use an existing Linode instance for the migration. + + {{< note >}} + If you create an instance to use for this migration, you may wish to delete it after the migration is complete. Deleting an instance that has an attached volume does not delete the volume. + {{< /note >}} + +1. Follow the [Add volumes](https://techdocs.akamai.com/cloud-computing/docs/manage-block-storage-volumes#add-volumes) product documentation to create and attach a new volume to the Linode instance. This volume should have a capacity equal to or higher than the total data stored on the source Azure disk. Review the [Migration Time Estimates](#migration-time-estimates) section for help with determining how much data is stored on the disk. + + When creating the volume, Cloud Manager displays instructions for how to create a filesystem on the new volume and then mount it. Make a note of the filesystem path that it is mounted under (e.g. `/mnt/linode-block-storage-volume`). + +### Configure Firewalls + +In this guide, the rsync command is run from a Linode instance and connects to an Azure virtual machine. This means that the Azure VM should accept inbound SSH traffic (port 22). You may also wish to specifically add the IP address of the Linode instance to the allow list for inbound traffic of the Azure VM. + +Linux distributions (on both Linode instances and Azure virtual machines) can have software firewalls configured inside the instance. The following guides describe some software firewalls that your instances may use: + +- [Configure a Firewall with Firewalld](/docs/guides/introduction-to-firewalld-on-centos/) +- [How to Configure a Firewall with UFW](/docs/guides/configure-firewall-with-ufw/) +- [A Tutorial for Controlling Network Traffic with iptables](/docs/guides/control-network-traffic-with-iptables/) + +You may also configure Cloud Firewalls to control traffic before it arrives at your computing instance. Our [Cloud Firewall](https://techdocs.akamai.com/cloud-computing/docs/cloud-firewall/) product documentation describes how to configure these rules. The [Comparing Cloud Firewalls to Linux firewall software](https://techdocs.akamai.com/cloud-computing/docs/comparing-cloud-firewalls-to-linux-firewall-software) guide further describes the difference between network firewalls and software firewalls. [Microsoft Azure's product documentation](https://learn.microsoft.com/en-us/azure/firewall/overview) describes how to configure Azure network firewalls. + +### Configure SSH Key Pair + +This guide uses SSH public key authentication for the rsync connection. You must have a public and private key pair installed on the Linode instance and Azure virtual machine. The [Generate an SSH Key Pair](/docs/guides/use-public-key-authentication-with-ssh/#generate-an-ssh-key-pair) section of the [SSH Public Key Authentication](/docs/guides/use-public-key-authentication-with-ssh/) guide describes how to create and use a key pair. + +This guide assumes the public and private keys are named `id_rsa.pub` and `id_rsa`, but your keys may have different names depending on the type of key pair you are using. + +- The *public key* should be uploaded to the Azure virtual machine. It should be appended to a new line of the `authorized_keys` file of the user on the Azure VM (e.g. `/home/azureuser/.ssh/authorized_keys`). + +- The *private key* should be located on the Linode instance. It should be uploaded to the `.ssh/` directory of the user on the Linode instance (e.g. `/home/linodeuser/.ssh/`) and have permissions set to `600`: + + ```command {title="SSH session with Linode instance"} + chmod 600 /home/linodeuser/.ssh/id_rsa/ + ``` + +### Initiate the Migration + +These instructions implement two recommended practices: + +- Running rsync in a persistent process + +- Sending output and errors to log files + +Migrations can take a long time, so having them run independently of your SSH session is important. This guide uses `tmux` to create a terminal session that persists between SSH connections. By sending output and errors to log files, you can keep a record of any migration failures that may happen. + +Review our [tmux guide](/docs/guides/persistent-terminal-sessions-with-tmux/) for help with other tmux commands. + +1. Install the `tmux` utility on your Linode instance using the official tmux instructions: [Installing tmux](https://github.com/tmux/tmux/wiki/Installing#installing-tmux). + +1. Create a new tmux session named `block-storage-migration`. This session is used to initiate the migration: + + ```command {title="SSH session with Linode instance"} + tmux new -s block-storage-migration + ``` + + After running this command, the tmux session is immediately activated in your terminal. + +1. Run the following commands to start migrating the contents of your Azure data disk to your Linode Block Storage Volume: + + ```command {title="SSH session with Linode instance (bs-migration tmux session)"} + echo "\n\nInitiating migration $(date)\n---" | tee -a bs-migration-logs.txt bs-migration-errors.txt >/dev/null + + rsync -chavzP --stats -e "ssh -i /home/linodeuser/.ssh/id_rsa" {{< placeholder "azureuser" >}}@{{< placeholder "AZURE_VM_IP" >}}:/datadrive/ /mnt/linode-block-storage-volume 1>>~/bs-migration-logs.txt 2>>~/bs-migration-errors.txt + ``` + + Replace the following values with the actual values from your Azure VM and Linode instance: + + - `/home/linodeuser/.ssh/id_rsa`: The name and location of the private key on your Linode instance + - `{{< placeholder "azureuser" >}}`: The name of the user on the Azure VM + - `{{< placeholder "AZURE_VM_IP" >}}`: The IP address of the Azure VM + - `/datadrive/`: The directory under which the Azure data disk is mounted + - `/mnt/linode-block-storage-volume`: The directory under which your Linode volume is mounted + + {{< note >}} + You may be prompted to accept the host key of the Azure VM if it is the first time that the Linode has connected to it. + {{< /note >}} + + **Command breakdown**: + + The first `echo` appends a message to the log files. Below is a detailed explanation of the key flags and parameters used in the `rsync` command: + + - `-c`: Tells rsync to use checksum comparison for file differences. Normally, rsync uses file size and modification time to decide if files need to be updated, but `-c` forces it to compute checksums, which is slower but can be more accurate if you want to be sure that files match exactly. + + - `-h`: Human-readable output, which makes file sizes like transfer statistics easier to read by using units like KB and MB, rather than raw byte counts. + + - `-a`: Archive mode. This is equivalent to specifying: `-rlptgoD`. The result of the `-a` flag is a complete, near-exact copy of the source directory: + + - `-r`: Recursively copy directories + + - `-l`: Preserve symbolic links + + - `-p`: Retain file permissions + + - `-t`: Keep timestamps + + - `-g`: Preserve group ownership + + - `-o`: Maintain file ownership + + - `-D`: Retain device files and special files + + - `-v`: Verbose mode. This makes rsync output more detailed information about what it is doing, and can be helpful for monitoring the progress of a large transfer or troubleshooting. + + - `-z`: Compression. This enables compression during data transfer, which can save bandwidth and speed up the transfer if the network connection is relatively slow. + + - `-P`: Combines two other flags: + + - `--progress`, which displays progress information for each file transfer. + + - `--partial`, which keeps partially transferred files if the transfer is interrupted, allowing it to resume more easily next time. + + - `--stats`: Provides detailed statistics at the end of the transfer, such as total bytes transferred, transfer speed, and file counts. + + - `-e "ssh -i /home/linodeuser/.ssh/id_rsa"`: Specifies a remote shell (SSH) with an identity key file for authentication. + + - `{{< placeholder "azureuser" >}}@{{< placeholder "AZURE_VM_IP" >}}:/datadrive/`: This specifies the source directory you're syncing from: + + - `{{< placeholder "azureuser" >}}`: The username on the remote server. + + - `{{< placeholder "AZURE_VM_IP" >}}`: The IP address of the remote server. + + - `/datadrive/`: The path on the remote server that you want to sync. The trailing slash (/) means rsync will copy the contents of /datadrive, rather than creating a /datadrive directory in the target. + + - `/mnt/linode-block-storage-volume`: The destination directory on the local machine where rsync will copy the files to. In this case, it will create an exact copy of /datadrive contents here. + +### Monitor the Migration + +Because the stdout and stderr streams were redirected to log files, the rsync command will not produce output in the terminal. Follow these steps to inspect and monitor the contents of the logs: + +1. To avoid interrupting the rsync process, *detach* from the tmux session by entering this sequence of keystrokes: Ctrl + B followed by D. You are returned to the SSH session that created the tmux session: + + ```output + [detached (from session block-storage-migration)] + ``` + +1. Use `tail -f` to inspect the log and error files and monitor any new output from them: + + ```command {title="SSH session with Linode instance"} + tail -f block-storage-migration-logs.txt + ``` + + ```command {title="SSH session with Linode instance"} + tail -f block-storage-migration-errors.txt + ``` + + Enter Ctrl + C to stop `tail`. + +1. You can re-enter the tmux session with the `tmux attach` command: + + ```command {title="SSH session with Linode instance"} + tmux attach -t block-storage-migration + ``` + +### Verify the Migration + +To verify that rsync has synced all the files as expected, re-run the `rsync` command with the `--dry-run –stats` flags, replacing the same values as before: + +```command {title="SSH session with Linode instance"} +rsync -chavzP --stats --dry-run -e "ssh -i /home/azureuser/.ssh/id_rsa" {{< placeholder "azureuser" >}}@{{< placeholder "AZURE_VM_IP" >}}:/datadrive/ /mnt/linode-block-storage-volume +``` + +If the output displays files yet to be transferred, then rsync did not fully replicate the files in the destination directory. A previous successful rsync transfer should result in the following output. Note that the number of created, deleted, and transferred files are zero: + +```output +receiving incremental file list + +Number of files: 2 (reg: 1, dir: 1) +Number of created files: 0 +Number of deleted files: 0 +Number of regular files transferred: 0 +Total file size: 10.49M bytes +Total transferred file size: 0 bytes +Literal data: 0 bytes +Matched data: 0 bytes +File list size: 84 +File list generation time: 0.003 seconds +File list transfer time: 0.000 seconds +Total bytes sent: 20 +Total bytes received: 95 + +sent 20 bytes received 95 bytes 230.00 bytes/sec +total size is 10.49M speedup is 91,180.52 (DRY RUN) +``` + +### Cleanup after Migration + +After the migration is complete, you may determine that the Azure VM and Linode instance no longer need to communicate. You can close traffic between these servers by doing the following: + +- Remove the firewall access granted in the [Configure Firewalls](#configure-firewalls) section + +- Revoke the SSH key used for the transfer. This is done by removing the SSH public key that was referenced from the `/home/azureuser/.ssh/authorized_keys` file on the Azure VM. \ No newline at end of file From 3dd0e7cbb14ef14b59dd03082fe5a6fe93f5e920 Mon Sep 17 00:00:00 2001 From: Rajakavitha Kodhandapani Date: Wed, 18 Dec 2024 00:50:14 +0530 Subject: [PATCH 3/8] [Update]Understanding iptables (#7161) * [Update]Understanding iptables Updated the tables and the chains supported after validating: ```root@localhost:~# sudo iptables -t nat -L Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination ``` * format the table --- docs/guides/security/firewalls/what-is-iptables/index.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/guides/security/firewalls/what-is-iptables/index.md b/docs/guides/security/firewalls/what-is-iptables/index.md index bdcc75a8f38..5e096a8b401 100644 --- a/docs/guides/security/firewalls/what-is-iptables/index.md +++ b/docs/guides/security/firewalls/what-is-iptables/index.md @@ -23,7 +23,14 @@ This guide helps you understand iptables and explains what is iptables. It gets A table is a collection of chains that serves a particular function. The 3 main tables in iptables are the Filter, NAT, and Mangle tables. -!["iptables table of tables](iptables-table-of-tables.png "iptables table of tables") +| **Filter** | **NAT** | **Mangle** | +|-------------------|--------------------------|-----------------------------------| +| INPUT chain | INPUT chain | INPUT chain | +| FORWARD chain | OUTPUT chain | FORWARD chain | +| OUTPUT chain | PREROUTING chain | OUTPUT chain | +| | POSTROUTING chain | PREROUTING chain | +| | | POSTROUTING chain | + - The **Filter Table** is used to control the flow of packets in and out of a system. - The **NAT Table** is used to redirect connections to other interfaces on the network. From ccba87e10513fc5ddc2d629c7e34008d358dbaa7 Mon Sep 17 00:00:00 2001 From: nmelehan-akamai Date: Tue, 17 Dec 2024 14:34:01 -0500 Subject: [PATCH 4/8] Update theme (#7163) * Update theme - Header and footer updates - Add file-issue buttons on hover to guide paragraphs/text (Note: this is temporarily disabled in the site configuration) * Temporarily disable file issue button feature --------- Co-authored-by: Nathan Melehan --- .../assets/js/main/helpers/helpers.js | 2 +- .../linode-docs-theme/assets/js/main/index.js | 4 + .../js/main/navigation/file-issue-button.js | 149 ++++++++ .../linode/linode-docs-theme/config.toml | 17 + .../config/development/config.toml | 1 + .../config/staging/config.toml | 1 + .../config/testing/config.toml | 1 + .../content/whatsnew/index.md | 1 + .../layouts/_default/baseof.html | 5 +- .../layouts/_default/whatsnew.html | 37 +- .../linode-docs-theme/layouts/index.html | 69 ++-- .../partials/sections/after-body-start.html | 5 + .../layouts/partials/sections/head-src.html | 1 + .../layouts/partials/sections/head.html | 17 +- .../navigation/file-issue-button.html | 60 ++++ .../layouts/shortcodes/tabs.html | 1 + .../linode-docs-theme/tailwind.config.js | 4 +- .../linode-website-partials/footer.html | 149 ++++---- .../linode-website-partials/header.html | 330 +++++++++--------- .../linode/linode-website-partials/header.js | 15 +- _vendor/modules.txt | 4 +- config.toml | 3 + go.mod | 2 +- go.sum | 3 + 24 files changed, 609 insertions(+), 272 deletions(-) create mode 100644 _vendor/github.com/linode/linode-docs-theme/assets/js/main/navigation/file-issue-button.js create mode 100644 _vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/navigation/file-issue-button.html diff --git a/_vendor/github.com/linode/linode-docs-theme/assets/js/main/helpers/helpers.js b/_vendor/github.com/linode/linode-docs-theme/assets/js/main/helpers/helpers.js index d4e4b98254c..c50475776b4 100644 --- a/_vendor/github.com/linode/linode-docs-theme/assets/js/main/helpers/helpers.js +++ b/_vendor/github.com/linode/linode-docs-theme/assets/js/main/helpers/helpers.js @@ -118,7 +118,7 @@ export function scrollToActiveExplorerNode() { } } -function getOffsetTop(container, el) { +export function getOffsetTop(container, el) { let offset = 0; while (el && el != container) { offset += el.offsetTop; diff --git a/_vendor/github.com/linode/linode-docs-theme/assets/js/main/index.js b/_vendor/github.com/linode/linode-docs-theme/assets/js/main/index.js index e403807cd0d..c4c0a38c5da 100644 --- a/_vendor/github.com/linode/linode-docs-theme/assets/js/main/index.js +++ b/_vendor/github.com/linode/linode-docs-theme/assets/js/main/index.js @@ -31,6 +31,7 @@ import { newSearchFiltersController, newSearchInputController, newSearchStore, g import { newHomeController } from './sections/home/home'; import { newSectionsController } from './sections/sections/index'; import { newSVGViewerController } from './navigation/svg-viewer'; +import { newFileIssueButton } from './navigation/file-issue-button'; // Set up the search configuration (as defined in config.toml). const searchConfig = getSearchConfig(params); @@ -101,6 +102,9 @@ const searchConfig = getSearchConfig(params); Alpine.data('lncPromoCodes', () => newPromoCodesController(params.is_test)); Alpine.data('lncFetch', fetchController); Alpine.data('lnvSVGViewer', newSVGViewerController); + if (params.file_issue_button && params.file_issue_button.enable) { + Alpine.data('lncFileIssueButton', () => newFileIssueButton(params.file_issue_button)); + } // Page controllers. Alpine.data('lncHome', (staticData) => { diff --git a/_vendor/github.com/linode/linode-docs-theme/assets/js/main/navigation/file-issue-button.js b/_vendor/github.com/linode/linode-docs-theme/assets/js/main/navigation/file-issue-button.js new file mode 100644 index 00000000000..7642896133e --- /dev/null +++ b/_vendor/github.com/linode/linode-docs-theme/assets/js/main/navigation/file-issue-button.js @@ -0,0 +1,149 @@ +var debug = 0 ? console.log.bind(console, '[file-issue-button]') : function () {}; + +import { getOffsetTop, isMobile } from '../helpers/helpers'; + +export function newFileIssueButton(conf) { + return { + isHovered: false, + hoveredEl: null, + isHoveredButton: false, + timeoutID: null, + + // The issue items presented to the user. + items: [], + + show() { + return this.isHoveredButton || this.isHovered; + }, + + hoverButton() { + // Prepare the issue items. + // First clear any existing items. + this.items.length = 0; + + for (let item of conf.issue_templates) { + let info = window.lnPageInfo; + let file = ''; + if (info.path) { + file = `https://github.com/linode/docs/blob/develop/docs/${info.path}`; + } + let context = this.hoveredEl.textContent.trim(); + debug('context:', context); + let href = `${conf.repo_url}/issues/new?&template=${item.id}&file=${encodeURIComponent( + file, + )}&context=${encodeURIComponent(context)}`; + this.items.push({ title: item.title, href: href }); + } + + // This will show the issue items dropdown. + this.isHoveredButton = true; + }, + + hoverOn(hoveredEl) { + if (this.isHovered) { + debug('hoverOn:', hoveredEl.tagName, 'vs', this.hoveredEl.tagName); + + if (hoveredEl.tagName !== this.hoveredEl.tagName) { + // Check if we're hovering over the same element or an ancestor. + if (hoveredEl.contains(this.hoveredEl)) { + debug('skip'); + return; + } + } else { + if (hoveredEl.tagName == 'TD' || hoveredEl.tagName == 'TH') { + debug('skip'); + return; + } + } + } + if (this.isHoveredButton) { + this.isHoveredButton = false; + } + if (this.timeoutID) { + clearTimeout(this.timeoutID); + } + debug('hoverOn:', hoveredEl.tagName); + + if (hoveredEl.tagName === 'PRE') { + // If the parent is a TD we need to select the second column, the first is line numbers. + let parent = hoveredEl.parentNode; + if (parent.tagName === 'TD') { + hoveredEl = parent.parentNode.parentNode; + let tds = hoveredEl.querySelectorAll('td'); + // Select last td. + hoveredEl = tds[tds.length - 1]; + } + } + + this.isHovered = true; + this.hoveredEl = hoveredEl; + + let el = this.$el; + let container = el.parentNode; + let distance = getOffsetTop(container, hoveredEl); + + // Position el relative to hoveredEl. + el.style.position = 'absolute'; + el.style.top = distance + 'px'; + el.style.left = '0'; + el.style.zIndex = 5; + + this.timeoutID = setTimeout(() => { + this.isHovered = false; + }, 2500); + }, + + init() { + return this.$nextTick(() => { + if (isMobile()) { + return; + } + let mainContentEl = document.getElementById('main__content'); + if (!mainContentEl) { + return; + } + + mainContentEl.querySelectorAll('.content').forEach((contentEl) => { + contentEl.addEventListener( + 'mouseover', + (e) => { + switch (e.target.tagName) { + case 'DL': + case 'LI': + case 'P': + case 'PRE': + case 'TD': + case 'TH': + this.hoverOn(e.target); + break; + case 'SPAN': + case 'CODE': + // Check if we're in a pre block. + let pre = e.target.closest('pre'); + if (pre) { + this.hoverOn(pre); + } + break; + case 'DIV': + // This class is set in the tabs component etc, + // to avoid getting many false positives on the DIVS. + let whitelist = ['file-issue-button-content', 'note', 'code']; + for (let w of whitelist) { + if (e.target.classList.contains(w)) { + this.hoverOn(e.target); + break; + } + } + break; + default: + debug('default:', e.target.tagName); + break; + } + }, + { passive: true }, + ); + }); + }); + }, + }; +} diff --git a/_vendor/github.com/linode/linode-docs-theme/config.toml b/_vendor/github.com/linode/linode-docs-theme/config.toml index 850f986ae64..80ff9943716 100644 --- a/_vendor/github.com/linode/linode-docs-theme/config.toml +++ b/_vendor/github.com/linode/linode-docs-theme/config.toml @@ -10,8 +10,25 @@ weglot_api_key = "wg_3b3ef29c81aa81292c64d1368ee318969" adobe_launch_script = "https://assets.adobedtm.com/fcfd3580c848/f9e7661907ee/launch-2fb69de42220.min.js" # OneTrust Domain used in production. +onetrust_script_src = "https://www.linode.com/ns/ot/202410.1.0/prod/scripttemplates/otSDKStub.js" onetrust_domain_script = "01922358-0e47-73fa-9452-fa124177d6d6" +# Configuration for the contextual menu over content items that +# allows the user to send specific feedback/issue about the content +# to GitHub. +# We currently pass attribute 'context' and 'file' prefilled from the front-end. +[params.file_issue_button] +enable = true +repo_url = "https://github.com/bep/githubissuestest" +[[params.file_issue_button.issue_templates]] +# The id maps to a GitHub issue template in the repository. +id = "issue-template-1.yml" +# The title is what gets shown in the contextual menu. +title = "Report a problem" +[[params.file_issue_button.issue_templates]] +id = "issue-template-2.yml" +title = "Report something else" + [params.search_config2] app_id = "KGUN8FAIPF" diff --git a/_vendor/github.com/linode/linode-docs-theme/config/development/config.toml b/_vendor/github.com/linode/linode-docs-theme/config/development/config.toml index f292e15ec94..45b87b99f23 100644 --- a/_vendor/github.com/linode/linode-docs-theme/config/development/config.toml +++ b/_vendor/github.com/linode/linode-docs-theme/config/development/config.toml @@ -3,4 +3,5 @@ adobe_launch_script = "https://assets.adobedtm.com/fcfd3580c848/f9e7661907ee/launch-006d022c8726-development.min.js" # OneTrust Domain used in test/development. + onetrust_script_src = "https://www.linode.com/ns/ot/202410.1.0/staging/scripttemplates/otSDKStub.js" onetrust_domain_script = "01922358-0e47-73fa-9452-fa124177d6d6-test" diff --git a/_vendor/github.com/linode/linode-docs-theme/config/staging/config.toml b/_vendor/github.com/linode/linode-docs-theme/config/staging/config.toml index 9091267e64a..246550c5804 100644 --- a/_vendor/github.com/linode/linode-docs-theme/config/staging/config.toml +++ b/_vendor/github.com/linode/linode-docs-theme/config/staging/config.toml @@ -3,4 +3,5 @@ adobe_launch_script = "https://assets.adobedtm.com/fcfd3580c848/f9e7661907ee/launch-96338797f65e-staging.min.js" # OneTrust Domain used in test/development. + onetrust_script_src = "https://www.linode.com/ns/ot/202410.1.0/staging/scripttemplates/otSDKStub.js" onetrust_domain_script = "01922358-0e47-73fa-9452-fa124177d6d6-test" diff --git a/_vendor/github.com/linode/linode-docs-theme/config/testing/config.toml b/_vendor/github.com/linode/linode-docs-theme/config/testing/config.toml index f292e15ec94..45b87b99f23 100644 --- a/_vendor/github.com/linode/linode-docs-theme/config/testing/config.toml +++ b/_vendor/github.com/linode/linode-docs-theme/config/testing/config.toml @@ -3,4 +3,5 @@ adobe_launch_script = "https://assets.adobedtm.com/fcfd3580c848/f9e7661907ee/launch-006d022c8726-development.min.js" # OneTrust Domain used in test/development. + onetrust_script_src = "https://www.linode.com/ns/ot/202410.1.0/staging/scripttemplates/otSDKStub.js" onetrust_domain_script = "01922358-0e47-73fa-9452-fa124177d6d6-test" diff --git a/_vendor/github.com/linode/linode-docs-theme/content/whatsnew/index.md b/_vendor/github.com/linode/linode-docs-theme/content/whatsnew/index.md index f09b37b9a64..9ab153445ad 100644 --- a/_vendor/github.com/linode/linode-docs-theme/content/whatsnew/index.md +++ b/_vendor/github.com/linode/linode-docs-theme/content/whatsnew/index.md @@ -1,5 +1,6 @@ --- title: "What’s New" +linkTitle: "New and updated Cloud Guides & Tutorials" date: 2021-04-16 layout: "whatsnew" --- diff --git a/_vendor/github.com/linode/linode-docs-theme/layouts/_default/baseof.html b/_vendor/github.com/linode/linode-docs-theme/layouts/_default/baseof.html index 78481ed9d43..7b415990bed 100644 --- a/_vendor/github.com/linode/linode-docs-theme/layouts/_default/baseof.html +++ b/_vendor/github.com/linode/linode-docs-theme/layouts/_default/baseof.html @@ -161,7 +161,7 @@ {{ if eq $mainLayout "content_toc" }}
+ class="main__content relative mb-20 container mx-auto max-w-content xl:max-w-6xl"> @@ -183,6 +183,9 @@ + {{ if site.Params.file_issue_button.enable }} + {{ partial "sections/navigation/file-issue-button.html" . }} + {{ end }}
{{ else }} {{ block "main-section" . }} diff --git a/_vendor/github.com/linode/linode-docs-theme/layouts/_default/whatsnew.html b/_vendor/github.com/linode/linode-docs-theme/layouts/_default/whatsnew.html index d791ace70bb..3196a18ef60 100644 --- a/_vendor/github.com/linode/linode-docs-theme/layouts/_default/whatsnew.html +++ b/_vendor/github.com/linode/linode-docs-theme/layouts/_default/whatsnew.html @@ -9,13 +9,34 @@
{{ .Summary }}
- {{ $deprecated := where site.RegularPages "Params.deprecated" true }} - {{ $pages := where site.RegularPages "Section" "in" (slice "guides" "products" ) | complement $deprecated }} - {{ $pages = $pages.ByLastmod.Reverse | first 20 }} -
- {{ range $pages }} - {{ partial "components/card.html" (dict "page" .) }} - {{ end }} -
+ {{ $guides := site.GetPage "guides" }} + {{ $pages := $guides.RegularPagesRecursive }} + + {{ $pages = $pages.ByLastmod.Reverse }} + {{ range $pages.GroupByLastmod "January 2006" | first 3 }} +

{{ .Key }}

+ + {{ end }} +

+ View All Cloud Guides & Tutorials +

{{ end }} diff --git a/_vendor/github.com/linode/linode-docs-theme/layouts/index.html b/_vendor/github.com/linode/linode-docs-theme/layouts/index.html index 86ca4d87bf5..bfd6a59acc1 100644 --- a/_vendor/github.com/linode/linode-docs-theme/layouts/index.html +++ b/_vendor/github.com/linode/linode-docs-theme/layouts/index.html @@ -362,7 +362,7 @@

  • Product docs
  • @@ -370,7 +370,7 @@

    API reference @@ -381,18 +381,14 @@

    {{ end }} {{ define "home/cards" }} -
    +

    {{ .title }}

    - {{ $numItemsNarrowViewport := 2 }} -
    +
    {{ range $i, $e := .cards }} - {{ $hideOnNarrow := ge $i $numItemsNarrowViewport }}

    Explore more {{ .more_text }} -

    +
    +
    + {{ with .whats_new_page }} + {{ .LinkTitle }} +
    + {{ end }} + {{ .more_text }} +
    + {{ if gt (len .cards) 3 }} + + + + {{ end }} +
    {{ end }}
    {{ end }} {{ define "home/cards-from-section" }} {{ $pages := slice }} + {{ $numPages := cond (eq .page.Section "guides") 9 3 }} {{ if eq .page.Section "reference-architecture" }} {{ $pages = .page.Sections }} {{ else }} {{ $pages = .page.RegularPagesRecursive }} {{ end }} - {{ $featured := $pages | first 3 }} + {{ $featured := $pages | first $numPages }} {{ $cards := slice }} {{ range $featured }} {{ $cards = $cards | append (dict "title" .LinkTitle "text" (or .Params.description .Summary) "href" .RelPermalink) }} {{ end }} - {{ $moreText := .page.Title }} + {{ $moreText := printf "Explore all %s" .page.Title }} + {{ $moreHref := .page.RelPermalink }} + {{ $whatsNewPage := "" }} + {{ if eq .page.Section "guides" }} + {{ $whatsNewPage = site.GetPage "whatsnew" }} + {{ end }} {{ template "home/cards" dict "title" .page.Title "cards" $cards "class" .class "more_text" $moreText "more_href" .page.RelPermalink + "whats_new_page" $whatsNewPage }} {{ end }} diff --git a/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/after-body-start.html b/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/after-body-start.html index 330beb2b7ef..26720b469e6 100644 --- a/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/after-body-start.html +++ b/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/after-body-start.html @@ -5,9 +5,14 @@ {{/* Scripts to be executed as soon as possible. */}} {{- $js := resources.Get "js/body-start/index.js" -}} {{ partial "helpers/script-src.html" (dict "js" $js "nodefer" true "params" (dict "is_production" (ne hugo.Environment "development") ) ) }} +{{ $path := "" }} +{{ if .File }} + {{ $path = .Params.path | default .File.Path }} +{{ end }} {{ $pageInfo := dict "href" .RelPermalink + "path" $path "permalink" .Permalink "kind" .Kind "section" .Section diff --git a/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/head-src.html b/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/head-src.html index 638d408d457..58766f6582d 100644 --- a/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/head-src.html +++ b/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/head-src.html @@ -42,6 +42,7 @@ {{ $cacheWarmerUrls := $algoliaData.urls }} {{ $params := (dict "search_config" site.Params.search_config2 + "file_issue_button" site.Params.file_issue_button "weglot_api_key" site.Params.weglot_api_key "page_title_suffix" site.Params.page_title_suffix "search_cachewarmer_urls" $cacheWarmerUrls diff --git a/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/head.html b/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/head.html index 76d465f3be6..38920841a06 100644 --- a/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/head.html +++ b/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/head.html @@ -30,8 +30,23 @@ {{/* OneTrust Cookies Consent Notice for www.linode.com. Load this before Adobe Analytics. */}} + + diff --git a/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/navigation/file-issue-button.html b/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/navigation/file-issue-button.html new file mode 100644 index 00000000000..aae72d0539f --- /dev/null +++ b/_vendor/github.com/linode/linode-docs-theme/layouts/partials/sections/navigation/file-issue-button.html @@ -0,0 +1,60 @@ + diff --git a/_vendor/github.com/linode/linode-docs-theme/layouts/shortcodes/tabs.html b/_vendor/github.com/linode/linode-docs-theme/layouts/shortcodes/tabs.html index 944740cc6f2..4f69a4015c7 100644 --- a/_vendor/github.com/linode/linode-docs-theme/layouts/shortcodes/tabs.html +++ b/_vendor/github.com/linode/linode-docs-theme/layouts/shortcodes/tabs.html @@ -18,6 +18,7 @@ {{- range (.Scratch.Get "tabsContent") -}}
    {{ .content }}
    diff --git a/_vendor/github.com/linode/linode-docs-theme/tailwind.config.js b/_vendor/github.com/linode/linode-docs-theme/tailwind.config.js index 9f23471856c..09ae3607f83 100644 --- a/_vendor/github.com/linode/linode-docs-theme/tailwind.config.js +++ b/_vendor/github.com/linode/linode-docs-theme/tailwind.config.js @@ -150,8 +150,8 @@ module.exports = { maxWidth: '860px', '@screen tablet': { - paddingLeft: '1.5rem', - paddingRight: '1.5rem', + paddingLeft: '2rem', + paddingRight: '2rem', }, '@screen lg': { diff --git a/_vendor/github.com/linode/linode-website-partials/footer.html b/_vendor/github.com/linode/linode-website-partials/footer.html index 8f1ca424f2a..b5c82ac68d7 100644 --- a/_vendor/github.com/linode/linode-website-partials/footer.html +++ b/_vendor/github.com/linode/linode-website-partials/footer.html @@ -24,7 +24,7 @@
    @@ -36,10 +36,10 @@