Skip to content

Improve code readability for beginners by adding comments to clarify complex logic on package main #539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 47 additions & 21 deletions examples/cmd/snapshotting/example_demo.go
Original file line number Diff line number Diff line change
@@ -11,6 +11,15 @@
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

// Package main provides a Go program for managing snapshots of Firecracker microVMs.
// The program utilizes the Firecracker Go SDK for creating and loading snapshots,
// and it demonstrates how to establish SSH connections to interact with microVMs.
// Comments are provided to explain each function's purpose and usage.

// In this program, a "snapshot" refers to a point-in-time copy of the state of a Firecracker microVM.
// Snapshots capture the complete memory and state of the microVM, allowing users to save and restore its exact configuration and execution context.
// They enable quick deployment and management of microVM instances with pre-defined configurations and states, which is useful for testing, development, and deployment scenarios.

Comment on lines +14 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package main

import (
@@ -31,19 +40,21 @@ import (
models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
)

// Constants for CNI configuration
const (
// Using default cache directory to ensure collision avoidance on IP allocations
cniCacheDir = "/var/lib/cni"
networkName = "fcnet"
ifName = "veth0"
cniCacheDir = "/var/lib/cni" // Default cache directory for IP allocations
networkName = "fcnet" // Name of the network
ifName = "veth0" // Interface name

networkMask string = "/24"
subnet string = "10.168.0.0" + networkMask
networkMask = "/24" // Subnet mask
subnet = "10.168.0.0" + networkMask

maxRetries int = 10
backoffTimeMs time.Duration = 500
maxRetries = 10 // Maximum number of retries for SSH connection
backoffTimeMs = 500 * time.Millisecond // Backoff time for retries
)

// writeCNIConfWithHostLocalSubnet writes CNI configuration to a file with a host-local subnet
func writeCNIConfWithHostLocalSubnet(path, networkName, subnet string) error {
return ioutil.WriteFile(path, []byte(fmt.Sprintf(`{
"cniVersion": "0.3.1",
@@ -63,14 +74,17 @@ func writeCNIConfWithHostLocalSubnet(path, networkName, subnet string) error {
}`, networkName, subnet)), 0644)
}

// configOpt is a functional option for configuring the Firecracker microVM
type configOpt func(*sdk.Config)

// withNetworkInterface adds a network interface configuration option to the Firecracker microVM config
func withNetworkInterface(networkInterface sdk.NetworkInterface) configOpt {
return func(c *sdk.Config) {
c.NetworkInterfaces = append(c.NetworkInterfaces, networkInterface)
}
}

// createNewConfig creates a new Firecracker microVM configuration
func createNewConfig(socketPath string, opts ...configOpt) sdk.Config {
dir, _ := os.Getwd()
fmt.Println(dir)
@@ -110,6 +124,7 @@ func createNewConfig(socketPath string, opts ...configOpt) sdk.Config {
return cfg
}

// connectToVM establishes an SSH connection to the Firecracker microVM
func connectToVM(m *sdk.Machine, sshKeyPath string) (*ssh.Client, error) {
key, err := ioutil.ReadFile(sshKeyPath)
if err != nil {
@@ -139,6 +154,7 @@ func connectToVM(m *sdk.Machine, sshKeyPath string) (*ssh.Client, error) {
return ssh.Dial("tcp", fmt.Sprintf("%s:22", ip), config)
}

// createSnapshotSSH creates a snapshot of a Firecracker microVM and returns the IP of the VM
func createSnapshotSSH(ctx context.Context, socketPath, memPath, snapPath string) string {
dir, err := os.Getwd()
if err != nil {
@@ -246,6 +262,7 @@ func createSnapshotSSH(ctx context.Context, socketPath, memPath, snapPath string
return vmIP
}

// loadSnapshotSSH loads a snapshot of the Firecracker microVM using SSH
func loadSnapshotSSH(ctx context.Context, socketPath, memPath, snapPath, ipToRestore string) {
var ipFreed bool = false
var err error
@@ -383,48 +400,57 @@ func loadSnapshotSSH(ctx context.Context, socketPath, memPath, snapPath, ipToRes

func main() {
// Check for kvm and root access
err := unix.Access("/dev/kvm", unix.W_OK)
if err != nil {
err := unix.Access("/dev/kvm", unix.W_OK) // Check if the program has write access to /dev/kvm
Copy link
Contributor

@swagatbora90 swagatbora90 May 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the code here is self-explanatory and/or understandable with a quick search. I would prefer to have more readable code, than annotate every line of code with comments.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your kind word.

if err != nil { // If there's an error (e.g., access denied), log and exit
log.Fatal(err)
}

// Check if the program is running with root privileges
if x, y := 0, os.Getuid(); x != y {
log.Fatal("Root acccess denied")
}

// Get the current working directory
dir, err := os.Getwd()
if err != nil {
if err != nil { // If there's an error getting the working directory, log and exit
log.Fatal(err)
}

// Create a directory for CNI configuration files
cniConfDir := filepath.Join(dir, "cni.conf")
err = os.Mkdir(cniConfDir, 0777)
if err != nil {
err = os.Mkdir(cniConfDir, 0777) // Create the directory with full permissions
if err != nil { // If there's an error creating the directory, log and exit
log.Fatal(err)
}
defer os.Remove(cniConfDir)
defer os.Remove(cniConfDir) // Remove the directory when main function exits

// Setup socket and snapshot + memory paths
tempdir, err := ioutil.TempDir("", "FCGoSDKSnapshotExample")
if err != nil {
// Setup temporary directory and paths for socket, snapshot, and memory files
tempdir, err := ioutil.TempDir("", "FCGoSDKSnapshotExample") // Create a temporary directory
if err != nil { // If there's an error creating the temporary directory, log and exit
log.Fatal(err)
}
defer os.Remove(tempdir)
socketPath := filepath.Join(tempdir, "snapshotssh")
defer os.Remove(tempdir) // Remove the temporary directory when main function exits
socketPath := filepath.Join(tempdir, "snapshotssh") // Create a socket path within the temporary directory

// Create a directory for snapshot and memory files
snapshotsshPath := filepath.Join(dir, "snapshotssh")
err = os.Mkdir(snapshotsshPath, 0777)
if err != nil && !errors.Is(err, os.ErrExist) {
err = os.Mkdir(snapshotsshPath, 0777) // Create the directory with full permissions
if err != nil && !errors.Is(err, os.ErrExist) { // If there's an error creating the directory and it's not already exist, log and exit
log.Fatal(err)
}
defer os.RemoveAll(snapshotsshPath)
defer os.RemoveAll(snapshotsshPath) // Remove the directory and its contents when main function exits

// Set paths for snapshot and memory files
snapPath := filepath.Join(snapshotsshPath, "SnapFile")
memPath := filepath.Join(snapshotsshPath, "MemFile")

// Create a background context
ctx := context.Background()

// Create a snapshot of the Firecracker microVM and get the IP address to restore
ipToRestore := createSnapshotSSH(ctx, socketPath, memPath, snapPath)
fmt.Println()

// Load the snapshot of the Firecracker microVM
loadSnapshotSSH(ctx, socketPath, memPath, snapPath, ipToRestore)
}