Skip to content

Commit

Permalink
WIP - DO NOT MERGE.
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Laing <mark.laing@canonical.com>
  • Loading branch information
markylaing committed Oct 30, 2023
1 parent a5dfac4 commit 77fd6a6
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 34 deletions.
13 changes: 13 additions & 0 deletions lxd/auth/driver_tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,18 @@ func (t *tls) certificateDetails(fingerprint string) (certificate.Type, bool, []
return certificate.TypeMetrics, false, nil, nil
}

// If not a client cert or a metrics cert, could be a deployment cert.
deploymentCerts := certs[certificate.TypeDeployments]
_, ok = deploymentCerts[fingerprint]
if ok {
projectNames, ok := projects[fingerprint]
if !ok {
// Certificate is not restricted.
return certificate.TypeClient, true, nil, nil
}

return certificate.TypeDeployments, false, projectNames, nil
}

return -1, false, nil, api.StatusErrorf(http.StatusForbidden, "Client certificate not found")
}
49 changes: 24 additions & 25 deletions lxd/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (

"github.com/gorilla/mux"

"github.com/canonical/lxd/lxd/auth"
"github.com/canonical/lxd/lxd/db"
dbCluster "github.com/canonical/lxd/lxd/db/cluster"
"github.com/canonical/lxd/lxd/deployments"
Expand All @@ -29,69 +28,69 @@ import (
var deploymentsCmd = APIEndpoint{
Path: "deployments",

Get: APIEndpointAction{Handler: deploymentsGet, AccessHandler: allowPermission(auth.ObjectTypeDeployment, auth.EntitlementCanView)},
Post: APIEndpointAction{Handler: deploymentsPost, AccessHandler: allowPermission(auth.ObjectTypeDeployment, auth.EntitlementCanEdit)},
Get: APIEndpointAction{Handler: deploymentsGet, AccessHandler: allowAuthenticated},
Post: APIEndpointAction{Handler: deploymentsPost, AccessHandler: allowAuthenticated},
}

var deploymentCmd = APIEndpoint{
Path: "deployments/{deploymentName}",

Delete: APIEndpointAction{Handler: deploymentDelete, AccessHandler: allowPermission(auth.ObjectTypeDeployment, auth.EntitlementCanEdit, "deploymentName")},
Get: APIEndpointAction{Handler: deploymentGet, AccessHandler: allowPermission(auth.ObjectTypeDeployment, auth.EntitlementCanView, "deploymentName")},
Put: APIEndpointAction{Handler: deploymentPut, AccessHandler: allowPermission(auth.ObjectTypeDeployment, auth.EntitlementCanEdit, "deploymentName")},
Patch: APIEndpointAction{Handler: deploymentPut, AccessHandler: allowPermission(auth.ObjectTypeDeployment, auth.EntitlementCanEdit, "deploymentName")},
Post: APIEndpointAction{Handler: deploymentPost, AccessHandler: allowPermission(auth.ObjectTypeDeployment, auth.EntitlementCanEdit, "deploymentName")},
Delete: APIEndpointAction{Handler: deploymentDelete, AccessHandler: allowAuthenticated},
Get: APIEndpointAction{Handler: deploymentGet, AccessHandler: allowAuthenticated},
Put: APIEndpointAction{Handler: deploymentPut, AccessHandler: allowAuthenticated},
Patch: APIEndpointAction{Handler: deploymentPut, AccessHandler: allowAuthenticated},
Post: APIEndpointAction{Handler: deploymentPost, AccessHandler: allowAuthenticated},
}

var deploymentKeysCmd = APIEndpoint{
Path: "deployments/{deploymentName}/keys",

Get: APIEndpointAction{Handler: deploymentKeysGet, AccessHandler: allowPermission(auth.ObjectTypeDeploymentKey, auth.EntitlementCanView, "deploymentName")},
Post: APIEndpointAction{Handler: deploymentKeysPost, AccessHandler: allowPermission(auth.ObjectTypeDeploymentKey, auth.EntitlementCanEdit, "deploymentName")},
Get: APIEndpointAction{Handler: deploymentKeysGet, AccessHandler: allowAuthenticated},
Post: APIEndpointAction{Handler: deploymentKeysPost, AccessHandler: allowAuthenticated},
}

var deploymentKeyCmd = APIEndpoint{
Path: "deployments/{deploymentName}/keys/{deploymentKeyName}",

Delete: APIEndpointAction{Handler: deploymentKeyDelete, AccessHandler: allowPermission(auth.ObjectTypeDeploymentKey, auth.EntitlementCanEdit, "deploymentName", "deploymentKeyName")},
Get: APIEndpointAction{Handler: deploymentKeyGet, AccessHandler: allowPermission(auth.ObjectTypeDeploymentKey, auth.EntitlementCanView, "deploymentName", "deploymentKeyName")},
Put: APIEndpointAction{Handler: deploymentKeyPut, AccessHandler: allowPermission(auth.ObjectTypeDeploymentKey, auth.EntitlementCanEdit, "deploymentName", "deploymentKeyName")},
Post: APIEndpointAction{Handler: deploymentKeyPost, AccessHandler: allowPermission(auth.ObjectTypeDeploymentKey, auth.EntitlementCanEdit, "deploymentName", "deploymentKeyName")},
Delete: APIEndpointAction{Handler: deploymentKeyDelete, AccessHandler: allowAuthenticated},
Get: APIEndpointAction{Handler: deploymentKeyGet, AccessHandler: allowAuthenticated},
Put: APIEndpointAction{Handler: deploymentKeyPut, AccessHandler: allowAuthenticated},
Post: APIEndpointAction{Handler: deploymentKeyPost, AccessHandler: allowAuthenticated},
}

var deploymentShapesCmd = APIEndpoint{
Path: "deployments/{deploymentName}/shapes",

Get: APIEndpointAction{Handler: deploymentShapesGet, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShape, auth.EntitlementCanView, "deploymentName")},
Post: APIEndpointAction{Handler: deploymentShapesPost, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShape, auth.EntitlementCanEdit, "deploymentName")},
Get: APIEndpointAction{Handler: deploymentShapesGet, AccessHandler: allowAuthenticated},
Post: APIEndpointAction{Handler: deploymentShapesPost, AccessHandler: allowAuthenticated},
}

var deploymentShapeCmd = APIEndpoint{
Path: "deployments/{deploymentName}/shapes/{deploymentShapeName}",

Delete: APIEndpointAction{Handler: deploymentShapeDelete, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShape, auth.EntitlementCanEdit, "deploymentName", "deploymentShapeName")},
Get: APIEndpointAction{Handler: deploymentShapeGet, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShape, auth.EntitlementCanView, "deploymentName", "deploymentShapeName")},
Put: APIEndpointAction{Handler: deploymentShapePut, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShape, auth.EntitlementCanEdit, "deploymentName", "deploymentShapeName")},
Post: APIEndpointAction{Handler: deploymentShapePost, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShape, auth.EntitlementCanEdit, "deploymentName", "deploymentShapeName")},
Delete: APIEndpointAction{Handler: deploymentShapeDelete, AccessHandler: allowAuthenticated},
Get: APIEndpointAction{Handler: deploymentShapeGet, AccessHandler: allowAuthenticated},
Put: APIEndpointAction{Handler: deploymentShapePut, AccessHandler: allowAuthenticated},
Post: APIEndpointAction{Handler: deploymentShapePost, AccessHandler: allowAuthenticated},
}

var deploymentShapeInstancesCmd = APIEndpoint{
Path: "deployments/{deploymentName}/shapes/{deploymentShapeName}/instances",

Get: APIEndpointAction{Handler: deploymentInstancesGet, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShapeInstance, auth.EntitlementCanView, "deploymentName", "deploymentShapeName")},
Post: APIEndpointAction{Handler: deploymentInstancesPost, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShapeInstance, auth.EntitlementCanEdit, "deploymentName", "deploymentShapeName")},
Get: APIEndpointAction{Handler: deploymentInstancesGet, AccessHandler: allowAuthenticated},
Post: APIEndpointAction{Handler: deploymentInstancesPost, AccessHandler: allowAuthenticated},
}

var deployedShapeInstancesCmd = APIEndpoint{
Path: "deployments/{deploymentName}/shapes/{deploymentShapeName}/instances/{name}",

Delete: APIEndpointAction{Handler: deploymentInstanceDelete, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShapeInstance, auth.EntitlementCanEdit, "deploymentName", "deploymentShapeName", "name")},
Delete: APIEndpointAction{Handler: deploymentInstanceDelete, AccessHandler: allowAuthenticated},
}

var deployedShapeInstancesStateCmd = APIEndpoint{
Path: "deployments/{deploymentName}/shapes/{deploymentShapeName}/instances/{name}/state",

Put: APIEndpointAction{Handler: deploymentInstanceState, AccessHandler: allowPermission(auth.ObjectTypeDeploymentShapeInstance, auth.EntitlementCanEdit, "deploymentName", "deploymentShapeName", "name")},
Put: APIEndpointAction{Handler: deploymentInstanceState, AccessHandler: allowAuthenticated},
}

// API endpoints.
Expand Down Expand Up @@ -1092,7 +1091,7 @@ func deploymentKeyPost(d *Daemon, r *http.Request) response.Response {
return response.SmartError(err)
}

lc := lifecycle.DeploymentKeyRenamed.Event(projectName, deploymentName, deploymentKeyName, request.CreateRequestor(r), logger.Ctx{"old_name": deploymentName})
lc := lifecycle.DeploymentKeyRenamed.Event(projectName, deploymentName, deploymentKeyName, request.CreateRequestor(r), logger.Ctx{"old_name": deploymentKeyName})
s.Events.SendLifecycle(projectName, lc)

return response.SyncResponseLocation(true, nil, lc.Source)
Expand Down
18 changes: 17 additions & 1 deletion lxd/deployments/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package deployments
import (
"context"
"fmt"
"net/http"
"strings"

"github.com/canonical/lxd/lxd/db"
Expand Down Expand Up @@ -329,7 +330,22 @@ func (d *Deployment) RenameDeploymentKey(newName string) error {
}

err = d.state.DB.Cluster.Transaction(d.state.ShutdownCtx, func(ctx context.Context, tx *db.ClusterTx) error {
return tx.RenameDeploymentKey(ctx, d.id, newName)
keys, err := tx.GetDeploymentKeys(ctx, db.DeploymentKeyFilter{
ProjectName: &d.projectName,
DeploymentName: &d.info.Name,
DeploymentKeyName: &d.keyInfo.Name,
})
if err != nil {
return err
}

if len(keys) == 0 {
return api.StatusErrorf(http.StatusNotFound, "No keys with that name")
} else if len(keys) > 1 {
return api.StatusErrorf(http.StatusTeapot, "Something strange is afoot")
}

return tx.RenameDeploymentKey(ctx, keys[0].ID, newName)
})
if err != nil {
return err
Expand Down
17 changes: 9 additions & 8 deletions test/suites/deployment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test_simple_deployment() {
lxc deployment create dep4 \
--description "Test description for dep4" \
--governor-webhook-url http://0.0.0.0/scale \
foo=bar foo.bar=baz
user.foo=bar user.bar=baz

# Creating deployment from stdin.
cat <<EOF | lxc deployment create dep5
Expand All @@ -52,11 +52,11 @@ EOF
lxc deployment list --format csv | grep dep4

# Showing a deployment
lxc deployment show dep4 | grep -q 'foo: bar'
lxc deployment show dep4 | grep -q 'user.foo: bar'
lxc deployment show dep4 | grep -q 'governor_webhook_url: http://0.0.0.0/scale'

# Getting a deployment key value / property
lxc deployment get dep4 foo | grep -q 'bar'
lxc deployment get dep4 user.foo | grep -q 'bar'
lxc deployment get dep4 governor_webhook_url --property | grep -q 'http://0.0.0.0/scale'

# Setting/Unsetting a deployment key value / property
Expand Down Expand Up @@ -89,7 +89,7 @@ test_deployment_shape() {
lxc deployment create dep1

# Create shapes
lxc deployment shape create dep1 shape1
! lxc deployment shape create dep1 shape1 || false # Scaling values cannot both be zero.
lxc deployment shape create dep1 shape2 \
--description "This is a test description for shape2" \
--scaling-min 2 \
Expand Down Expand Up @@ -130,6 +130,7 @@ EOF
# Editing a shape
cat <<EOF | lxc deployment shape edit dep1 shape3
description: New description for shape3
scaling_maximum: 8
config:
new.user.foo: blah2
new.user.foo.bar: blah3
Expand All @@ -144,7 +145,7 @@ EOF
lxc deployment shape list dep1 --format csv | grep new-shape

# deleting a shape
lxc deployment shape delete dep1 shape1
! lxc deployment shape delete dep1 shape1 || false # This shape was never created.
! lxc deployment shape delete depNotFound shape1 || false # deployment not found
! lxc deployment shape delete dep1 shapeNotFound || false # shape not found
lxc deployment shape delete dep1 shape2
Expand All @@ -153,7 +154,7 @@ EOF
# Creating a shape from an image
ensure_import_testimage

lxc deployment shape create dep1 shape1 --from-image testimage
lxc deployment shape create dep1 shape1 --from-image testimage --scaling-max 1
lxc deployment shape show dep1 shape1 | grep -e 'type: image' -e 'alias: testimage' -e 'protocol: simplestreams'
! lxc deployment shape create dep1 shape1 --from-image wrongimage || false # image not found

Expand Down Expand Up @@ -185,7 +186,7 @@ description: Test LXD Profile for a test deployment shape
name: example-profile
EOF

lxc deployment shape create dep1 shape2 --from-profile test-profile
lxc deployment shape create dep1 shape2 --from-profile test-profile --scaling-max 1
lxc deployment shape show dep1 shape2 | awk '
BEGIN { found=0; inside=0; }
/^devices:/ { inside=1; }
Expand All @@ -195,7 +196,7 @@ inside && /eth0:/ && /name: eth0/ && /nictype: bridged/ && /parent: lxdbr0/ && /

# Creating a shape from an image and an instance profile
# while specifying that the shape must create vm instances (and not containers that are set by default)
lxc deployment shape create dep1 complete-shape --from-profile test-profile --from-image testimage --vm
lxc deployment shape create dep1 complete-shape --from-profile test-profile --from-image testimage --vm --scaling-max 1
lxc deployment shape show dep1 complete-shape | awk '
BEGIN { found=0; inside=0; }
/^instance_template:/ { inside=1; }
Expand Down

0 comments on commit 77fd6a6

Please sign in to comment.