List all Azure container registry images via Golang - azure

I would like to list all images from Azure registry via Golang.
What I founded is this: https://github.com/Azure-Samples/azure-sdk-for-go-samples/tree/main/sdk/resourcemanager/containerregistry but nothing from there seems to help me.
Any ideas please?
LE:
I ended up with this code
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/runtime/2019-08-15-preview/containerregistry"
"github.com/Azure/go-autorest/autorest"
)
type ACRTokenProvider struct {
accessToken string
}
func (a *ACRTokenProvider) OAuthToken() string {
return a.accessToken
}
func newACRAuthorizer() (*autorest.BearerAuthorizer, error) {
tenantId := "TENANT_ID"
acrService := "servicename.azurecr.io"
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
panic(err)
}
ctx := context.Background()
aadToken, err := cred.GetToken(ctx, policy.TokenRequestOptions{Scopes: []string{"https://management.azure.com/"}})
if err != nil {
panic(err)
}
formData := url.Values{
"grant_type": {"access_token"},
"service": {acrService},
"tenant": {tenantId},
"access_token": {aadToken.Token},
}
jsonResponse, err := http.PostForm(fmt.Sprintf("https://%s/oauth2/exchange", acrService), formData)
if err != nil {
panic(err)
}
var response map[string]interface{}
json.NewDecoder(jsonResponse.Body).Decode(&response)
return autorest.NewBearerAuthorizer(&ACRTokenProvider{accessToken: fmt.Sprint(response["refresh_token"])}), nil
}
func main() {
client := containerregistry.NewRepositoryClient("https://servicename.azurecr.io")
authorizer, err := newACRAuthorizer()
if err != nil {
fmt.Println(err)
}
client.Authorizer = authorizer
// Do what you need to do with client here
attributes, err := client.GetList(context.Background(), "registryName", nil)
if err != nil {
log.Printf("Error while fetching attributes, %v ", err)
}
fmt.Print(attributes)
}
But the response is this
Original Error: autorest/azure: Service returned an error. Status=401 Code="Unknown" Message="Unknown service error" Details=[{"errors":[{"code":"UNAUTHORIZED","detail":[{"Action":"*","Name":"catalog","Type":"registry"}],"message":"authentication required, visit https://aka.ms/acr/authorization for more information."}]}]
What I'm missing?

i'm not sure about the GO SDK, but you can always consume the REST API directly:
GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.ContainerRegistry/registries?api-version=2019-05-01
https://learn.microsoft.com/en-us/rest/api/containerregistry/registries/list?tabs=HTTP

Related

Golang SSH tunneling and ProxyJump

What I need is to perform the equivalent of the following command but in Go code:
ssh -L 9999:192.168.1.1:80 -J root#[IPv6 address] myuser#100.1.1.100
I'm not even sure where to start with this one.
I haven't been able to find any examples online and I'm at a loss.
Does anyone know how this could be done in Go?
package main
import (
"io"
"log"
"net"
"golang.org/x/crypto/ssh"
)
func main() {
client, err := ssh.Dial("tcp", "100.1.1.100:22", &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{ssh.Password("")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
})
if err != nil {
log.Panicln(err)
return
}
log.Println("init ssh client")
ln, err := net.Listen("tcp", ":9999")
if err != nil {
log.Panicln(err)
return
}
log.Println("local listen")
for {
localconn, err := ln.Accept()
if err != nil {
log.Panicln(err)
return
}
sshconn, err := client.DialTCP("", nil, &net.TCPAddr{IP: net.ParseIP("192.168.1.1"), Port: 80})
if err != nil {
log.Panicln(err)
return
}
// local <--> remote
go func() {
errc := make(chan error, 1)
spc := switchProtocolCopier{user: localconn, backend: sshconn}
go spc.copyToBackend(errc)
go spc.copyFromBackend(errc)
log.Printf("stop conn error: %v\n", <-errc)
}()
}
}
// switchProtocolCopier exists so goroutines proxying data back and
// forth have nice names in stacks.
type switchProtocolCopier struct {
user, backend io.ReadWriter
}
func (c switchProtocolCopier) copyFromBackend(errc chan<- error) {
_, err := io.Copy(c.user, c.backend)
errc <- err
}
func (c switchProtocolCopier) copyToBackend(errc chan<- error) {
_, err := io.Copy(c.backend, c.user)
errc <- err
}

How to fetch blob from azure

I am trying to download some data from Azure Blob Storage in Go via the official azure-sdk-for-go.
To setup my development environment I have successfully logged in via az login. I have verified that the blob can be accessed via CLI:
az storage blob download --container-name [container-name] --name [blob-name] --account-name [storage-account-name] -f out.txt
This works as expected. To fetch the file unsing go I am using the following snippet (as a reproducer):
func getBlob(account, container, object string) ([]byte, error) {
blobPath := fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s", uri.Host, container, object)
ctx := context.Background()
credential, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return []byte{}, err
}
blobClient, err := azblob.NewBlockBlobClient(blobPath, credential, nil)
if err != nil {
return []byte{}, err
}
get, err := blobClient.Download(ctx, nil)
if err != nil {
return []byte{}, err
}
downloadedData := &bytes.Buffer{}
reader := get.Body(&azblob.RetryReaderOptions{})
_, err = downloadedData.ReadFrom(reader)
if err != nil {
return []byte{}, err
}
err = reader.Close()
if err != nil {
return []byte{}, err
}
data = downloadedData.Bytes()
return data, nil
}
Being logged in via az login I would expect azidentity.NewDefaultAzureCredential(nil) to use this session/cerdentials (see https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#-option-3-sign-in-with-azure-cli), however that appears not no work as expected. The error I get is the following:
===== RESPONSE ERROR (ErrorCode=AuthorizationPermissionMismatch) =====
Description=This request is not authorized to perform this operation using this permission.
RequestId:b078ec61-xxxx-xxxx-xxxx-604682000000
Time:2022-05-05T10:24:18.8093649Z, Details: (none)
exit status 255
What am I missing?
(I am coming from a AWS background so chances are I am making assumptions on how things should work based on that experience.)
Apparently interacting with blobs does not work with credentials provided by azidentity.NewDefaultAzureCredential(). Azure requires either SAS Tokens or Shared Keys in order to work with blobs. Here's an example function that can be used to get a client for specific blob:
func getBlobClient(account, container, object string) (*azblob.BlockBlobClient, error) {
accountKey, ok := os.LookupEnv("AZURE_STORAGE_ACCOUNT_KEY")
if !ok {
return nil, errors.New("AZURE_STORAGE_ACCOUNT_KEY could not be found")
}
credential, err := azblob.NewSharedKeyCredential(account, accountKey)
if err != nil {
return nil, err
}
accountPath := fmt.Sprintf("https://%s.blob.core.windows.net/", account)
serviceClient, err := azblob.NewServiceClientWithSharedKey(accountPath, credential, nil)
if err != nil {
return nil, err
}
containerClient, err := serviceClient.NewContainerClient(container)
if err != nil {
return nil, err
}
blobClient, err := containerClient.NewBlockBlobClient(object)
if err != nil {
return nil, err
}
return blobClient, nil
}
This uses the AZURE_STORAGE_ACCOUNT_KEY environment variable for the credentials.
The examples that can be found are rather confusing (and possibly wrong), an issue is opened here:
https://github.com/Azure-Samples/storage-blobs-go-quickstart/issues/7

Query latest world state using Hyperledger 1.0 Nodejs SDK

Based on hyperledger SDK doc, we can use nodeJS SDK to query for the block and the transaction info. Is it possible to use this SDK to query the latest world state, e.g, query the value for a given key?
To being able to query for latest world state your chaincode has to provide this capability, namely you have to implement this logic and incorporate it into your chaincode. Then it will simply require to execute the chaincode to get the value for the key you are interested it.
For example you can do something similar to this:
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
// Person
type Asset struct {
ID string `json:"id"`
Name string `json:"name"`
Price string `json:"price"`
}
// assetManagement the chaincode interface implementation to manage
// the ledger of person records
type assetManagement struct {
}
func (p *assetManagement) Init(stub shim.ChaincodeStubInterface) peer.Response {
return shim.Success(nil)
}
func (p *assetManagement) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
actionName, params := stub.GetFunctionAndParameters()
if actionName == "addAsset" {
return p.addAsset(stub)
} else if actionName == "getAsset" {
return p.getAsset(stub)
}
return shim.Error("Unknown function name")
}
func (p *assetManagement) getAsset(stub shim.ChaincodeStubInterface) peer.Response {
_, params := stub.GetFunctionAndParameters()
assetID := params[0]
state, err := stub.GetState(assetID)
if err != nil {
return shim.Error(fmt.Sprintf("%s", err))
}
return shim.Success(state)
}
func (p *assetManagement) addAsset(stub shim.ChaincodeStubInterface) peer.Response {
// TODO add loggic adding new asset
}
func main() {
err := shim.Start(new(assetManagement))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
Next all you need is to invoke chaincode passing function name getAsset with asset ID and will get latest state for that asset. Here is the code based on Go SDK:
// Skipped initialization.
txRequest := apitxn.ChaincodeInvokeRequest{
Targets: []apitxn.ProposalProcessor{p},
Fcn: "getAsset",
Args: []string{"42"},
TransientMap: map[string][]byte{},
ChaincodeID: "assetChaincode",
}
proposalResponse, _, err := ch.SendTransactionProposal(txRequest)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%v\n", proposalResponse[0].ProposalResponse)
tx, err := ch.CreateTransaction(proposalResponse)
if err != nil {
fmt.Println(err)
return
}
txResponse, err := ch.SendTransaction(tx)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(txResponse[0])

How to make reading and writing to file concurent in Golang?

I setup a webserver and I use my own package where I do some write/read from and to files. When the server gets a tcp connection, I start a different goroutine to handle the request for each connection. In the request handler func, I call the func DoSomething() of some_package.
Here's the code for web_server.go:
package main
import (
sp "./some_package"
"log"
"net"
"os"
"net/http"
)
func main() {
l, err := net.Listen("tcp", "0.0.0.0" + ":" + "4567")
if err != nil {
log.Println("Error listening:", err.Error())
os.Exit(1)
}
defer l.Close()
log.Println("Listening on 0.0.0.0:4567")
go func() {
for {
// Listen for an incoming connection.
conn, err := l.Accept()
if err != nil {
log.Println("Error accepting: ", err.Error())
os.Exit(1)
}
// Handle connections in a new goroutine.
go handlerFunction(conn)
}
}()
log.Printf("Setting up the Webserver...")
err = http.ListenAndServe("0.0.0.0:"+"4568", nil)
if err != nil {
log.Fatal(err)
}
}
func handlerFunction(conn net.Conn) {
defer conn.Close()
sp.DoSomething()
}
The function DoSomething() reads and writes to file. You can see the code where it is declared in the package:
package some_package
import (
"io/ioutil"
"strconv"
"os"
"log"
)
func IncrementValue(pastValue string)(newValue string){
newValueInt, _ := strconv.Atoi(pastValue)
return strconv.Itoa(newValueInt + 1)
}
func DoSomething() (err error){
initialValue := "1"
filename := "myFile.txt"
if _, err := os.Stat(filename); err == nil {
someText, err := ioutil.ReadFile(filename)
if err != nil {
log.Printf("Error reading")
return err
}
newValue := IncrementValue(string(someText))
err = ioutil.WriteFile(filename,[]byte(newValue), 0644)
if err != nil {
return err
}
}else{
err = ioutil.WriteFile(filename,[]byte(initialValue), 0644)
if err != nil {
return err
}
}
return
}
How can I use a locking mechanism like mutex.Lock and mutex.Unlock in this case to make the reading and writing to file concurrent so when one routine which is currently writing can stop the other from reading till the first one writes to file successfully?
Is my example suitable to be concurrent when reading or writing to file?
Is this the right approach to do so? Thank You
You can't make the reading and writing of a file concurrent (well, it's possible, but not with the access pattern you're describing). Use a single mutex to serialize all access to your file:
var fileMutex sync.Mutex
func DoSomething() {
fileMutex.Lock()
defer fileMutex.Unlock()
//...
}

How to feed a password to SSH?

I need to use password authenticated scp to download a file from a server. How do I do so using Go? Tried the following code, but it doesn't pass in the password.
package main
import (
"os/exec"
"time"
)
func main() {
password := "password"
cmd := exec.Command("scp", "admin#192.168.1.150:file", "file")
in, err := cmd.StdinPipe()
if err != nil {
panic(err)
}
defer in.Close()
out, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
defer out.Close()
if err = cmd.Run(); err != nil {
panic(err)
}
go func() {
time.Sleep(10 * time.Second)
_, err = in.Write([]byte(password + "\n"))
if err != nil {
panic(err)
}
}()
}
Edit: I ended up using the gexpect (github.com/ThomasRooney/gexpect) library.
package main
import (
"github.com/ThomasRooney/gexpect"
"log"
)
func main() {
child, err := gexpect.Spawn("scp admin#192.168.1.150:file file")
if err != nil {
log.Fatalln(err)
}
child.Expect("password:")
child.SendLine("password")
child.Interact()
child.Close()
}
The answer to this self-answered question might help:
Golang write input and get output from terminal process
at least, he mentions in the answer that he "was able to get ssh access working with a password", which is not mentioned explicitly in the question - that's why you probably didn't find it while searching the site?

Resources