client/lxd_containers.go
func (r *ProtocolLXD) CopyContainer(source ContainerServer, container api.Container, args *ContainerCopyArgs) (RemoteOperation, error) {
	// Base request
	req := api.ContainersPost{
		Name:         container.Name,
		ContainerPut: container.Writable(),
	}
	req.Source.BaseImage = container.Config["volatile.base_image"]

	// Process the copy arguments
	if args != nil {
		// Sanity checks
		if args.ContainerOnly {
			if !r.HasExtension("container_only_migration") {
				return nil, fmt.Errorf("The target server is missing the required \"container_only_migration\" API extension")
			}

			if !source.HasExtension("container_only_migration") {
				return nil, fmt.Errorf("The source server is missing the required \"container_only_migration\" API extension")
			}
		}

		if shared.StringInSlice(args.Mode, []string{"push", "relay"}) {
			if !r.HasExtension("container_push") {
				return nil, fmt.Errorf("The target server is missing the required \"container_push\" API extension")
			}

			if !source.HasExtension("container_push") {
				return nil, fmt.Errorf("The source server is missing the required \"container_push\" API extension")
			}
		}

		if args.Mode == "push" && !source.HasExtension("container_push_target") {
			return nil, fmt.Errorf("The source server is missing the required \"container_push_target\" API extension")
		}

		if args.Refresh {
			if !r.HasExtension("container_incremental_copy") {
				return nil, fmt.Errorf("The target server is missing the required \"container_incremental_copy\" API extension")
			}

			if !source.HasExtension("container_incremental_copy") {
				return nil, fmt.Errorf("The source server is missing the required \"container_incremental_copy\" API extension")
			}
		}

		// Allow overriding the target name
		if args.Name != "" {
			req.Name = args.Name
		}

		req.Source.Live = args.Live
		req.Source.ContainerOnly = args.ContainerOnly
		req.Source.Refresh = args.Refresh
	}

	if req.Source.Live {
		req.Source.Live = container.StatusCode == api.Running
	}

	sourceInfo, err := source.GetConnectionInfo()
	if err != nil {
		return nil, fmt.Errorf("Failed to get source connection info: %v", err)
	}

	destInfo, err := r.GetConnectionInfo()
	if err != nil {
		return nil, fmt.Errorf("Failed to get destination connection info: %v", err)
	}

	// Optimization for the local copy case
	if destInfo.URL == sourceInfo.URL && destInfo.SocketPath == sourceInfo.SocketPath && (!r.IsClustered() || container.Location == r.clusterTarget || r.HasExtension("cluster_internal_copy")) {
		// Project handling
		if destInfo.Project != sourceInfo.Project {
			if !r.HasExtension("container_copy_project") {
				return nil, fmt.Errorf("The server is missing the required \"container_copy_project\" API extension")
			}

			req.Source.Project = sourceInfo.Project
		}

		// Local copy source fields
		req.Source.Type = "copy"
		req.Source.Source = container.Name

		// Copy the container
		op, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}

		rop := remoteOperation{
			targetOp: op,
			chDone:   make(chan bool),
		}

		// Forward targetOp to remote op
		go func() {
			rop.err = rop.targetOp.Wait()
			close(rop.chDone)
		}()

		return &rop, nil
	}

	// Source request
	sourceReq := api.ContainerPost{
		Migration:     true,
		Live:          req.Source.Live,
		ContainerOnly: req.Source.ContainerOnly,
	}

	// Push mode migration
	if args != nil && args.Mode == "push" {
		// Get target server connection information
		info, err := r.GetConnectionInfo()
		if err != nil {
			return nil, err
		}

		// Create the container
		req.Source.Type = "migration"
		req.Source.Mode = "push"
		req.Source.Refresh = args.Refresh

		op, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}

		opAPI := op.Get()

		targetSecrets := map[string]string{}
		for k, v := range opAPI.Metadata {
			targetSecrets[k] = v.(string)
		}

		// Prepare the source request
		target := api.ContainerPostTarget{}
		target.Operation = opAPI.ID
		target.Websockets = targetSecrets
		target.Certificate = info.Certificate
		sourceReq.Target = &target

		return r.tryMigrateContainer(source, container.Name, sourceReq, info.Addresses)
	}

	// Get source server connection information
	info, err := source.GetConnectionInfo()
	if err != nil {
		return nil, err
	}

	op, err := source.MigrateContainer(container.Name, sourceReq)
	if err != nil {
		return nil, err
	}
	opAPI := op.Get()

	sourceSecrets := map[string]string{}
	for k, v := range opAPI.Metadata {
		sourceSecrets[k] = v.(string)
	}

	// Relay mode migration
	if args != nil && args.Mode == "relay" {
		// Push copy source fields
		req.Source.Type = "migration"
		req.Source.Mode = "push"

		// Start the process
		targetOp, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}
		targetOpAPI := targetOp.Get()

		// Extract the websockets
		targetSecrets := map[string]string{}
		for k, v := range targetOpAPI.Metadata {
			targetSecrets[k] = v.(string)
		}

		// Launch the relay
		err = r.proxyMigration(targetOp.(*operation), targetSecrets, source, op.(*operation), sourceSecrets)
		if err != nil {
			return nil, err
		}

		// Prepare a tracking operation
		rop := remoteOperation{
			targetOp: targetOp,
			chDone:   make(chan bool),
		}

		// Forward targetOp to remote op
		go func() {
			rop.err = rop.targetOp.Wait()
			close(rop.chDone)
		}()

		return &rop, nil
	}

	// Pull mode migration
	req.Source.Type = "migration"
	req.Source.Mode = "pull"
	req.Source.Operation = opAPI.ID
	req.Source.Websockets = sourceSecrets
	req.Source.Certificate = info.Certificate

	return r.tryCreateContainer(req, info.Addresses)
}
func (r *ProtocolLXD) CopyContainer(source ContainerServer, container api.Container, args *ContainerCopyArgs) (RemoteOperation, error) {
	// Base request
	req := api.ContainersPost{
		Name:         container.Name,
		ContainerPut: container.Writable(),
	}
	req.Source.BaseImage = container.Config["volatile.base_image"]

	// Process the copy arguments
	if args != nil {
		// Sanity checks
		if args.ContainerOnly {
			if !r.HasExtension("container_only_migration") {
				return nil, fmt.Errorf("The target server is missing the required \"container_only_migration\" API extension")
			}

			if !source.HasExtension("container_only_migration") {
				return nil, fmt.Errorf("The source server is missing the required \"container_only_migration\" API extension")
			}
		}

		if shared.StringInSlice(args.Mode, []string{"push", "relay"}) {
			if !r.HasExtension("container_push") {
				return nil, fmt.Errorf("The target server is missing the required \"container_push\" API extension")
			}

			if !source.HasExtension("container_push") {
				return nil, fmt.Errorf("The source server is missing the required \"container_push\" API extension")
			}
		}

		if args.Mode == "push" && !source.HasExtension("container_push_target") {
			return nil, fmt.Errorf("The source server is missing the required \"container_push_target\" API extension")
		}

		if args.Refresh {
			if !r.HasExtension("container_incremental_copy") {
				return nil, fmt.Errorf("The target server is missing the required \"container_incremental_copy\" API extension")
			}

			if !source.HasExtension("container_incremental_copy") {
				return nil, fmt.Errorf("The source server is missing the required \"container_incremental_copy\" API extension")
			}
		}

		// Allow overriding the target name
		if args.Name != "" {
			req.Name = args.Name
		}

		req.Source.Live = args.Live
		req.Source.ContainerOnly = args.ContainerOnly
		req.Source.Refresh = args.Refresh
	}

	if req.Source.Live {
		req.Source.Live = container.StatusCode == api.Running
	}

	sourceInfo, err := source.GetConnectionInfo()
	if err != nil {
		return nil, fmt.Errorf("Failed to get source connection info: %v", err)
	}

	destInfo, err := r.GetConnectionInfo()
	if err != nil {
		return nil, fmt.Errorf("Failed to get destination connection info: %v", err)
	}

	// Optimization for the local copy case
	if destInfo.URL == sourceInfo.URL && destInfo.SocketPath == sourceInfo.SocketPath && (!r.IsClustered() || container.Location == r.clusterTarget || r.HasExtension("cluster_internal_copy")) {
		// Project handling
		if destInfo.Project != sourceInfo.Project {
			if !r.HasExtension("container_copy_project") {
				return nil, fmt.Errorf("The server is missing the required \"container_copy_project\" API extension")
			}

			req.Source.Project = sourceInfo.Project
		}

		// Local copy source fields
		req.Source.Type = "copy"
		req.Source.Source = container.Name

		// Copy the container
		op, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}

		rop := remoteOperation{
			targetOp: op,
			chDone:   make(chan bool),
		}

		// Forward targetOp to remote op
		go func() {
			rop.err = rop.targetOp.Wait()
			close(rop.chDone)
		}()

		return &rop, nil
	}

	// Source request
	sourceReq := api.ContainerPost{
		Migration:     true,
		Live:          req.Source.Live,
		ContainerOnly: req.Source.ContainerOnly,
	}

	// Push mode migration
	if args != nil && args.Mode == "push" {
		// Get target server connection information
		info, err := r.GetConnectionInfo()
		if err != nil {
			return nil, err
		}

		// Create the container
		req.Source.Type = "migration"
		req.Source.Mode = "push"
		req.Source.Refresh = args.Refresh

		op, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}

		opAPI := op.Get()

		targetSecrets := map[string]string{}
		for k, v := range opAPI.Metadata {
			targetSecrets[k] = v.(string)
		}

		// Prepare the source request
		target := api.ContainerPostTarget{}
		target.Operation = opAPI.ID
		target.Websockets = targetSecrets
		target.Certificate = info.Certificate
		sourceReq.Target = &target

		return r.tryMigrateContainer(source, container.Name, sourceReq, info.Addresses)
	}

	// Get source server connection information
	info, err := source.GetConnectionInfo()
	if err != nil {
		return nil, err
	}

	op, err := source.MigrateContainer(container.Name, sourceReq)
	if err != nil {
		return nil, err
	}
	opAPI := op.Get()

	sourceSecrets := map[string]string{}
	for k, v := range opAPI.Metadata {
		sourceSecrets[k] = v.(string)
	}

	// Relay mode migration
	if args != nil && args.Mode == "relay" {
		// Push copy source fields
		req.Source.Type = "migration"
		req.Source.Mode = "push"

		// Start the process
		targetOp, err := r.CreateContainer(req)
		if err != nil {
			return nil, err
		}
		targetOpAPI := targetOp.Get()

		// Extract the websockets
		targetSecrets := map[string]string{}
		for k, v := range targetOpAPI.Metadata {
			targetSecrets[k] = v.(string)
		}

		// Launch the relay
		err = r.proxyMigration(targetOp.(*operation), targetSecrets, source, op.(*operation), sourceSecrets)
		if err != nil {
			return nil, err
		}

		// Prepare a tracking operation
		rop := remoteOperation{
			targetOp: targetOp,
			chDone:   make(chan bool),
		}

		// Forward targetOp to remote op
		go func() {
			rop.err = rop.targetOp.Wait()
			close(rop.chDone)
		}()

		return &rop, nil
	}

	// Pull mode migration
	req.Source.Type = "migration"
	req.Source.Mode = "pull"
	req.Source.Operation = opAPI.ID
	req.Source.Websockets = sourceSecrets
	req.Source.Certificate = info.Certificate

	return r.tryCreateContainer(req, info.Addresses)
}
func ( r * ProtocolLXD ) CopyContainer ( source ContainerServer , container api . Container , args * ContainerCopyArgs ) ( RemoteOperation , error ) { // Base request req := api . ContainersPost { Name : container . Name , ContainerPut : container . Writable ( ) , } req . Source . BaseImage = container . Config [ " " ] // Process the copy arguments if args != nil { // Sanity checks if args . ContainerOnly { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } if ! source . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } } if shared . StringInSlice ( args . Mode , [ ] string { " " , " " } ) { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } if ! source . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } } if args . Mode == " " && ! source . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } if args . Refresh { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } if ! source . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } } // Allow overriding the target name if args . Name != " " { req . Name = args . Name } req . Source . Live = args . Live req . Source . ContainerOnly = args . ContainerOnly req . Source . Refresh = args . Refresh } if req . Source . Live { req . Source . Live = container . StatusCode == api . Running } sourceInfo , err := source . GetConnectionInfo ( ) if err != nil { return nil , fmt . Errorf ( " " , err ) } destInfo , err := r . GetConnectionInfo ( ) if err != nil { return nil , fmt . Errorf ( " " , err ) } // Optimization for the local copy case if destInfo . URL == sourceInfo . URL && destInfo . SocketPath == sourceInfo . SocketPath && ( ! r . IsClustered ( ) || container . Location == r . clusterTarget || r . HasExtension ( " " ) ) { // Project handling if destInfo . Project != sourceInfo . Project { if ! r . HasExtension ( " " ) { return nil , fmt . Errorf ( " \" \" " ) } req . Source . Project = sourceInfo . Project } // Local copy source fields req . Source . Type = " " req . Source . Source = container . Name // Copy the container op , err := r . CreateContainer ( req ) if err != nil { return nil , err } rop := remoteOperation { targetOp : op , chDone : make ( chan bool ) , } // Forward targetOp to remote op go func ( ) { rop . err = rop . targetOp . Wait ( ) close ( rop . chDone ) } ( ) return & rop , nil } // Source request sourceReq := api . ContainerPost { Migration : true , Live : req . Source . Live , ContainerOnly : req . Source . ContainerOnly , } // Push mode migration if args != nil && args . Mode == " " { // Get target server connection information info , err := r . GetConnectionInfo ( ) if err != nil { return nil , err } // Create the container req . Source . Type = " " req . Source . Mode = " " req . Source . Refresh = args . Refresh op , err := r . CreateContainer ( req ) if err != nil { return nil , err } opAPI := op . Get ( ) targetSecrets := map [ string ] string { } for k , v := range opAPI . Metadata { targetSecrets [ k ] = v . ( string ) } // Prepare the source request target := api . ContainerPostTarget { } target . Operation = opAPI . ID target . Websockets = targetSecrets target . Certificate = info . Certificate sourceReq . Target = & target return r . tryMigrateContainer ( source , container . Name , sourceReq , info . Addresses ) } // Get source server connection information info , err := source . GetConnectionInfo ( ) if err != nil { return nil , err } op , err := source . MigrateContainer ( container . Name , sourceReq ) if err != nil { return nil , err } opAPI := op . Get ( ) sourceSecrets := map [ string ] string { } for k , v := range opAPI . Metadata { sourceSecrets [ k ] = v . ( string ) } // Relay mode migration if args != nil && args . Mode == " " { // Push copy source fields req . Source . Type = " " req . Source . Mode = " " // Start the process targetOp , err := r . CreateContainer ( req ) if err != nil { return nil , err } targetOpAPI := targetOp . Get ( ) // Extract the websockets targetSecrets := map [ string ] string { } for k , v := range targetOpAPI . Metadata { targetSecrets [ k ] = v . ( string ) } // Launch the relay err = r . proxyMigration ( targetOp . ( * operation ) , targetSecrets , source , op . ( * operation ) , sourceSecrets ) if err != nil { return nil , err } // Prepare a tracking operation rop := remoteOperation { targetOp : targetOp , chDone : make ( chan bool ) , } // Forward targetOp to remote op go func ( ) { rop . err = rop . targetOp . Wait ( ) close ( rop . chDone ) } ( ) return & rop , nil } // Pull mode migration req . Source . Type = " " req . Source . Mode = " " req . Source . Operation = opAPI . ID req . Source . Websockets = sourceSecrets req . Source . Certificate = info . Certificate return r . tryCreateContainer ( req , info . Addresses ) }
// CopyContainer copies a container from a remote server. Additional options can be passed using ContainerCopyArgs
CopyContainer copies a container from a remote server . Additional options can be passed using ContainerCopyArgs