Last active
August 27, 2024 22:49
-
-
Save uhthomas/d1e890aee1ce3d7ea1dccb6239101b3f to your computer and use it in GitHub Desktop.
improved docker distrubtion invalid repository finder and deleter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"flag" | |
"fmt" | |
"log" | |
"path" | |
"github.com/aws/aws-sdk-go/aws" | |
"github.com/aws/aws-sdk-go/aws/credentials" | |
"github.com/aws/aws-sdk-go/aws/session" | |
"github.com/aws/aws-sdk-go/service/s3" | |
) | |
func isEmptyRepository(ctx context.Context, c *s3.S3, bucket, prefix string) (bool, error) { | |
res, err := c.ListObjectsV2WithContext(ctx, &s3.ListObjectsV2Input{ | |
Bucket: aws.String(bucket), | |
Prefix: aws.String(path.Join(prefix, "_manifests")), | |
MaxKeys: aws.Int64(1), | |
}) | |
if err != nil { | |
return false, fmt.Errorf("list objects: %w", err) | |
} | |
return len(res.Contents) == 0, nil | |
} | |
func removeEmptyRepository(ctx context.Context, c *s3.S3, bucket, prefix string) error { | |
empty, err := isEmptyRepository(ctx, c, bucket, prefix) | |
if err != nil { | |
return fmt.Errorf("is empty repository: %w", err) | |
} | |
if !empty { | |
return nil | |
} | |
log.Printf("%s is empty and will be removed", prefix) | |
var continuationToken *string | |
for { | |
res, err := c.ListObjectsV2WithContext(ctx, &s3.ListObjectsV2Input{ | |
Bucket: aws.String(bucket), | |
Prefix: aws.String(prefix), | |
ContinuationToken: continuationToken, | |
}) | |
if err != nil { | |
return fmt.Errorf("list objects: %w", err) | |
} | |
var objects []*s3.ObjectIdentifier | |
for _, o := range res.Contents { | |
log.Printf("delete %s", aws.StringValue(o.Key)) | |
objects = append(objects, &s3.ObjectIdentifier{ | |
Key: o.Key, | |
}) | |
} | |
if _, err := c.DeleteObjectsWithContext(ctx, &s3.DeleteObjectsInput{ | |
Bucket: aws.String(bucket), | |
Delete: &s3.Delete{ | |
Objects: objects, | |
}, | |
}); err != nil { | |
return fmt.Errorf("delete objects: %w", err) | |
} | |
if res.NextContinuationToken == nil { | |
break | |
} | |
continuationToken = res.NextContinuationToken | |
} | |
return nil | |
} | |
func removeEmptyRepositories(ctx context.Context, c *s3.S3, bucket, prefix string) error { | |
var continuationToken *string | |
for { | |
res, err := c.ListObjectsV2WithContext(ctx, &s3.ListObjectsV2Input{ | |
Bucket: aws.String(bucket), | |
Prefix: aws.String(prefix), | |
ContinuationToken: continuationToken, | |
Delimiter: aws.String("/"), | |
}) | |
if err != nil { | |
return fmt.Errorf("list objects: %w", err) | |
} | |
for _, p := range res.CommonPrefixes { | |
if err := removeEmptyRepository(ctx, c, bucket, aws.StringValue(p.Prefix)); err != nil { | |
return fmt.Errorf("remove empty repository: %w", err) | |
} | |
} | |
if res.NextContinuationToken == nil { | |
break | |
} | |
continuationToken = res.NextContinuationToken | |
} | |
return nil | |
} | |
func Main(ctx context.Context) error { | |
region := flag.String("region", "auto", "s3 region") | |
endpoint := flag.String("endpoint", "", "s3 endpoint, for example https://abc.r2.cloudflarestorage.com") | |
bucket := flag.String("bucket", "", "s3 bucket") | |
accessKeyID := flag.String("access-key-id", "", "s3 access key id") | |
accessKeySecret := flag.String("access-key-secret", "", "s3 access key secret") | |
flag.Parse() | |
if *bucket == "" { | |
return fmt.Errorf("bucket is required") | |
} | |
sess, err := session.NewSession(&aws.Config{ | |
Region: region, | |
Endpoint: endpoint, | |
Credentials: credentials.NewStaticCredentials(*accessKeyID, *accessKeySecret, ""), | |
}) | |
if err != nil { | |
return fmt.Errorf("new session: %w", err) | |
} | |
const prefix = "docker/registry/v2/repositories/" | |
if err := removeEmptyRepositories(ctx, s3.New(sess), *bucket, prefix); err != nil { | |
return fmt.Errorf("remove empty repositories: %w", err) | |
} | |
return nil | |
} | |
func main() { | |
if err := Main(context.Background()); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.