github.com/apptainer/singularity@v3.1.1+incompatible/cmd/internal/cli/actions.go (about)

     1  // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package cli
     7  
     8  import (
     9  	"fmt"
    10  	"strings"
    11  
    12  	ocitypes "github.com/containers/image/types"
    13  	"github.com/spf13/cobra"
    14  	"github.com/sylabs/singularity/docs"
    15  	"github.com/sylabs/singularity/internal/pkg/build"
    16  	"github.com/sylabs/singularity/internal/pkg/client/cache"
    17  	ociclient "github.com/sylabs/singularity/internal/pkg/client/oci"
    18  	"github.com/sylabs/singularity/internal/pkg/libexec"
    19  	"github.com/sylabs/singularity/internal/pkg/sylog"
    20  	"github.com/sylabs/singularity/internal/pkg/util/uri"
    21  	"github.com/sylabs/singularity/pkg/build/types"
    22  	library "github.com/sylabs/singularity/pkg/client/library"
    23  )
    24  
    25  func init() {
    26  	actionCmds := []*cobra.Command{
    27  		ExecCmd,
    28  		ShellCmd,
    29  		RunCmd,
    30  		TestCmd,
    31  	}
    32  
    33  	// TODO : the next n lines of code are repeating too much but I don't
    34  	// know how to shorten them tonight
    35  	for _, cmd := range actionCmds {
    36  		cmd.Flags().AddFlag(actionFlags.Lookup("bind"))
    37  		cmd.Flags().AddFlag(actionFlags.Lookup("contain"))
    38  		cmd.Flags().AddFlag(actionFlags.Lookup("containall"))
    39  		cmd.Flags().AddFlag(actionFlags.Lookup("cleanenv"))
    40  		cmd.Flags().AddFlag(actionFlags.Lookup("home"))
    41  		cmd.Flags().AddFlag(actionFlags.Lookup("ipc"))
    42  		cmd.Flags().AddFlag(actionFlags.Lookup("net"))
    43  		cmd.Flags().AddFlag(actionFlags.Lookup("network"))
    44  		cmd.Flags().AddFlag(actionFlags.Lookup("network-args"))
    45  		cmd.Flags().AddFlag(actionFlags.Lookup("dns"))
    46  		cmd.Flags().AddFlag(actionFlags.Lookup("nv"))
    47  		cmd.Flags().AddFlag(actionFlags.Lookup("overlay"))
    48  		cmd.Flags().AddFlag(actionFlags.Lookup("pid"))
    49  		cmd.Flags().AddFlag(actionFlags.Lookup("uts"))
    50  		cmd.Flags().AddFlag(actionFlags.Lookup("pwd"))
    51  		cmd.Flags().AddFlag(actionFlags.Lookup("scratch"))
    52  		cmd.Flags().AddFlag(actionFlags.Lookup("userns"))
    53  		cmd.Flags().AddFlag(actionFlags.Lookup("workdir"))
    54  		cmd.Flags().AddFlag(actionFlags.Lookup("hostname"))
    55  		cmd.Flags().AddFlag(actionFlags.Lookup("fakeroot"))
    56  		cmd.Flags().AddFlag(actionFlags.Lookup("keep-privs"))
    57  		cmd.Flags().AddFlag(actionFlags.Lookup("no-privs"))
    58  		cmd.Flags().AddFlag(actionFlags.Lookup("add-caps"))
    59  		cmd.Flags().AddFlag(actionFlags.Lookup("drop-caps"))
    60  		cmd.Flags().AddFlag(actionFlags.Lookup("allow-setuid"))
    61  		cmd.Flags().AddFlag(actionFlags.Lookup("writable"))
    62  		cmd.Flags().AddFlag(actionFlags.Lookup("writable-tmpfs"))
    63  		cmd.Flags().AddFlag(actionFlags.Lookup("no-home"))
    64  		cmd.Flags().AddFlag(actionFlags.Lookup("no-init"))
    65  		cmd.Flags().AddFlag(actionFlags.Lookup("security"))
    66  		cmd.Flags().AddFlag(actionFlags.Lookup("apply-cgroups"))
    67  		cmd.Flags().AddFlag(actionFlags.Lookup("app"))
    68  		cmd.Flags().AddFlag(actionFlags.Lookup("containlibs"))
    69  		cmd.Flags().AddFlag(actionFlags.Lookup("no-nv"))
    70  		cmd.Flags().AddFlag(actionFlags.Lookup("tmpdir"))
    71  		cmd.Flags().AddFlag(actionFlags.Lookup("nohttps"))
    72  		cmd.Flags().AddFlag(actionFlags.Lookup("docker-username"))
    73  		cmd.Flags().AddFlag(actionFlags.Lookup("docker-password"))
    74  		cmd.Flags().AddFlag(actionFlags.Lookup("docker-login"))
    75  		if cmd == ShellCmd {
    76  			cmd.Flags().AddFlag(actionFlags.Lookup("shell"))
    77  		}
    78  		cmd.Flags().SetInterspersed(false)
    79  	}
    80  
    81  	SingularityCmd.AddCommand(ExecCmd)
    82  	SingularityCmd.AddCommand(ShellCmd)
    83  	SingularityCmd.AddCommand(RunCmd)
    84  	SingularityCmd.AddCommand(TestCmd)
    85  }
    86  
    87  func handleOCI(cmd *cobra.Command, u string) (string, error) {
    88  	authConf, err := makeDockerCredentials(cmd)
    89  	if err != nil {
    90  		sylog.Fatalf("While creating Docker credentials: %v", err)
    91  	}
    92  
    93  	sysCtx := &ocitypes.SystemContext{
    94  		OCIInsecureSkipTLSVerify:    noHTTPS,
    95  		DockerInsecureSkipTLSVerify: noHTTPS,
    96  		DockerAuthConfig:            authConf,
    97  	}
    98  
    99  	sum, err := ociclient.ImageSHA(u, sysCtx)
   100  	if err != nil {
   101  		return "", fmt.Errorf("failed to get SHA of %v: %v", u, err)
   102  	}
   103  
   104  	name := uri.GetName(u)
   105  	imgabs := cache.OciTempImage(sum, name)
   106  
   107  	if exists, err := cache.OciTempExists(sum, name); err != nil {
   108  		return "", fmt.Errorf("unable to check if %v exists: %v", imgabs, err)
   109  	} else if !exists {
   110  		sylog.Infof("Converting OCI blobs to SIF format")
   111  		b, err := build.NewBuild(u, imgabs, "sif", "", "", types.Options{TmpDir: tmpDir, NoTest: true, NoHTTPS: noHTTPS, DockerAuthConfig: authConf})
   112  		if err != nil {
   113  			return "", fmt.Errorf("unable to create new build: %v", err)
   114  		}
   115  
   116  		if err := b.Full(); err != nil {
   117  			return "", fmt.Errorf("unable to build: %v", err)
   118  		}
   119  
   120  		sylog.Infof("Image cached as SIF at %s", imgabs)
   121  	}
   122  
   123  	return imgabs, nil
   124  }
   125  
   126  func handleLibrary(u string) (string, error) {
   127  	libraryImage, err := library.GetImage("https://library.sylabs.io", authToken, u)
   128  	if err != nil {
   129  		return "", err
   130  	}
   131  
   132  	imageName := uri.GetName(u)
   133  	imagePath := cache.LibraryImage(libraryImage.Hash, imageName)
   134  
   135  	if exists, err := cache.LibraryImageExists(libraryImage.Hash, imageName); err != nil {
   136  		return "", fmt.Errorf("unable to check if %v exists: %v", imagePath, err)
   137  	} else if !exists {
   138  		sylog.Infof("Downloading library image")
   139  		if err = library.DownloadImage(imagePath, u, "https://library.sylabs.io", true, authToken); err != nil {
   140  			return "", fmt.Errorf("unable to Download Image: %v", err)
   141  		}
   142  
   143  		if cacheFileHash, err := library.ImageHash(imagePath); err != nil {
   144  			return "", fmt.Errorf("Error getting ImageHash: %v", err)
   145  		} else if cacheFileHash != libraryImage.Hash {
   146  			return "", fmt.Errorf("Cached File Hash(%s) and Expected Hash(%s) does not match", cacheFileHash, libraryImage.Hash)
   147  		}
   148  	}
   149  
   150  	return imagePath, nil
   151  }
   152  
   153  func handleShub(u string) (string, error) {
   154  	imageName := uri.GetName(u)
   155  	imagePath := cache.ShubImage("hash", imageName)
   156  
   157  	libexec.PullShubImage(imagePath, u, true, noHTTPS)
   158  
   159  	return imagePath, nil
   160  }
   161  
   162  func handleNet(u string) (string, error) {
   163  	refParts := strings.Split(u, "/")
   164  	imageName := refParts[len(refParts)-1]
   165  	imagePath := cache.NetImage("hash", imageName)
   166  
   167  	exists, err := cache.NetImageExists("hash", imageName)
   168  	if err != nil {
   169  		return "", fmt.Errorf("unable to check if %v exists: %v", imagePath, err)
   170  	}
   171  	if !exists {
   172  		sylog.Infof("Downloading network image")
   173  		libexec.PullNetImage(imagePath, u, true)
   174  	} else {
   175  		sylog.Infof("Use image from cache")
   176  	}
   177  
   178  	return imagePath, nil
   179  }
   180  
   181  func replaceURIWithImage(cmd *cobra.Command, args []string) {
   182  	// If args[0] is not transport:ref (ex. instance://...) formatted return, not a URI
   183  	t, _ := uri.Split(args[0])
   184  	if t == "instance" || t == "" {
   185  		return
   186  	}
   187  
   188  	var image string
   189  	var err error
   190  
   191  	switch t {
   192  	case uri.Library:
   193  		sylabsToken(cmd, args) // Fetch Auth Token for library access
   194  
   195  		image, err = handleLibrary(args[0])
   196  	case uri.Shub:
   197  		image, err = handleShub(args[0])
   198  	case ociclient.IsSupported(t):
   199  		image, err = handleOCI(cmd, args[0])
   200  	case uri.HTTP:
   201  		image, err = handleNet(args[0])
   202  	case uri.HTTPS:
   203  		image, err = handleNet(args[0])
   204  	default:
   205  		sylog.Fatalf("Unsupported transport type: %s", t)
   206  	}
   207  
   208  	if err != nil {
   209  		sylog.Fatalf("Unable to handle %s uri: %v", args[0], err)
   210  	}
   211  
   212  	args[0] = image
   213  	return
   214  }
   215  
   216  // ExecCmd represents the exec command
   217  var ExecCmd = &cobra.Command{
   218  	DisableFlagsInUseLine: true,
   219  	TraverseChildren:      true,
   220  	Args:                  cobra.MinimumNArgs(2),
   221  	PreRun:                replaceURIWithImage,
   222  	Run: func(cmd *cobra.Command, args []string) {
   223  		a := append([]string{"/.singularity.d/actions/exec"}, args[1:]...)
   224  		execStarter(cmd, args[0], a, "")
   225  	},
   226  
   227  	Use:     docs.ExecUse,
   228  	Short:   docs.ExecShort,
   229  	Long:    docs.ExecLong,
   230  	Example: docs.ExecExamples,
   231  }
   232  
   233  // ShellCmd represents the shell command
   234  var ShellCmd = &cobra.Command{
   235  	DisableFlagsInUseLine: true,
   236  	TraverseChildren:      true,
   237  	Args:                  cobra.MinimumNArgs(1),
   238  	PreRun:                replaceURIWithImage,
   239  	Run: func(cmd *cobra.Command, args []string) {
   240  		a := []string{"/.singularity.d/actions/shell"}
   241  		execStarter(cmd, args[0], a, "")
   242  	},
   243  
   244  	Use:     docs.ShellUse,
   245  	Short:   docs.ShellShort,
   246  	Long:    docs.ShellLong,
   247  	Example: docs.ShellExamples,
   248  }
   249  
   250  // RunCmd represents the run command
   251  var RunCmd = &cobra.Command{
   252  	DisableFlagsInUseLine: true,
   253  	TraverseChildren:      true,
   254  	Args:                  cobra.MinimumNArgs(1),
   255  	PreRun:                replaceURIWithImage,
   256  	Run: func(cmd *cobra.Command, args []string) {
   257  		a := append([]string{"/.singularity.d/actions/run"}, args[1:]...)
   258  		execStarter(cmd, args[0], a, "")
   259  	},
   260  
   261  	Use:     docs.RunUse,
   262  	Short:   docs.RunShort,
   263  	Long:    docs.RunLong,
   264  	Example: docs.RunExamples,
   265  }
   266  
   267  // TestCmd represents the test command
   268  var TestCmd = &cobra.Command{
   269  	DisableFlagsInUseLine: true,
   270  	TraverseChildren:      true,
   271  	Args:                  cobra.MinimumNArgs(1),
   272  	PreRun:                replaceURIWithImage,
   273  	Run: func(cmd *cobra.Command, args []string) {
   274  		a := append([]string{"/.singularity.d/actions/test"}, args[1:]...)
   275  		execStarter(cmd, args[0], a, "")
   276  	},
   277  
   278  	Use:     docs.RunTestUse,
   279  	Short:   docs.RunTestShort,
   280  	Long:    docs.RunTestLong,
   281  	Example: docs.RunTestExample,
   282  }