1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
package zone
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/lxc/incus/v6/internal/server/db"
"github.com/lxc/incus/v6/internal/server/db/cluster"
"github.com/lxc/incus/v6/internal/server/state"
"github.com/lxc/incus/v6/shared/api"
"github.com/lxc/incus/v6/shared/util"
)
// LoadByName loads and initializes a Network zone from the database by name.
func LoadByName(s *state.State, name string) (NetworkZone, error) {
var dbZone []cluster.NetworkZone
var zoneInfo *api.NetworkZone
err := s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
var err error
filter := cluster.NetworkZoneFilter{Name: &name}
dbZone, err = cluster.GetNetworkZones(ctx, tx.Tx(), filter)
if err != nil {
return err
}
if len(dbZone) != 1 {
return fmt.Errorf("Loading network zone named %s returned an unexpected amount of results: %d", name, len(dbZone))
}
zoneInfo, err = dbZone[0].ToAPI(ctx, tx.Tx())
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
var zone NetworkZone = &zone{}
zone.init(s, int64(dbZone[0].ID), dbZone[0].Project, zoneInfo)
return zone, nil
}
// LoadByNameAndProject loads and initializes a Network zone from the database by project and name.
func LoadByNameAndProject(s *state.State, projectName string, name string) (NetworkZone, error) {
var dbZone *cluster.NetworkZone
var zoneInfo *api.NetworkZone
err := s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
var err error
filter := cluster.NetworkZoneFilter{
Project: &projectName,
Name: &name,
}
zones, err := cluster.GetNetworkZones(ctx, tx.Tx(), filter)
if err != nil {
return err
}
if len(zones) == 0 {
return api.StatusErrorf(http.StatusNotFound, "Network zone not found")
}
dbZone = &zones[0]
zoneInfo, err = dbZone.ToAPI(ctx, tx.Tx())
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
var zone NetworkZone = &zone{}
zone.init(s, int64(dbZone.ID), projectName, zoneInfo)
return zone, nil
}
// Create validates supplied record and creates new Network zone record in the database.
func Create(s *state.State, projectName string, zoneInfo *api.NetworkZonesPost) error {
var zone NetworkZone = &zone{}
zone.init(s, -1, projectName, nil)
err := zone.validateName(zoneInfo.Name)
if err != nil {
return err
}
err = zone.validateConfig(&zoneInfo.NetworkZonePut)
if err != nil {
return err
}
// Load the project.
var p *api.Project
err = s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
project, err := cluster.GetProject(ctx, tx.Tx(), projectName)
if err != nil {
return err
}
p, err = project.ToAPI(ctx, tx.Tx())
return err
})
if err != nil {
return err
}
// Validate restrictions.
if util.IsTrue(p.Config["restricted"]) {
found := false
for _, entry := range strings.Split(p.Config["restricted.networks.zones"], ",") {
entry = strings.TrimSpace(entry)
if zoneInfo.Name == entry || strings.HasSuffix(zoneInfo.Name, "."+entry) {
found = true
break
}
}
if !found {
return api.StatusErrorf(http.StatusForbidden, "Project isn't allowed to use this DNS zone")
}
}
err = s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
dbZone := cluster.NetworkZone{
Project: projectName,
Name: zoneInfo.Name,
Description: zoneInfo.Description,
}
id, err := cluster.CreateNetworkZone(ctx, tx.Tx(), dbZone)
if err != nil {
return err
}
err = cluster.CreateNetworkZoneConfig(ctx, tx.Tx(), id, zoneInfo.Config)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// Trigger a refresh of the TSIG entries.
err = s.DNS.UpdateTSIG()
if err != nil {
return err
}
return nil
}
// Exists checks the zone name(s) provided exists.
// If multiple names are provided, also checks that duplicate names aren't specified in the list.
func Exists(s *state.State, name ...string) error {
checkedzoneNames := make(map[string]struct{}, len(name))
err := s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
for _, zoneName := range name {
filter := cluster.NetworkZoneFilter{Name: &zoneName}
dbZone, err := cluster.GetNetworkZones(ctx, tx.Tx(), filter)
if err != nil {
return err
}
if len(dbZone) != 1 {
return fmt.Errorf("Loading network zone named %s returned an unexpected amount of results: %d", zoneName, len(dbZone))
}
status, err := cluster.NetworkZoneExists(ctx, tx.Tx(), dbZone[0].Project, zoneName)
if !status {
return fmt.Errorf("Network zone %q does not exist", zoneName)
}
if err != nil {
return fmt.Errorf("Error when checking existence of %q network zone in project %q", zoneName, dbZone[0].Project)
}
_, found := checkedzoneNames[zoneName]
if found {
return fmt.Errorf("Network zone %q specified multiple times", zoneName)
}
checkedzoneNames[zoneName] = struct{}{}
}
return nil
})
if err != nil {
return err
}
return nil
}
|