lxd/db/node.go
func (c *ClusterTx) NodeIsEmpty(id int64) (string, error) {
	// Check if the node has any containers.
	containers, err := query.SelectStrings(c.tx, "SELECT name FROM containers WHERE node_id=?", id)
	if err != nil {
		return "", errors.Wrapf(err, "Failed to get containers for node %d", id)
	}
	if len(containers) > 0 {
		message := fmt.Sprintf(
			"Node still has the following containers: %s", strings.Join(containers, ", "))
		return message, nil
	}

	// Check if the node has any images available only in it.
	images := []struct {
		fingerprint string
		nodeID      int64
	}{}
	dest := func(i int) []interface{} {
		images = append(images, struct {
			fingerprint string
			nodeID      int64
		}{})
		return []interface{}{&images[i].fingerprint, &images[i].nodeID}

	}
	stmt, err := c.tx.Prepare(`
SELECT fingerprint, node_id FROM images JOIN images_nodes ON images.id=images_nodes.image_id`)
	if err != nil {
		return "", err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest)
	if err != nil {
		return "", errors.Wrapf(err, "Failed to get image list for node %d", id)
	}
	index := map[string][]int64{} // Map fingerprints to IDs of nodes
	for _, image := range images {
		index[image.fingerprint] = append(index[image.fingerprint], image.nodeID)
	}

	fingerprints := []string{}
	for fingerprint, ids := range index {
		if len(ids) > 1 {
			continue
		}
		if ids[0] == id {
			fingerprints = append(fingerprints, fingerprint)
		}
	}

	if len(fingerprints) > 0 {
		message := fmt.Sprintf(
			"Node still has the following images: %s", strings.Join(fingerprints, ", "))
		return message, nil
	}

	// Check if the node has any custom volumes.
	volumes, err := query.SelectStrings(
		c.tx, "SELECT name FROM storage_volumes WHERE node_id=? AND type=?",
		id, StoragePoolVolumeTypeCustom)
	if err != nil {
		return "", errors.Wrapf(err, "Failed to get custom volumes for node %d", id)
	}
	if len(volumes) > 0 {
		message := fmt.Sprintf(
			"Node still has the following custom volumes: %s", strings.Join(volumes, ", "))
		return message, nil
	}

	return "", nil
}
func (c *ClusterTx) NodeIsEmpty(id int64) (string, error) {
	// Check if the node has any containers.
	containers, err := query.SelectStrings(c.tx, "SELECT name FROM containers WHERE node_id=?", id)
	if err != nil {
		return "", errors.Wrapf(err, "Failed to get containers for node %d", id)
	}
	if len(containers) > 0 {
		message := fmt.Sprintf(
			"Node still has the following containers: %s", strings.Join(containers, ", "))
		return message, nil
	}

	// Check if the node has any images available only in it.
	images := []struct {
		fingerprint string
		nodeID      int64
	}{}
	dest := func(i int) []interface{} {
		images = append(images, struct {
			fingerprint string
			nodeID      int64
		}{})
		return []interface{}{&images[i].fingerprint, &images[i].nodeID}

	}
	stmt, err := c.tx.Prepare(`
SELECT fingerprint, node_id FROM images JOIN images_nodes ON images.id=images_nodes.image_id`)
	if err != nil {
		return "", err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest)
	if err != nil {
		return "", errors.Wrapf(err, "Failed to get image list for node %d", id)
	}
	index := map[string][]int64{} // Map fingerprints to IDs of nodes
	for _, image := range images {
		index[image.fingerprint] = append(index[image.fingerprint], image.nodeID)
	}

	fingerprints := []string{}
	for fingerprint, ids := range index {
		if len(ids) > 1 {
			continue
		}
		if ids[0] == id {
			fingerprints = append(fingerprints, fingerprint)
		}
	}

	if len(fingerprints) > 0 {
		message := fmt.Sprintf(
			"Node still has the following images: %s", strings.Join(fingerprints, ", "))
		return message, nil
	}

	// Check if the node has any custom volumes.
	volumes, err := query.SelectStrings(
		c.tx, "SELECT name FROM storage_volumes WHERE node_id=? AND type=?",
		id, StoragePoolVolumeTypeCustom)
	if err != nil {
		return "", errors.Wrapf(err, "Failed to get custom volumes for node %d", id)
	}
	if len(volumes) > 0 {
		message := fmt.Sprintf(
			"Node still has the following custom volumes: %s", strings.Join(volumes, ", "))
		return message, nil
	}

	return "", nil
}
// NodeIsEmpty returns an empty string if the node with the given ID has no
// containers or images associated with it. Otherwise, it returns a message
// say what's left.