mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
versionsapi: implement rm cmd in cli
Signed-off-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
This commit is contained in:
parent
53cc63362f
commit
0011d960f7
17
go.mod
17
go.mod
@ -45,17 +45,20 @@ require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2 v2.0.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.0.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.3
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.7
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.17.3
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.19.4
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.46
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.22.2
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.17.1
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.75.0
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.19.2
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.9
|
||||
github.com/aws/smithy-go v1.13.5
|
||||
github.com/coreos/go-systemd/v22 v22.5.0
|
||||
github.com/docker/docker v20.10.22+incompatible
|
||||
@ -133,12 +136,12 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.28
|
||||
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.27
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.13.26
|
||||
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.13.25
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect
|
||||
@ -254,7 +257,7 @@ require (
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1
|
||||
github.com/rubenv/sql-migrate v1.1.2 // indirect
|
||||
github.com/russross/blackfriday v1.6.0 // indirect
|
||||
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 // indirect
|
||||
|
35
go.sum
35
go.sum
@ -105,6 +105,9 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapp
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 h1:/Di3vB4sNeQ+7A8efjUVENvyB945Wruvstucqp7ZArg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2 v2.0.0 h1:xxe4naFUPYEW1W6C8yWrfFNmyZLnEbO+CsbsSF83wDo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2 v2.0.0/go.mod h1:aLFjumYDvv63tH1qnqkcmdjdZ6Sn+/viPv7H3jft0oY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.1 h1:H3g2mkmu105ON0c/Gqx3Bm+bzoIijLom8LmV9Gjn7X0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.0.0 h1:KepfQdVTTQl/UmAbRALdkUUUfcWfu8xRaqrQ03ZGwvM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.0.0/go.mod h1:Q3u+T/qw3Kb1Wf3DFKiFwEZlyaAyPb4yBgWm9wq7yh8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0 h1:lMW1lD/17LUA5z1XTURo7LcVG2ICBPlyMHjIUrcFZNQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0=
|
||||
@ -203,6 +206,7 @@ github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.2/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
|
||||
@ -213,36 +217,43 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.7 h1:qUUcNS5Z1092XBFT66IJM7mYkMwg
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.7/go.mod h1:AdCcbZXHQCjJh6NaH3pFaw8LUeBFn5+88BZGMVGuBT8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.46 h1:OCX1pQ4pcqhsDV7B92HzdLWjHWOQsILvjLinpaUWhcc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.46/go.mod h1:MxCBOcyNXGJRvfpPiH+L6n/BF9zbowthGSUZdDvQF/c=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.26/go.mod h1:2E0LdbJW6lbeU4uxjum99GZzI0ZjDpAb0CoSCM0oeEY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.20/go.mod h1:/+6lSiby8TBFpTVXZgKiN/rCfkYXEGvhlM4zCgPpt7w=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18 h1:H/mF2LNWwX00lD6FlYfKpLLZgUW7oIzCBkig78x4Xok=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18/go.mod h1:T2Ku+STrYQ1zIkL1wMvj8P3wWQaaCMKNdz70MT2FLfE=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.17.3 h1:GKDlULxx6rUH67l/CRnG0xZzeMLZVk5gVCkVqNK6bgg=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.17.3/go.mod h1:xHK1ta0bQEa5jL6rahKRJvsibjzDO7NTIs5itzsF4w8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0 h1:m6HYlpZlTWb9vHuuRHpWRieqPHWlS0mvQ90OJNrG/Nk=
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0/go.mod h1:mV0E7631M1eXdB+tlGFIw6JxfsC7Pz7+7Aw15oLVhZw=
|
||||
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.28 h1:Ae7aH1PEbrVSg1fSQy33E/mILWd1Csncz95FMfxYWms=
|
||||
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.28/go.mod h1:ix71C17la8K2MUJrqJzu+i7+aPoQYTAy14hKQbGDB9w=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.22.2 h1:XP88fE1Y8a5308lYzmTnC0be6prtaKAXc6qlD1e4YIE=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.22.2/go.mod h1:xUOmvPrMKmH94stXswKsGSkL02vMpNU+rTG+eIzFfNQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.17.1 h1:JO95wZ1lbTeGDdPLb5bTp4oxmZyGLfLXDzdfLhhRHfQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.17.1/go.mod h1:LpFZR0QsWbDJGtipKU9FsT0RptrLURfO1Qpz4UxahVc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.75.0 h1:F0v9HcF7/PSmgG7O7qnVOZLTRb2I2ajrIql+hFSkouU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.75.0/go.mod h1:/sbgra0egm5fRRlq58Qp+Mrq4mCgWOc4Ug5K6xWCK6M=
|
||||
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.27 h1:wxij3U3NcK5Ku/VURBqR1r7Ip+kLsmfZd0b7BlqCKyk=
|
||||
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.27/go.mod h1:vxihO/POqPi4jNKVRG+pIaPdNUhqCGlcFRBYL6aJF4c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 h1:kv5vRAl00tozRxSnI0IszPWGXsJOyA7hmEUHFYqsyvw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22/go.mod h1:Od+GU5+Yx41gryN/ZGZzAJMZ9R1yn6lgA0fD5Lo5SkQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.20/go.mod h1:Xs52xaLBqDEKRcAfX/hgjmD3YQ7c/W+BEyfamlO/W2E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 h1:vY5siRXvW5TrOKm2qKEf9tliBfdLxdfy0i02LOcmqUo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21/go.mod h1:WZvNXT1XuH8dnJM0HvOlvk+RNn7NbAPvA/ACO0QarSc=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.19.4 h1:bX+nEwdukfDdfGPUjNNqs7NwZyqyMjIy5YpZda9Gcu4=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.19.4/go.mod h1:13sjgMH7Xu4e46+0BEDhSnNh+cImHSYS5PpBjV3oXcU=
|
||||
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.13.26 h1:/PUrNPA60N/tE60pf3AZPIe5th/E5GXgVq+PdLgae5c=
|
||||
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.13.26/go.mod h1:NjPeUP8L8V1lN1ik1Znb0cEnIgGA3Upt/UFSzwBLC6o=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.19.2 h1:pgOVfu7E6zBddKGks4TvL4YuFsL/oTpiWDIzs4WPLjY=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.19.2/go.mod h1:XH60PhgtbXDXFBzJ2auE6bpIELxAYTnoVFFwPtG8JwY=
|
||||
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.13.25 h1:0vjMVw755SnqnySkc7zdVmn2LVNozUFlSbu0A/v+9Ws=
|
||||
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.13.25/go.mod h1:69YP7x9Jp1ZPwQsl6yh3fW2moP87tuhPsH4RmHemOfE=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6 h1:W8pLcSn6Uy0eXgDBUUl8M8Kxv7JCoP68ZKTD04OXLEA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6/go.mod h1:L2l2/q76teehcW7YEsgsDjqdsDTERJeX3nOMIFlgGUE=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 h1:77V7vnw/NC4DORHVgA97+Ky2p1ri0+ZVYXh6ordUZU0=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.9 h1:ogcakjF/mrZOo9oJVWmRbG838C04oWGXI8T8IY4xcfM=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.9/go.mod h1:S7AsUoaHONHV2iGM5QXQOonnaV05cK9fty2dXRdouws=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.28/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3IGYO8P8Jx+TgSDhAXtQMY=
|
||||
|
@ -46,6 +46,7 @@ func newRootCmd() *cobra.Command {
|
||||
rootCmd.AddCommand(newAddCmd())
|
||||
rootCmd.AddCommand(newLatestCmd())
|
||||
rootCmd.AddCommand(newListCmd())
|
||||
rootCmd.AddCommand(newRemoveCmd())
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
766
internal/versionsapi/cli/rm.go
Normal file
766
internal/versionsapi/cli/rm.go
Normal file
@ -0,0 +1,766 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
"cloud.google.com/go/compute/apiv1/computepb"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
armcomputev4 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4"
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
||||
"github.com/aws/smithy-go"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
verclient "github.com/edgelesssys/constellation/v2/internal/versionsapi/client"
|
||||
gaxv2 "github.com/googleapis/gax-go/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func newRemoveCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "remove",
|
||||
Short: "Remove a version/ref",
|
||||
Long: `Remove a version/ref from the versions API.
|
||||
|
||||
Developers should not use this command directly. It is invoked by the CI/CD pipeline.
|
||||
Most developers won't have the required permissions to use this command.
|
||||
|
||||
❗ If you use the command nevertheless, you better know what you do.
|
||||
`,
|
||||
RunE: runRemove,
|
||||
Args: cobra.ExactArgs(0),
|
||||
}
|
||||
|
||||
cmd.Flags().String("ref", "", "Ref to delete from.")
|
||||
cmd.Flags().String("stream", "", "Stream to delete from.")
|
||||
cmd.Flags().String("version", "", "Version to delete. The versioned objects are deleted.")
|
||||
cmd.Flags().String("version-path", "", "Short path of a single version to delete. The versioned objects are deleted.")
|
||||
cmd.Flags().Bool("all", false, "Delete the entire ref. All versions and versioned objects are deleted.")
|
||||
cmd.Flags().Bool("dryrun", false, "Whether to run in dry-run mode (no changes are made)")
|
||||
cmd.Flags().String("gcp-project", "constellation-images", "GCP project to use")
|
||||
cmd.Flags().String("az-subscription", "0d202bbb-4fa7-4af8-8125-58c269a05435", "Azure subscription to use")
|
||||
cmd.Flags().String("az-location", "northeurope", "Azure location to use")
|
||||
cmd.Flags().String("az-resource-group", "constellation-images", "Azure resource group to use")
|
||||
|
||||
cmd.MarkFlagsRequiredTogether("stream", "version")
|
||||
cmd.MarkFlagsMutuallyExclusive("all", "stream")
|
||||
cmd.MarkFlagsMutuallyExclusive("all", "version")
|
||||
cmd.MarkFlagsMutuallyExclusive("all", "version-path")
|
||||
cmd.MarkFlagsMutuallyExclusive("version-path", "ref")
|
||||
cmd.MarkFlagsMutuallyExclusive("version-path", "stream")
|
||||
cmd.MarkFlagsMutuallyExclusive("version-path", "version")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(cmd *cobra.Command, args []string) (retErr error) {
|
||||
flags, err := parseRmFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log := logger.New(logger.PlainLog, flags.logLevel)
|
||||
log.Debugf("Parsed flags: %+v", flags)
|
||||
|
||||
log.Debugf("Validating flags.")
|
||||
if err := flags.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Creating GCP client.")
|
||||
gcpClient, err := newGCPClient(cmd.Context(), flags.gcpProject)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating GCP client: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("Creating AWS client.")
|
||||
awsClient, err := newAWSClient(cmd.Context(), flags.region)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating AWS client: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("Creating Azure client.")
|
||||
azClient, err := newAzureClient(flags.azSubscription, flags.azLocation, flags.azResourceGroup)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating Azure client: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("Creating versions API client.")
|
||||
verclient, err := verclient.NewClient(cmd.Context(), flags.region, flags.bucket, flags.distributionID, flags.dryrun, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating client: %w", err)
|
||||
}
|
||||
defer func(retErr *error) {
|
||||
log.Infof("Invalidating cache. This may take some time.")
|
||||
if err := verclient.InvalidateCache(cmd.Context()); err != nil && retErr == nil {
|
||||
*retErr = fmt.Errorf("invalidating cache: %w", err)
|
||||
}
|
||||
}(&retErr)
|
||||
|
||||
imageClients := rmImageClients{
|
||||
version: verclient,
|
||||
gcp: gcpClient,
|
||||
aws: awsClient,
|
||||
az: azClient,
|
||||
}
|
||||
|
||||
if flags.all {
|
||||
log.Infof("Deleting ref %s", flags.ref)
|
||||
if err := deleteRef(cmd.Context(), imageClients, flags.ref, flags.dryrun, log); err != nil {
|
||||
return fmt.Errorf("deleting ref: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("Deleting single version %s", flags.ver.ShortPath())
|
||||
if err := deleteSingleVersion(cmd.Context(), imageClients, flags.ver, flags.dryrun, log); err != nil {
|
||||
return fmt.Errorf("deleting single version: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteSingleVersion(ctx context.Context, clients rmImageClients, ver versionsapi.Version, dryrun bool, log *logger.Logger) error {
|
||||
var retErr error
|
||||
|
||||
log.Debugf("Deleting version %s from versions API", ver.Version)
|
||||
if err := clients.version.DeleteVersion(ctx, ver); err != nil {
|
||||
retErr = multierr.Append(retErr, fmt.Errorf("deleting version from versions API: %w", err))
|
||||
}
|
||||
|
||||
log.Debugf("Deleting images for %s", ver.Version)
|
||||
if err := deleteImage(ctx, clients, ver, dryrun, log); err != nil {
|
||||
retErr = multierr.Append(retErr, fmt.Errorf("deleting images: %w", err))
|
||||
}
|
||||
|
||||
return retErr
|
||||
}
|
||||
|
||||
func deleteRef(ctx context.Context, clients rmImageClients, ref string, dryrun bool, log *logger.Logger) error {
|
||||
var retErr error
|
||||
|
||||
var vers []versionsapi.Version
|
||||
for _, stream := range []string{"nightly", "console", "debug"} {
|
||||
log.Infof("Listing versions of stream %s", stream)
|
||||
|
||||
minorVersions, err := listMinorVersions(ctx, clients.version, ref, stream)
|
||||
var notFoundErr *verclient.NotFoundError
|
||||
if errors.As(err, ¬FoundErr) {
|
||||
log.Debugf("No minor versions found for stream %s", stream)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("listing minor versions for stream %s: %w", stream, err)
|
||||
}
|
||||
|
||||
patchVersions, err := listPatchVersions(ctx, clients.version, ref, stream, minorVersions)
|
||||
if errors.As(err, ¬FoundErr) {
|
||||
log.Debugf("No patch versions found for stream %s", stream)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("listing patch versions for stream %s: %w", stream, err)
|
||||
}
|
||||
|
||||
vers = append(vers, patchVersions...)
|
||||
}
|
||||
log.Infof("Found %d versions to delete", len(vers))
|
||||
|
||||
for _, ver := range vers {
|
||||
if err := deleteImage(ctx, clients, ver, dryrun, log); err != nil {
|
||||
retErr = multierr.Append(retErr, fmt.Errorf("deleting images for version %s: %w", ver.Version, err))
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Deleting ref %s from versions API", ref)
|
||||
if err := clients.version.DeleteRef(ctx, ref); err != nil {
|
||||
retErr = multierr.Append(retErr, fmt.Errorf("deleting ref from versions API: %w", err))
|
||||
}
|
||||
|
||||
return retErr
|
||||
}
|
||||
|
||||
func deleteImage(ctx context.Context, clients rmImageClients, ver versionsapi.Version, dryrun bool, log *logger.Logger) error {
|
||||
var retErr error
|
||||
|
||||
imageInfo := versionsapi.ImageInfo{
|
||||
Ref: ver.Ref,
|
||||
Stream: ver.Stream,
|
||||
Version: ver.Version,
|
||||
}
|
||||
imageInfo, err := clients.version.FetchImageInfo(ctx, imageInfo)
|
||||
var notFound *verclient.NotFoundError
|
||||
if errors.As(err, ¬Found) {
|
||||
log.Warnf("Image info for %s not found.", ver.Version)
|
||||
log.Warnf("Skipping image deletion.")
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("fetching image info: %w", err)
|
||||
}
|
||||
|
||||
log.Infof("Deleting AWS images from %s", imageInfo.JSONPath())
|
||||
for awsRegion, awsImage := range imageInfo.AWS {
|
||||
if err := clients.aws.deleteImage(ctx, awsImage, awsRegion, dryrun, log); err != nil {
|
||||
retErr = multierr.Append(retErr, fmt.Errorf("deleting AWS image %s: %w", awsImage, err))
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Deleting GCP images from %s", imageInfo.JSONPath())
|
||||
for _, gcpImage := range imageInfo.GCP {
|
||||
if err := clients.gcp.deleteImage(ctx, gcpImage, dryrun, log); err != nil {
|
||||
retErr = multierr.Append(retErr, fmt.Errorf("deleting GCP image %s: %w", gcpImage, err))
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Deleting Azure images from %s", imageInfo.JSONPath())
|
||||
for _, azImage := range imageInfo.Azure {
|
||||
if err := clients.az.deleteImage(ctx, azImage, dryrun, log); err != nil {
|
||||
retErr = multierr.Append(retErr, fmt.Errorf("deleting Azure image %s: %w", azImage, err))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(katexochen): Implement versions API trash. In case of failure, we should
|
||||
// collect the resources that couldn't be deleted and store them in the trash, so
|
||||
// that we can retry deleting them later.
|
||||
|
||||
return retErr
|
||||
}
|
||||
|
||||
type rmImageClients struct {
|
||||
version *verclient.Client
|
||||
gcp *gcpClient
|
||||
aws *awsClient
|
||||
az *azureClient
|
||||
}
|
||||
|
||||
type rmFlags struct {
|
||||
ref string
|
||||
stream string
|
||||
version string
|
||||
versionPath string
|
||||
all bool
|
||||
dryrun bool
|
||||
region string
|
||||
bucket string
|
||||
distributionID string
|
||||
gcpProject string
|
||||
azSubscription string
|
||||
azLocation string
|
||||
azResourceGroup string
|
||||
logLevel zapcore.Level
|
||||
|
||||
ver versionsapi.Version
|
||||
}
|
||||
|
||||
func (f *rmFlags) validate() error {
|
||||
if f.ref == versionsapi.ReleaseRef {
|
||||
return fmt.Errorf("cannot delete from release ref")
|
||||
}
|
||||
|
||||
if f.all {
|
||||
if err := versionsapi.ValidateRef(f.ref); err != nil {
|
||||
return fmt.Errorf("invalid ref: %w", err)
|
||||
}
|
||||
|
||||
if f.ref == "main" {
|
||||
return fmt.Errorf("cannot delete 'main' ref")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.versionPath != "" {
|
||||
ver, err := versionsapi.NewVersionFromShortPath(f.versionPath, versionsapi.VersionKindImage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid version path: %w", err)
|
||||
}
|
||||
f.ver = ver
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
ver := versionsapi.Version{
|
||||
Ref: f.ref,
|
||||
Stream: f.stream,
|
||||
Version: f.version,
|
||||
Kind: versionsapi.VersionKindImage,
|
||||
}
|
||||
if err := ver.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid version: %w", err)
|
||||
}
|
||||
f.ver = ver
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseRmFlags(cmd *cobra.Command) (*rmFlags, error) {
|
||||
ref, err := cmd.Flags().GetString("ref")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref = versionsapi.CanonicalizeRef(ref)
|
||||
stream, err := cmd.Flags().GetString("stream")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version, err := cmd.Flags().GetString("version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versionPath, err := cmd.Flags().GetString("version-path")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all, err := cmd.Flags().GetBool("all")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dryrun, err := cmd.Flags().GetBool("dryrun")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
region, err := cmd.Flags().GetString("region")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bucket, err := cmd.Flags().GetString("bucket")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
distributionID, err := cmd.Flags().GetString("distribution-id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gcpProject, err := cmd.Flags().GetString("gcp-project")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
azSubscription, err := cmd.Flags().GetString("az-subscription")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
azLocation, err := cmd.Flags().GetString("az-location")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
azResourceGroup, err := cmd.Flags().GetString("az-resource-group")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verbose, err := cmd.Flags().GetBool("verbose")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logLevel := zapcore.InfoLevel
|
||||
if verbose {
|
||||
logLevel = zapcore.DebugLevel
|
||||
}
|
||||
|
||||
return &rmFlags{
|
||||
ref: ref,
|
||||
stream: stream,
|
||||
version: version,
|
||||
versionPath: versionPath,
|
||||
all: all,
|
||||
dryrun: dryrun,
|
||||
region: region,
|
||||
bucket: bucket,
|
||||
distributionID: distributionID,
|
||||
gcpProject: gcpProject,
|
||||
azSubscription: azSubscription,
|
||||
azLocation: azLocation,
|
||||
azResourceGroup: azResourceGroup,
|
||||
logLevel: logLevel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type awsClient struct {
|
||||
ec2 ec2API
|
||||
}
|
||||
|
||||
// newAWSClient creates a new awsClient.
|
||||
// Requires IAM permission 'ec2:DeregisterImage'.
|
||||
func newAWSClient(ctx context.Context, region string) (*awsClient, error) {
|
||||
return &awsClient{}, nil
|
||||
}
|
||||
|
||||
type ec2API interface {
|
||||
DeregisterImage(ctx context.Context, params *ec2.DeregisterImageInput, optFns ...func(*ec2.Options),
|
||||
) (*ec2.DeregisterImageOutput, error)
|
||||
DescribeImages(ctx context.Context, params *ec2.DescribeImagesInput, optFns ...func(*ec2.Options),
|
||||
) (*ec2.DescribeImagesOutput, error)
|
||||
DeleteSnapshot(ctx context.Context, params *ec2.DeleteSnapshotInput, optFns ...func(*ec2.Options),
|
||||
) (*ec2.DeleteSnapshotOutput, error)
|
||||
}
|
||||
|
||||
func (a *awsClient) deleteImage(ctx context.Context, ami string, region string, dryrun bool, log *logger.Logger) error {
|
||||
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.ec2 = ec2.NewFromConfig(cfg)
|
||||
|
||||
snapshotID, err := a.getSnapshotID(ctx, ami, log)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get AWS snapshot ID for image %s: %v", ami, err)
|
||||
}
|
||||
|
||||
if err := a.deregisterImage(ctx, ami, dryrun, log); err != nil {
|
||||
return fmt.Errorf("deregistering image %s: %w", ami, err)
|
||||
}
|
||||
|
||||
if snapshotID != "" {
|
||||
if err := a.deleteSnapshot(ctx, snapshotID, dryrun, log); err != nil {
|
||||
return fmt.Errorf("deleting snapshot %s: %w", snapshotID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *awsClient) deregisterImage(ctx context.Context, ami string, dryrun bool, log *logger.Logger) error {
|
||||
log.Debugf("Deregistering image %s", ami)
|
||||
|
||||
deregisterReq := ec2.DeregisterImageInput{
|
||||
ImageId: &ami,
|
||||
DryRun: &dryrun,
|
||||
}
|
||||
_, err := a.ec2.DeregisterImage(ctx, &deregisterReq)
|
||||
var apiErr smithy.APIError
|
||||
if errors.As(err, &apiErr) &&
|
||||
(apiErr.ErrorCode() == "InvalidAMIID.NotFound" ||
|
||||
apiErr.ErrorCode() == "InvalidAMIID.Unavailable") {
|
||||
log.Warnf("AWS image %s not found", ami)
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *awsClient) getSnapshotID(ctx context.Context, ami string, log *logger.Logger) (string, error) {
|
||||
log.Debugf("Describing image %s", ami)
|
||||
|
||||
req := ec2.DescribeImagesInput{
|
||||
ImageIds: []string{ami},
|
||||
}
|
||||
resp, err := a.ec2.DescribeImages(ctx, &req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("describing image %s: %w", ami, err)
|
||||
}
|
||||
|
||||
if len(resp.Images) == 0 {
|
||||
return "", fmt.Errorf("image %s not found", ami)
|
||||
}
|
||||
|
||||
if len(resp.Images) > 1 {
|
||||
return "", fmt.Errorf("found multiple images with ami %s", ami)
|
||||
}
|
||||
image := resp.Images[0]
|
||||
|
||||
if len(image.BlockDeviceMappings) != 1 {
|
||||
return "", fmt.Errorf("found %d block device mappings for image %s, expected 1", len(image.BlockDeviceMappings), ami)
|
||||
}
|
||||
if image.BlockDeviceMappings[0].Ebs == nil {
|
||||
return "", fmt.Errorf("image %s does not have an EBS block device mapping", ami)
|
||||
}
|
||||
ebs := image.BlockDeviceMappings[0].Ebs
|
||||
|
||||
if ebs.SnapshotId == nil {
|
||||
return "", fmt.Errorf("image %s does not have an EBS snapshot", ami)
|
||||
}
|
||||
snapshotID := *ebs.SnapshotId
|
||||
|
||||
return snapshotID, nil
|
||||
}
|
||||
|
||||
func (a *awsClient) deleteSnapshot(ctx context.Context, snapshotID string, dryrun bool, log *logger.Logger) error {
|
||||
log.Debugf("Deleting AWS snapshot %s", snapshotID)
|
||||
|
||||
req := ec2.DeleteSnapshotInput{
|
||||
SnapshotId: &snapshotID,
|
||||
DryRun: &dryrun,
|
||||
}
|
||||
_, err := a.ec2.DeleteSnapshot(ctx, &req)
|
||||
var apiErr smithy.APIError
|
||||
if errors.As(err, &apiErr) &&
|
||||
(apiErr.ErrorCode() == "InvalidSnapshot.NotFound" ||
|
||||
apiErr.ErrorCode() == "InvalidSnapshot.Unavailable") {
|
||||
log.Warnf("AWS snapshot %s not found", snapshotID)
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type gcpClient struct {
|
||||
project string
|
||||
compute gcpComputeAPI
|
||||
}
|
||||
|
||||
func newGCPClient(ctx context.Context, project string) (*gcpClient, error) {
|
||||
compute, err := compute.NewImagesRESTClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gcpClient{
|
||||
compute: compute,
|
||||
project: project,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type gcpComputeAPI interface {
|
||||
Delete(ctx context.Context, req *computepb.DeleteImageRequest, opts ...gaxv2.CallOption,
|
||||
) (*compute.Operation, error)
|
||||
io.Closer
|
||||
}
|
||||
|
||||
func (g *gcpClient) deleteImage(ctx context.Context, image string, dryrun bool, log *logger.Logger) error {
|
||||
req := &computepb.DeleteImageRequest{
|
||||
Image: image,
|
||||
Project: g.project,
|
||||
}
|
||||
|
||||
if dryrun {
|
||||
log.Debugf("DryRun: delete image request: %v", req)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("Deleting image %s", image)
|
||||
op, err := g.compute.Delete(ctx, req)
|
||||
if err != nil && strings.Contains(err.Error(), "404") {
|
||||
log.Warnf("GCP image %s not found", image)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("deleting image %s: %w", image, err)
|
||||
}
|
||||
|
||||
log.Debugf("Waiting for operation to finish.")
|
||||
if err := op.Wait(ctx); err != nil {
|
||||
return fmt.Errorf("waiting for operation: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gcpClient) Close() error {
|
||||
return g.compute.Close()
|
||||
}
|
||||
|
||||
type azureClient struct {
|
||||
subscription string
|
||||
location string
|
||||
resourceGroup string
|
||||
galleries azureGalleriesAPI
|
||||
image azureGalleriesImageAPI
|
||||
imageVersions azureGalleriesImageVersionAPI
|
||||
}
|
||||
|
||||
func newAzureClient(subscription, location, resourceGroup string) (*azureClient, error) {
|
||||
cred, err := azidentity.NewDefaultAzureCredential(nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
galleriesClient, err := armcomputev4.NewGalleriesClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
galleriesImageClient, err := armcomputev4.NewGalleryImagesClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
galleriesImageVersionClient, err := armcomputev4.NewGalleryImageVersionsClient(subscription, cred, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &azureClient{
|
||||
subscription: subscription,
|
||||
location: location,
|
||||
resourceGroup: resourceGroup,
|
||||
galleries: galleriesClient,
|
||||
image: galleriesImageClient,
|
||||
imageVersions: galleriesImageVersionClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type azureGalleriesAPI interface {
|
||||
NewListPager(options *armcomputev4.GalleriesClientListOptions,
|
||||
) *runtime.Pager[armcomputev4.GalleriesClientListResponse]
|
||||
}
|
||||
|
||||
type azureGalleriesImageAPI interface {
|
||||
BeginDelete(ctx context.Context, resourceGroupName string, galleryName string, galleryImageName string,
|
||||
options *armcomputev4.GalleryImagesClientBeginDeleteOptions,
|
||||
) (*runtime.Poller[armcomputev4.GalleryImagesClientDeleteResponse], error)
|
||||
}
|
||||
|
||||
type azureGalleriesImageVersionAPI interface {
|
||||
NewListByGalleryImagePager(resourceGroupName string, galleryName string, galleryImageName string,
|
||||
options *armcomputev4.GalleryImageVersionsClientListByGalleryImageOptions,
|
||||
) *runtime.Pager[armcomputev4.GalleryImageVersionsClientListByGalleryImageResponse]
|
||||
|
||||
BeginDelete(ctx context.Context, resourceGroupName string, galleryName string, galleryImageName string,
|
||||
galleryImageVersionName string, options *armcomputev4.GalleryImageVersionsClientBeginDeleteOptions,
|
||||
) (*runtime.Poller[armcomputev4.GalleryImageVersionsClientDeleteResponse], error)
|
||||
}
|
||||
|
||||
var (
|
||||
azImageRegex = regexp.MustCompile("^/subscriptions/[[:alnum:]._-]+/resourceGroups/([[:alnum:]._-]+)/providers/Microsoft.Compute/galleries/([[:alnum:]._-]+)/images/([[:alnum:]._-]+)/versions/([[:alnum:]._-]+)$")
|
||||
azCommunityImageRegex = regexp.MustCompile("^/CommunityGalleries/([[:alnum:]-]+)/Images/([[:alnum:]._-]+)/Versions/([[:alnum:]._-]+)$")
|
||||
)
|
||||
|
||||
func (a *azureClient) deleteImage(ctx context.Context, image string, dryrun bool, log *logger.Logger) error {
|
||||
azImage, err := a.parseImage(ctx, image, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dryrun {
|
||||
log.Debugf("DryRun: delete image %v", azImage)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("Deleting image %q, version %q", azImage.imageDefinition, azImage.version)
|
||||
poller, err := a.imageVersions.BeginDelete(ctx, azImage.resourceGroup, azImage.gallery,
|
||||
azImage.imageDefinition, azImage.version, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("begin delete image version: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("Waiting for operation to finish.")
|
||||
if _, err := poller.PollUntilDone(ctx, nil); err != nil {
|
||||
return fmt.Errorf("waiting for operation: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("Checking if image definition %q still has versions left", azImage.imageDefinition)
|
||||
pager := a.imageVersions.NewListByGalleryImagePager(azImage.resourceGroup, azImage.gallery,
|
||||
azImage.imageDefinition, nil)
|
||||
for pager.More() {
|
||||
nextResult, err := pager.NextPage(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing image versions of image definition %s: %w", azImage.imageDefinition, err)
|
||||
}
|
||||
if len(nextResult.Value) != 0 {
|
||||
log.Debugf("Image definition %q still has versions left, won't be deleted", azImage.imageDefinition)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(15 * time.Second) // Azure needs time understand that there is no version left...
|
||||
|
||||
log.Debugf("Deleting image definition %s", azImage.imageDefinition)
|
||||
op, err := a.image.BeginDelete(ctx, azImage.resourceGroup, azImage.gallery, azImage.imageDefinition, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting image definition %s: %w", azImage.imageDefinition, err)
|
||||
}
|
||||
|
||||
log.Debugf("Waiting for operation to finish.")
|
||||
if _, err := op.PollUntilDone(ctx, nil); err != nil {
|
||||
return fmt.Errorf("waiting for operation: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type azImage struct {
|
||||
resourceGroup string
|
||||
gallery string
|
||||
imageDefinition string
|
||||
version string
|
||||
}
|
||||
|
||||
func (a *azureClient) parseImage(ctx context.Context, image string, log *logger.Logger) (azImage, error) {
|
||||
if m := azImageRegex.FindStringSubmatch(image); len(m) == 5 {
|
||||
log.Debugf(
|
||||
"Image matches local image format, resource group: %s, gallery: %s, image definition: %s, version: %s.",
|
||||
m[1], m[2], m[3], m[4],
|
||||
)
|
||||
return azImage{
|
||||
resourceGroup: m[1],
|
||||
gallery: m[2],
|
||||
imageDefinition: m[3],
|
||||
version: m[4],
|
||||
}, nil
|
||||
}
|
||||
|
||||
if !azCommunityImageRegex.MatchString(image) {
|
||||
return azImage{}, fmt.Errorf("invalid image %s", image)
|
||||
}
|
||||
|
||||
m := azCommunityImageRegex.FindStringSubmatch(image)
|
||||
galleryPublicName := m[1]
|
||||
imageDefinition := m[2]
|
||||
version := m[3]
|
||||
|
||||
log.Debugf(
|
||||
"Image matches community image format, gallery public name: %s, image definition: %s, version: %s.",
|
||||
galleryPublicName, imageDefinition, version,
|
||||
)
|
||||
|
||||
var galleryName string
|
||||
pager := a.galleries.NewListPager(nil)
|
||||
for pager.More() {
|
||||
nextResult, err := pager.NextPage(ctx)
|
||||
if err != nil {
|
||||
return azImage{}, fmt.Errorf("failed to advance page: %w", err)
|
||||
}
|
||||
for _, v := range nextResult.Value {
|
||||
if v.Name == nil {
|
||||
log.Debugf("Skipping gallery with nil name.")
|
||||
continue
|
||||
}
|
||||
if v.Properties.SharingProfile == nil {
|
||||
log.Debugf("Skipping gallery %s with nil sharing profile.", *v.Name)
|
||||
continue
|
||||
}
|
||||
if v.Properties.SharingProfile.CommunityGalleryInfo == nil {
|
||||
log.Debugf("Skipping gallery %s with nil community gallery info.", *v.Name)
|
||||
continue
|
||||
}
|
||||
if v.Properties.SharingProfile.CommunityGalleryInfo.PublicNames == nil {
|
||||
log.Debugf("Skipping gallery %s with nil public names.", *v.Name)
|
||||
continue
|
||||
}
|
||||
for _, publicName := range v.Properties.SharingProfile.CommunityGalleryInfo.PublicNames {
|
||||
if publicName == nil {
|
||||
log.Debugf("Skipping nil public name.")
|
||||
continue
|
||||
}
|
||||
if *publicName == galleryPublicName {
|
||||
galleryName = *v.Name
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
if galleryName != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if galleryName == "" {
|
||||
return azImage{}, fmt.Errorf("failed to find gallery for public name %s", galleryPublicName)
|
||||
}
|
||||
|
||||
return azImage{
|
||||
resourceGroup: a.resourceGroup,
|
||||
gallery: galleryName,
|
||||
imageDefinition: imageDefinition,
|
||||
version: version,
|
||||
}, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user