Extend containerd and ctr with support for (extensible) image encryption (OpenPGP, JWE, ...)

This series of patches implements support for image encryption in containerd and ctr based on OpenPGP (GnuPGP). It is a followup to an issue raised for the opencontainers image specification to support encrypted containers. This pull request is mean to start a discussion based on an implementation.
The following provides example steps for how to encrypt and decrypt images as well as manage the list of recipients that can decrypt an image. It is currently not possible to start an encrypted image right away but one has to go through the step of decryption first. Support for starting an encrypted image could be added following acceptance of an initial set of patches. Also, there are currently no test cases covering this code.
Regards,
Stefan, Brandon, and Harshal
Note: The demo below encrypts an image for<EMAIL_ADDRESS>for which the demonstrator also has the private key. You may want to create your own gpg private key, associate it with your email, and repeat the demo with your new key.
Encrypted Container Images Containerd Demo
Overview
As in this issue, we propose a new media type for encrypted layers of a container image. This addition would facilitate the ecosystem for encrypted container images. This allows users with stricter trust requirements to be able ensure end-to-end encryption from build to runtime. In addition, it allows users to use a centralized managed repository (i.e. Docker Hub) without any risk of their images being compromised.
Today, we have implemented an encrypt/decrypt interface in containerd to perform the cryptographic operations on existing images based on the openpgp protocol RFC4880. The client, ctr, initiating the procedures manages the keys via gpg/gpg2, and is able to encrypt specific layers, as well as add/remove recipients from images.
The current implementation can be found here:
Basic Functions Demo
We will demonstrate the ability to encrypt the public alpine:latest image, and push the encrypted image to a docker registry. We will then show the pulling of the encrypted image from the repository and decryption of the image for use.
 Thus, we allow the use of gpg to manage keys. In the event that we want to perform encryption for a trusted party, we want to retrieve their public key so that we can securely share encryption keys with them.
First, we use the gpg client to retrieve the public key of a recipient we want to create an encrypted image for. In this case, we want to create an encrypted image for Brandon Lum<EMAIL_ADDRESS>$ gpg --keyserver hkp://pgp.mit.edu --search-keys<EMAIL_ADDRESS>gpg: searching for<EMAIL_ADDRESS>from hkp server pgp.mit.edu
(1)	Brandon Lum<EMAIL_ADDRESS>	  4096 bit RSA key A289B9F0, created: 2018-07-19
(2)	Brandon Lum<EMAIL_ADDRESS>	  4096 bit RSA key 795B4468, created: 2010-10-10
Keys 1-2 of 2 for<EMAIL_ADDRESS> Enter number(s), N)ext, or Q)uit > 1
gpg: requesting key A289B9F0 from hkp server pgp.mit.edu
gpg: key A289B9F0: "Brandon Lum<EMAIL_ADDRESS>not changed
gpg: Total number processed: 1
gpg:              unchanged: 1

$ gpg --list-keys
/home/lumjjb/.gnupg/pubring.gpg
-------------------------------
pub   4096R/A289B9F0 2018-07-19
uid                  Brandon Lum<EMAIL_ADDRESS>sub   4096R/A512CBB7 2018-07-19

We then signify that we want to encrypt the image docker.io/library/alpine:latest for user<EMAIL_ADDRESS>and tag the new encrypted image as docker.io/lumjjb/alpine:enc.
$ sudo ctr images encrypt --recipient<EMAIL_ADDRESS>docker.io/library/alpine:latest docker.io/lumjjb/alpine:enc
Encrypting docker.io/library/alpine:latest to docker.io/lumjjb/alpine:enc

$ sudo ctr images list
REF                             TYPE                                                      DIGEST                                                                  SIZE    PLATFORMS                                                                   LABELS
docker.io/library/alpine:latest application/vnd.docker.distribution.manifest.list.v2+json sha256:7043076348bf5040220df6ad703798fd8593a0918d06d3ce30c6c93be117e430 2.1 MiB linux/386,linux/amd64,linux/arm/v6,linux/arm64/v8,linux/ppc64le,linux/s390x -
docker.io/lumjjb/alpine:enc     application/vnd.docker.distribution.manifest.list.v2+json sha256:8152ae17bcfd47664f251e33bfef5051946973668615640637653a7f44e8803d 2.1 MiB linux/386,linux/amd64,linux/arm/v6,linux/arm64/v8,linux/ppc64le,linux/s390x -

We then push the encrypted image to the Docker Hub registry.
TODO: Push

Containerd client decrypt
As user, Brandon Lum<EMAIL_ADDRESS>we will pull, decrypt and use the encrypted alpine:latest image.
We first pull the encrypted image we pushed earlier:
TODO: PULL

$ sudo ctr images list
REF                             TYPE                                                      DIGEST                                                                  SIZE    PLATFORMS                                                                   LABELS
docker.io/lumjjb/alpine:enc     application/vnd.docker.distribution.manifest.list.v2+json sha256:8152ae17bcfd47664f251e33bfef5051946973668615640637653a7f44e8803d 2.1 MiB linux/386,linux/amd64,linux/arm/v6,linux/arm64/v8,linux/ppc64le,linux/s390x -

We then verify that we have the private key necessary to decrypt the wrapped keys, and Proceed with performing a decryption. We note that our private key is passphrase protected. And thus we are asked to enter our passphrase to decrypt the encrypted image. In the command we specify that we would like to decrypt the image docker.io/lumjjb/alpine:enc and save the decrypted image as docker.io/lumjjb/alpine:plain. We note that the decryption process will automatically derive what keys are
required from the annotations within the layers' manifests.
$ gpg --list-secret-keys
/home/lumjjb/.gnupg/secring.gpg
-------------------------------
sec   4096R/A289B9F0 2018-07-19
uid                  Brandon Lum<EMAIL_ADDRESS>ssb   4096R/A512CBB7 2018-07-19

$ sudo ctr images decrypt docker.io/lumjjb/alpine:enc docker.io/lumjjb/alpine:plain
Decrypting docker.io/lumjjb/alpine:enc to docker.io/lumjjb/alpine:plain
Passphrase required for Key id 0x760a6f54a512cbb7:
sec   rsa4096/A289B9F0 2018-07-19 [SC]
uid         [ unknown] Brandon Lum<EMAIL_ADDRESS>ssb   rsa4096/A512CBB7 2018-07-19 [E]

Enter passphrase for key with Id 0x760a6f54a512cbb7:

$ sudo ctr images list
REF                             TYPE                                                      DIGEST                                                                  SIZE    PLATFORMS                                                                   LABELS
docker.io/lumjjb/alpine:enc     application/vnd.docker.distribution.manifest.list.v2+json sha256:8152ae17bcfd47664f251e33bfef5051946973668615640637653a7f44e8803d 2.1 MiB linux/386,linux/amd64,linux/arm/v6,linux/arm64/v8,linux/ppc64le,linux/s390x -
docker.io/lumjjb/alpine:plain   application/vnd.docker.distribution.manifest.list.v2+json sha256:7a90bfa518320e7c0fe4045776cfc1e7de201ae1d1b6b1322d3c889de3b57ea2 2.1 MiB linux/386,linux/amd64,linux/arm/v6,linux/arm64/v8,linux/ppc64le,linux/s390x -

Additional functionality
We have implemented additional functionality to provide convenience and support for various usecases:
Show layer info.
We allow users to show the layer information of an image - as well as the keys associated with the image. This will allow the user to check what keys are required or who is allowed to decrypt the image.
$ sudo ctr images layerinfo docker.io/lumjjb/alpine:enc
   #                                                                    DIGEST         PLATFORM      SIZE   ENCRYPTION              KEY IDS
   0   sha256:43458cf20345ded65a9aca9e37252255b031226f53fb868099cdc5985138b621      linux/amd64   2207033          pgp   0x760a6f54a512cbb7
   0   sha256:10e8be14a58242185c7348f8988d67a4a0bc1534142bdaaf9a0e8dd2dd3fec39     linux/arm/v6   2146483          pgp   0x760a6f54a512cbb7
   1   sha256:1a0845f6ac68b0895bcb90e73188735bfb2587345f9ebaac1d9bf6429a702ba9     linux/arm/v6       258          pgp   0x760a6f54a512cbb7
   0   sha256:4126f7cc423377e26a53e937529474bcc2c4633d6d2056f0cdab9a7a5d59b8f8   linux/arm64/v8   2099981          pgp   0x760a6f54a512cbb7
   1   sha256:a3c6e403d8e067e31caf3e0cdec69bf2df65e96b1325549328cf90b2d13cc3a7   linux/arm64/v8       250          pgp   0x760a6f54a512cbb7
   0   sha256:5584fa94c65b898b4d20a86d7c157941e0cbbbd49875de83f7f52dfae30fe552        linux/386   2271417          pgp   0x760a6f54a512cbb7
   1   sha256:e62eb8fecfa72b0d307c630d7ca5e3a3312d6a4fb3c0c80e6a3cbafa86bbdf9c        linux/386       258          pgp   0x760a6f54a512cbb7
   0   sha256:a7eaa2d506d40bd8a2714a28e1aec2ac50dfae250bc292f25d8d15300f243ccb    linux/ppc64le   2195355          pgp   0x760a6f54a512cbb7
   1   sha256:4e0fa7b800e9e00a51b9220549b3973b5af23e619e0407d5829717166b35bb16    linux/ppc64le       250          pgp   0x760a6f54a512cbb7
   0   sha256:3ac40479ee23a4e0cf693aec2ff31a7cf3cb7ca6f99bb5b27e1d74cca15452b6      linux/s390x   2307983          pgp   0x760a6f54a512cbb7
   1   sha256:816052ed0b36d240b70d6f86e27b1ff6a53c09363d80837ffb6e5a1595959ac4      linux/s390x       258          pgp   0x760a6f54a512cbb7

Encrypt specific layer
Ability to encrypt specific layers have also been added. This will allow the user to benefit from deduplication of layers of non-secret code/data. I.e. Building a secret application on top of an ubuntu image, the ubuntu part of the image is not secret. Layers are indexed bottom up, with negative layers pointing to top layers. In this example, we specify the encryption of the topmost layer by passing in the --layer -1 flag.
$ sudo ctr images encrypt --recipient<EMAIL_ADDRESS>--layer -1 docker.io/library/alpine:latest docker.io/lumjjb/alpine:enc-layer
Encrypting docker.io/library/alpine:latest to docker.io/lumjjb/alpine:enc-layer

$ sudo ctr images layerinfo docker.io/lumjjb/alpine:enc-layer
   #                                                                    DIGEST         PLATFORM      SIZE   ENCRYPTION              KEY IDS
   0   sha256:444b8ce03329f2615f1567b7fbc36df6b63f804998a41dc8b4b8d171c0360118      linux/amd64   2207033          pgp   0x760a6f54a512cbb7
   0   sha256:ee7d700abbf209aa401ef5d53f86af298a25e8154b3259036e9307d08f255c5d     linux/arm/v6   2145998
   1   sha256:5b59acb1359c7689836884da73320c3f05d28beae142d4d5ab4543638d3c8abc     linux/arm/v6       258          pgp   0x760a6f54a512cbb7
   0   sha256:47e04371c99027fae42871b720fdc6cdddcb65062bfa05f0c3bb0a594cb5bbbd   linux/arm64/v8   2099514
   1   sha256:1be8013b02bfc605de021e0297626755251f520e726265554056e6093a6245d8   linux/arm64/v8       250          pgp   0x760a6f54a512cbb7
   0   sha256:ef15772113129a5330876ce10683bbf6509a4c4c99b3a99894dcbc7560975052        linux/386   2270920
   1   sha256:9e5b0d854e7c35f6b346d205bbc8814b4212f20295213986a43d18e1972d7552        linux/386       258          pgp   0x760a6f54a512cbb7
   0   sha256:e642bcb5b1890a07dd2fc8be2bc35edf5e2b651d4993e71caef03b4b43ace970    linux/ppc64le   2194861
   1   sha256:b7bb6214c5eaaa69a3af200db7c93a237f244a29f261f8a80fe60d71eed9615e    linux/ppc64le       250          pgp   0x760a6f54a512cbb7
   0   sha256:cdf21ace94188d512903eea53ea8559677e0e6ffd5d6a180a1d88c118abc96fc      linux/s390x   2307471
   1   sha256:f9c6417700af0d44f473f477b9db997d48796695a3b226cf800d506e619bfd09      linux/s390x       258          pgp   0x760a6f54a512cbb7

Add recipients
By issuing the encrypt command on an already existing image, it allows the addition of recipients. This allows the adding of recipient wrapped keys to the image without re-encryption of the image. Thus allowing the addition of recipients to benefit from re-uploading the large encrypted blob. Because we need the symmetric key needs to be re-wrapped, the adding of recipients requires access to at least one of the existing wrapped keys of the image.
$ sudo ctr images encrypt --recipient<EMAIL_ADDRESS>docker.io/lumjjb/alpine:enc docker.io/lumjjb/alpine:enc-add
Encrypting docker.io/lumjjb/alpine:enc to docker.io/lumjjb/alpine:enc-add
Passphrase required for Key id 0x760a6f54a512cbb7:
sec   rsa4096/A289B9F0 2018-07-19 [SC]
uid         [ unknown] Brandon Lum<EMAIL_ADDRESS>ssb   rsa4096/A512CBB7 2018-07-19 [E]

Enter passphrase for key with Id 0x760a6f54a512cbb7:
lumjjb@lumjjb-ThinkPad-P50:~/go/src/github.com/containerd/containerd$ sudo ctr images layerinfo docker.io/lumjjb/alpine:enc-add
   #                                                                    DIGEST         PLATFORM      SIZE   ENCRYPTION                                  KEY IDS
   0   sha256:43458cf20345ded65a9aca9e37252255b031226f53fb868099cdc5985138b621      linux/amd64   2207033          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   0   sha256:10e8be14a58242185c7348f8988d67a4a0bc1534142bdaaf9a0e8dd2dd3fec39     linux/arm/v6   2146483          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   1   sha256:1a0845f6ac68b0895bcb90e73188735bfb2587345f9ebaac1d9bf6429a702ba9     linux/arm/v6       258          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   0   sha256:4126f7cc423377e26a53e937529474bcc2c4633d6d2056f0cdab9a7a5d59b8f8   linux/arm64/v8   2099981          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   1   sha256:a3c6e403d8e067e31caf3e0cdec69bf2df65e96b1325549328cf90b2d13cc3a7   linux/arm64/v8       250          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   0   sha256:5584fa94c65b898b4d20a86d7c157941e0cbbbd49875de83f7f52dfae30fe552        linux/386   2271417          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   1   sha256:e62eb8fecfa72b0d307c630d7ca5e3a3312d6a4fb3c0c80e6a3cbafa86bbdf9c        linux/386       258          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   0   sha256:a7eaa2d506d40bd8a2714a28e1aec2ac50dfae250bc292f25d8d15300f243ccb    linux/ppc64le   2195355          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   1   sha256:4e0fa7b800e9e00a51b9220549b3973b5af23e619e0407d5829717166b35bb16    linux/ppc64le       250          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   0   sha256:3ac40479ee23a4e0cf693aec2ff31a7cf3cb7ca6f99bb5b27e1d74cca15452b6      linux/s390x   2307983          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f
   1   sha256:816052ed0b36d240b70d6f86e27b1ff6a53c09363d80837ffb6e5a1595959ac4      linux/s390x       258          pgp   0x760a6f54a512cbb7, 0x2a7ba8ea1cff3e8f

Remove recipients
This allows the removal of a recipient of an encrypted image. This is done by issuing the --remove flag.
$ sudo ctr images encrypt --remove --recipient<EMAIL_ADDRESS>docker.io/lumjjb/alpine:enc-add docker.io/lumjjb/alpine:enc-removed
Encrypting docker.io/lumjjb/alpine:enc-add to docker.io/lumjjb/alpine:enc-removed
lumjjb@lumjjb-ThinkPad-P50:~/go/src/github.com/containerd/containerd$ sudo ctr images layerinfo docker.io/lumjjb/alpine:enc-removed
   #                                                                    DIGEST         PLATFORM      SIZE   ENCRYPTION              KEY IDS
   0   sha256:43458cf20345ded65a9aca9e37252255b031226f53fb868099cdc5985138b621      linux/amd64   2207033          pgp   0x760a6f54a512cbb7
   0   sha256:10e8be14a58242185c7348f8988d67a4a0bc1534142bdaaf9a0e8dd2dd3fec39     linux/arm/v6   2146483          pgp   0x760a6f54a512cbb7
   1   sha256:1a0845f6ac68b0895bcb90e73188735bfb2587345f9ebaac1d9bf6429a702ba9     linux/arm/v6       258          pgp   0x760a6f54a512cbb7
   0   sha256:4126f7cc423377e26a53e937529474bcc2c4633d6d2056f0cdab9a7a5d59b8f8   linux/arm64/v8   2099981          pgp   0x760a6f54a512cbb7
   1   sha256:a3c6e403d8e067e31caf3e0cdec69bf2df65e96b1325549328cf90b2d13cc3a7   linux/arm64/v8       250          pgp   0x760a6f54a512cbb7
   0   sha256:5584fa94c65b898b4d20a86d7c157941e0cbbbd49875de83f7f52dfae30fe552        linux/386   2271417          pgp   0x760a6f54a512cbb7
   1   sha256:e62eb8fecfa72b0d307c630d7ca5e3a3312d6a4fb3c0c80e6a3cbafa86bbdf9c        linux/386       258          pgp   0x760a6f54a512cbb7
   0   sha256:a7eaa2d506d40bd8a2714a28e1aec2ac50dfae250bc292f25d8d15300f243ccb    linux/ppc64le   2195355          pgp   0x760a6f54a512cbb7
   1   sha256:4e0fa7b800e9e00a51b9220549b3973b5af23e619e0407d5829717166b35bb16    linux/ppc64le       250          pgp   0x760a6f54a512cbb7
   0   sha256:3ac40479ee23a4e0cf693aec2ff31a7cf3cb7ca6f99bb5b27e1d74cca15452b6      linux/s390x   2307983          pgp   0x760a6f54a512cbb7
   1   sha256:816052ed0b36d240b70d6f86e27b1ff6a53c09363d80837ffb6e5a1595959ac4      linux/s390x       258          pgp   0x760a6f54a512cbb7

Next steps
Implement handling of encrypted container mediatype by containerd
Currently, the way decryption of an image is done is via the use of an explicit ctr client. We would like for runtimes to be able to decrypt the images directly when running them. i.e. containerd daemon should know how to handle the mediaType of +pgp and decrypt the image as part of the image unpacking process.
Passing of keys to runtime
We note that when having containerd handle the encrypted mediatype, it would have to be able to access keys for the decryption process. However, we need to be able to properly handle permissions of access of keys.

Codecov Report

Merging #2532 into master will decrease coverage by 0.64%.
The diff coverage is 0.68%.

@@            Coverage Diff             @@
##           master    #2532      +/-   ##
==========================================
- Coverage   45.05%   44.41%   -0.65%
==========================================
  Files          94       94
  Lines        9796     9941     +145
==========================================
+ Hits         4414     4415       +1
- Misses       4662     4806     +144
  Partials      720      720

Flag
Coverage Δ

#linux
48.4% <0.9%> (-0.68%)
:arrow_down:

#windows
40.86% <0.68%> (-0.71%)
:arrow_down:

Impacted Files
Coverage Δ

platforms/platforms.go
70.87% <0%> (-13.04%)
:arrow_down:

metadata/images.go
38.72% <0.77%> (-19.75%)
:arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1ba4aa0...5843306. Read the comment docs.

The current implementation requires OpenPGP keys and results in an OpenPGP formatted blob for the wrapped keys and the bulk data. This could be abstracted to only require a public key for encryption as input and result in a common format for the wrapped session key (wrapped once for each recipient) and the symmetrically encrypted bulk data, though I think this may require the definition of a new format. I just want to mention this here.
So, for OpenPGP RFC 4880 defines the wrapped session key as follows :
struct  openpgp_wrapped_key {
  uint8_t version;
  uint8_t keyid[8];
  uint8_t public_key_algo;
  uint8_t wrapped_key[]; // like 256 bytes
};

For PKCS#7 the wrapped session key is defined like this:
RecipientInfo ::= SEQUENCE {
     version Version,
     issuerAndSerialNumber IssuerAndSerialNumber,
     keyEncryptionAlgorithm

       KeyEncryptionAlgorithmIdentifier,
     encryptedKey EncryptedKey }

   EncryptedKey ::= OCTET STRING

I would argue one could come up with a data structure like this here in exemplary JSON format that could accommodate any of these wrapped keys:
{
    "key_type": <key_type>,
    "specifics": {
        <key type specific map>
    },
}

For OpenPGP this would look like this:
{
    "key_type": "OpenPGP",
    "specifics" : {
        "version": "3",
        "keyid": 0x1234567890abcdef,
        "public_key_algo": OPENPGPG_RSA_VALUE,
        "wrapped_key": "<base64 encoded wrapped key"
    }
}

For PKCS#7 this would look like this:
{
    "key_type": "PKCS7",
    "specifics": {
        "version": ...,
        "issuerAndSerialNumber": "..."
        "keyEncryptionAlgorithm": PKCS7_RSA_VALUE,
        "encryptedKey": "<base64 encoded wrapped key>"
    }
}

The image encryption process would create a list of the above two examples, one such map for each recipient and specific to the recipient's key. The JSON format is just an example here. The bulk data, which are the layer data, could then be written out in a format like this one that can be handled easily:
struct bulk_data {
    uint8_t symmetric_encryption_algo; // AES128+CBC etc.
    uint8_t iv[];
    <other fields>
    uint8_t symmetrically_encrypted_data[]; // encrypted layer data
};

The advantage of this type of format would be that this should allow us to handle any type of public key as input (OpenPGP, OpenSSL, x509, JWK, TPM1.2, TPM2, SmartCards, HSMs) and we can define the 'specifics' for the individual key type. In some cases 'specifics' could point to some sort of config file where the IP address is located to reach the device to handle the decryption. The specific technology would be required to unwrap the symmetric key using the private key. The symmetric key could then generically be used to decrypt the bulk data.
This would avoid possibly different formats for the encrypted layer and where the current OpenPGP implementation produces a very specific format.

We could also use JWK to represent some of the keys. Some types of keys, like for hardware security modules or TPMs, could be provided as JWKs to the person encrypting the image and that person could then pass multiple of those on the command line. The JWK format for hardware devices' keys is not standardized in JWK specs afaics, though, but using JWK provides a path to support it in the future. Some other keys, like in the more common PEM format or for PGP recipients could still be passed directly. The client tool may need to detect the format:
ctr images encrypt --recipient foo.jwk --recipient<EMAIL_ADDRESS>--recipient foo.pem myimage:latest myimage.encrypted:latest

Internally we should probably convert the pem and pgp type of keys into a map[string]string like JWK and could then use JWK fields to represent the 'specifics' map above when storing metadata for the wrapped key. Though this is all outside some standard and would have to be agreed upon.

JWE  would probably be the best (unifying) standard to follow, though we probably couldn't follow it to the letter, either. The encrypted bulk data is part of the JSON there and in the layer case it would be a file. Also for the standards route I haven't seen OpenPGP type of keys representation in JWK format.

@stevvooe @dmcgowan
Next version supports recipients with different key types (so far: PEM,DER,OpenPGP) and supports JWE (PEM, DER) and OpenPGP (OpenPGP keys) for the encryption of the symmetric key that in turn handles the layer encryption:
$ ./bin/ctr images encrypt --recipient mypubkey.der --recipient<EMAIL_ADDRESS>docker.io/library/alpine:latest docker.io/library/alpine:enc
Encrypting docker.io/library/alpine:latest to docker.io/library/alpine:enc
$ ./bin/ctr images layerinfo docker.io/library/alpine:enc
   #                                                                    DIGEST         PLATFORM      SIZE   ENCRYPTION           RECIPIENTS
   0   sha256:f260961a5ca25f350126de9a3d5044c14d36fd314302d3e718b2c8f08fef6500      linux/amd64   2206558      jwe,pgp   0x31b1a243cca8327c
   0   sha256:28e2e6e9a236ee7c19110dd9ea4c829a15e9245c94274de3cdada177a17dfac9     linux/arm/v6   2146014      pgp,jwe   0x31b1a243cca8327c
   1   sha256:ffe2c7d70e71b2876a93fc668a1d504174b6c0ae822b9e7308b23d92a857b066     linux/arm/v6       191      pgp,jwe   0x31b1a243cca8327c
   0   sha256:32601601b1b44949671f155e5a68954e5d6f5dee97a8e36d5244f4518e0ecdc3   linux/arm64/v8   2099530      pgp,jwe   0x31b1a243cca8327c
   1   sha256:9e2bd6932b21044b01ee79fae24ccb17da3871b3b402ccbd6409320b0ed74a22   linux/arm64/v8       192      pgp,jwe   0x31b1a243cca8327c
   0   sha256:02106242e67fe50370ba7f20b596667d585cab02885b88466369cd70236b9152        linux/386   2270936      pgp,jwe   0x31b1a243cca8327c
   1   sha256:dfd5346996fea573115dd7befa173c83bfcdbcfcf0a798e76ab730e17f76d41e        linux/386       191      jwe,pgp   0x31b1a243cca8327c
   0   sha256:4b7bd6006c9b872ea5d5d961f066966c14e0d6ebd4ca19fb8c4f0778fb7ed5b7    linux/ppc64le   2194877      pgp,jwe   0x31b1a243cca8327c
   1   sha256:ef47bc61579f472562db3812c8625fa0cc171911376968bf8dfc897c8a82b8cd    linux/ppc64le       192      pgp,jwe   0x31b1a243cca8327c
   0   sha256:2fa06b1ff9a8f6c998c6e084c35b9d8e78c2de5a573fb4d1bac5aba3e21e4b81      linux/s390x   2307487      pgp,jwe   0x31b1a243cca8327c
   1   sha256:8fec4adf9eabaf9abf347708ece813ba48092196c47f336ae65d68a2ac19dea6      linux/s390x       191      jwe,pgp   0x31b1a243cca8327c
$ ./bin/ctr images layerinfo docker.io/library/alpine:enc
   #                                                                    DIGEST         PLATFORM      SIZE   ENCRYPTION           RECIPIENTS
   0   sha256:f08d7546ef57875d3a955ce6ec283207ed43b6af435e9b6986c4339bac4ad751      linux/amd64   2206558      jwe,pgp   0x31b1a243cca8327c
   0   sha256:cadd080d7d536dbc5e24fc042741b2e42cca30eeb0cfffa33212c2a6b352ad9b     linux/arm/v6   2146014      jwe,pgp   0x31b1a243cca8327c
   1   sha256:b403f3d81c8e3d72a5b6a04f9cbf97cc01c0a0de0b2a7f6765c0dad90eabe817     linux/arm/v6       191      jwe,pgp   0x31b1a243cca8327c
   0   sha256:e991388e789a5e6b4245fb75bbb40414f005fb4aa119211d961bfba8888bd6d8   linux/arm64/v8   2099530      jwe,pgp   0x31b1a243cca8327c
   1   sha256:3ce7819e926b326e4fc3ae0f3b0ba291104048d032f0d8e288a781bb9c325263   linux/arm64/v8       192      jwe,pgp   0x31b1a243cca8327c
   0   sha256:1402e1efe8d82d25c1afe9e6d193c3feb702d2761f125711634f9918b2f0ca9b        linux/386   2270936      jwe,pgp   0x31b1a243cca8327c
   1   sha256:01187aab1b4f9fb9dc363c9d364feb489eb9df2d3b4d7e8a071b05f40093b6ab        linux/386       191      jwe,pgp   0x31b1a243cca8327c
   0   sha256:a5fea124b55c297ed02283c7de9475985113a946c52c0872b38ab001653391a9    linux/ppc64le   2194877      jwe,pgp   0x31b1a243cca8327c
   1   sha256:34dd7a216a042b98e852b007f36ddb2576dfe0a97d9b4b3a7b2050d1662003f9    linux/ppc64le       192      jwe,pgp   0x31b1a243cca8327c
   0   sha256:751813c9e9cdb8374d1aec0fe11cde9be0b8cda9f45fc032435ea48ddb445016      linux/s390x   2307487      jwe,pgp   0x31b1a243cca8327c
   1   sha256:46033bd0b600a1ff37d1604f3d0c425efd1e64b1ba493dec0a29543264c8aaf3      linux/s390x       191      jwe,pgp   0x31b1a243cca8327c
[stefanb@sbct-3 containerd]$ ./bin/ctr images decrypt docker.io/library/alpine:enc docker.io/library/alpine:dec --key ./mykey.der
Decrypting docker.io/library/alpine:enc to docker.io/library/alpine:dec

[stefanb@sbct-3 containerd]$ ./bin/ctr images layerinfo docker.io/library/alpine:dec
   #                                                                    DIGEST         PLATFORM      SIZE   ENCRYPTION   RECIPIENTS
   0   sha256:8e3ba11ec2a2b39ab372c60c16b421536e50e5ce64a0bc81765c2e38381bcff6      linux/amd64   2206542
   0   sha256:ee7d700abbf209aa401ef5d53f86af298a25e8154b3259036e9307d08f255c5d     linux/arm/v6   2145998
   1   sha256:a1653f4692c1ccea69cd46121d4f1371957f66e97a20141371dd4da10ebaec19     linux/arm/v6       175
   0   sha256:47e04371c99027fae42871b720fdc6cdddcb65062bfa05f0c3bb0a594cb5bbbd   linux/arm64/v8   2099514
   1   sha256:b4103359e1ecd9a7253d8b8a041d4e81db1ff4a5e1950bc0e02305d221c9e6c2   linux/arm64/v8       176
   0   sha256:ef15772113129a5330876ce10683bbf6509a4c4c99b3a99894dcbc7560975052        linux/386   2270920
   1   sha256:6df692b84cf35e6038d677f9ab7de2a3c671c57671043da1d20d99252e0d9c42        linux/386       175
   0   sha256:e642bcb5b1890a07dd2fc8be2bc35edf5e2b651d4993e71caef03b4b43ace970    linux/ppc64le   2194861
   1   sha256:2e09410b1fce4c4630b3bc57c6ee158ee9821ec415d0acaa1607b9e0bcf76d91    linux/ppc64le       176
   0   sha256:cdf21ace94188d512903eea53ea8559677e0e6ffd5d6a180a1d88c118abc96fc      linux/s390x   2307471
   1   sha256:ea178433f2f09080fbbf77f09da1b16d588b7ced380d495a2f2ad00d28468338      linux/s390x       175
[stefanb@sbct-3 containerd]$ ./bin/ctr images decrypt docker.io/library/alpine:enc docker.io/library/alpine:dec2 --key ./1F0CB5C638F9153E50C299648671DD2E305C10E8.bin
Decrypting docker.io/library/alpine:enc to docker.io/library/alpine:dec2

[stefanb@sbct-3 containerd]$ ./bin/ctr images layerinfo docker.io/library/alpine:dec2
   #                                                                    DIGEST         PLATFORM      SIZE   ENCRYPTION   RECIPIENTS
   0   sha256:8e3ba11ec2a2b39ab372c60c16b421536e50e5ce64a0bc81765c2e38381bcff6      linux/amd64   2206542
   0   sha256:ee7d700abbf209aa401ef5d53f86af298a25e8154b3259036e9307d08f255c5d     linux/arm/v6   2145998
   1   sha256:a1653f4692c1ccea69cd46121d4f1371957f66e97a20141371dd4da10ebaec19     linux/arm/v6       175
   0   sha256:47e04371c99027fae42871b720fdc6cdddcb65062bfa05f0c3bb0a594cb5bbbd   linux/arm64/v8   2099514
   1   sha256:b4103359e1ecd9a7253d8b8a041d4e81db1ff4a5e1950bc0e02305d221c9e6c2   linux/arm64/v8       176
   0   sha256:ef15772113129a5330876ce10683bbf6509a4c4c99b3a99894dcbc7560975052        linux/386   2270920
   1   sha256:6df692b84cf35e6038d677f9ab7de2a3c671c57671043da1d20d99252e0d9c42        linux/386       175
   0   sha256:e642bcb5b1890a07dd2fc8be2bc35edf5e2b651d4993e71caef03b4b43ace970    linux/ppc64le   2194861
   1   sha256:2e09410b1fce4c4630b3bc57c6ee158ee9821ec415d0acaa1607b9e0bcf76d91    linux/ppc64le       176
   0   sha256:cdf21ace94188d512903eea53ea8559677e0e6ffd5d6a180a1d88c118abc96fc      linux/s390x   2307471
   1   sha256:ea178433f2f09080fbbf77f09da1b16d588b7ced380d495a2f2ad00d28468338      linux/s390x       175

Encryption to PGP, JWE, and PKCS7 recipients:
$ ./bin/ctr images encrypt --gpg-homedir /tmp/tmp.AGrSDkaSad/gpg2 --recipient<EMAIL_ADDRESS>--recipient<EMAIL_ADDRESS>--recipient /tmp/tmp.AGrSDkaSad/mypubkey.pem --recipient /tmp/tmp.AGrSDkaSad/mypubkey2.pem --recipient /tmp/tmp.AGrSDkaSad/clientcert.pem --recipient /tmp/tmp.AGrSDkaSad/client2cert.pem docker.io/library/alpine:latest docker.io/library/alpine:enc
Encrypting docker.io/library/alpine:latest to docker.io/library/alpine:enc
$ ./bin/ctr images layerinfo docker.io/library/alpine:enc
   #                                                                    DIGEST         PLATFORM      SIZE      ENCRYPTION                                               RECIPIENTS
   0   sha256:3427d6934e7749d556be6881a17265c9817abc6447df80a09c8eecc465c5bfb3      linux/amd64   2206947   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   0   sha256:d9a094b6b49fc760501d44ae96f19284e86db0a51b979756ca8a0df4a2746c79     linux/arm/v6   2146469   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   1   sha256:ef87d8b3048d8f1f7af7605328f63aab078a1433116dc15738989551184d7a87     linux/arm/v6       191   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   0   sha256:4b0872dff46806a4037c5f158d1d17d5252c9e1f421b7c61445f1a64f6a853a8   linux/arm64/v8   2099778   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   1   sha256:fe022206e6848082f9c1d6e69974157af70ad56bf8698d89e1641d4598bf8ce9   linux/arm64/v8       192   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   0   sha256:d1fceb26d4a2dc1f30d05fd0f9567edb5997d504f044ad6486aecc3d5aaa9b4e        linux/386   2271476   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   1   sha256:383a3d4c6789667dbfb6b3742492c4a925315e750f99a5d664ff72f2bb0ae659        linux/386       191   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   0   sha256:8aea19b10fd75004ab8fd2d02df719c06528ad3539e686a2d26c933d53f25675    linux/ppc64le   2195242   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   1   sha256:965a60ab5513a2eee33f4d960b63ee347215eb31d06a4ed61f6d90d209462d76    linux/ppc64le       193   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   0   sha256:783541963cb4e52173193fe947bb7a7f7e5a6657a4cbbb6b8b077bbee7255605      linux/s390x   2307762   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]
   1   sha256:69d3260b3f5430ade9a3ee0f1b71a32a8e4ef268552beeae29930a8795dc54bf      linux/s390x       192   jwe,pgp,pkcs7   0x6d6d5017a3752cbd, 0xb0310f009d3abc2f, [jwe], [pkcs7]'

Won't touch it for a while...

@stevvooe This updated series of patches now adds a few patches that check for whether a user is authorized (entitled) to create a container from an image. The check is done inside client.NewContainer(), so on the 'lowest' part where all clients using the containerd client library will run into. We have support for ctr run and ctr container create to check authorization by requiring users to pass along the keys for an encrypted image from which they want to create a container.

@stevvooe @dmcgowan Regarding plugin: The image decryption is part of the diff plugin's Apply() function that now decrypts the container before unpacking and untarring it. This part is (currently) here: https://github.com/containerd/containerd/blob/0f871cabeb990b9c905d364c1c183b404ab756ff/diff/apply/apply.go#L86 Any comments on this?

Here is a diagram of the plugin changes from @harche  for convenience.

@dmcgowan Would be great if you can take another look - as discussed at Kubecon. Thanks!

Looking through the PR, I think the code could be vastly simplified by putting the decryption parameters inside of the image descriptor. Stripping out these annotations be storing would be trivial, but increasing the APIs and interfaces to account for another map has made the code more complex. The descriptors are mostly ephemeral and there are very few spots where those annotations would even get persisted (whether in the metadata database or newly created content), it would be cleaner just to find those places and ensure annotation storage is explicit.

Looking through the PR, I think the code could be vastly simplified by putting the decryption parameters inside of the image descriptor. Stripping out these annotations be storing would be trivial, but increasing the APIs and interfaces to account for another map has made the code more complex. The descriptors are mostly ephemeral and there are very few spots where those annotations would even get persisted (whether in the metadata database or newly created content), it would be cleaner just to find those places and ensure annotation storage is explicit.

There are different maps.The annotations in the image descriptor carry the encrypted material, which needs to be written out. The dcparameters are the decryption keys passed in via ctr or API for decrypting an image.

Looking through the PR, I think the code could be vastly simplified by putting the decryption parameters inside of the image descriptor.

What is the 'image' descriptor here? Is it the type images struct in images/images.go? I suppose it's not the ocispec.Descriptor. If you could be a bit more concrete about where to embed the dcparameters that would be helpful.

@stefanberger Maybe I don't fully understand it, but I think it is the annotations in ocispec.Descriptor, except that we would only augment the annotations when making the call, but would not be part of the image being stored eventually. I assume it would be something like augment annotations with dcparameters, consume via diff plugin (and it will strip off these parameters) and return the output descriptor leaving out these parameters...
I think @harche also mentioned that this may be neater in the future for when we have to do it with the config and manifest in the future rather than changing every calling interface for those descriptors, resulting in API complexity.

@dmcgowan Thank you for the feedback! I think this has given us some action items to work on! We will have a discussion about the changes and we hope to be done with a revised version by next week!
I think an outstanding question that we need clarification of what you had in mind with the keys as identifiers. I think we may be lacking on some context.

We discussed this previously, but this now encrypts and decrypts the entire layer in one operation in memory. This is problematic from the memory usage point of view for large layers, and also from the security point of view as you must not encrypt more than (just under) 64GB with AES with a single key and IV or the encryption can be broken. It really needs to be divided into smaller blocks that can be processed in memory, with ordering and termination constraints.
AFAIK the sane options are to use https://github.com/miscreant/miscreant/wiki/STREAM (has a Go implementation) or to use the code that Minio uses for encryption (somewhat more ad hoc).

@justincormack I'll try to switch it to miscreant's StreamEncryptor.

@justincormack I have now brought a Reader (for now content.ReaderAt) into our current symmetric key encryption function, which should make it easier to replace the encryptor with the one from 'miscreant', which seems to allow to just read chunks from the Reader. The trickier part is to convert the code that writes the data back into the content store while decreasing the memory consumption. The current interface requires me to pass an ocispec.Descriptor with the digest of the encrypted layer and its size. Obviously the digest will only be available after full encryption. We could get to the digest by using two passes for the encryption, which would require twice the time obviously, or by file renaming, or maybe there's another trick ? @dmcgowan @stevvooe
	p, annotations, err := encryption.EncryptLayer(cc.Ec, dataReader, desc)
	if err != nil {
		return ocispec.Descriptor{}, []byte{}, err
	}

	// were data touched ?
	if len(p) > 0 {
		size = int64(len(p))
		d = digest.FromBytes(p)
	} else {
		size = desc.Size
		d = desc.Digest
	}

	newDesc := ocispec.Descriptor{
		Digest:   d,
		Size:     size,
		Platform: desc.Platform,
	}
	[...]
	// some operations, such as changing recipients, may not touch the layer at all
	if len(p) > 0 {
		ref := fmt.Sprintf("layer-%s", newDesc.Digest.String())
		err = content.WriteBlob(ctx, cs, ref, containedReader{bytes.NewReader(p)}, newDesc)
	}

@lumjjb

I like the idea of the use of a file trick for the memory issue because of the need to support FIPS compliant crypto routines in the future - and since there are currently no foreseeable golang FIPS compliance, we will probably need to call out to an external binding/binary, which makes the memory problem hard to get around.

I'm guessing that if we use a temporary file to perform the encryption, that we would only incur one round of encryption but two rounds of reading/writing?

The idea would be to still do the encryption in the golang code. We now have a reader that reads from the plain stream and the blockcipher encryption code would be converted to return a reader that does the work by reading from the plain stream in MB sized chunks using that miscreant golang crypto library to do the chunkwise encryption and returning the encrypted chunks as part of a Read(). So all the encryption work would happen in that Read() function. The problem is the file hash creation and possibly that ref in the code above. I haven't looked at it very deeply yet. I believe the client sends the already encrypted data to the server via grpc and normally passes along the sha256 hash of the data which the server side uses as the filename. In our case we would have to have some sort of special filename that triggers the server side to calculate the sha256 hash while writing the data to the file and then does the renaming of the file at the end. At least that's what I was thinking. I am not sure how the ref is used and whether the client side uses it or the server side uses it. If the client side uses it we may have to calculate the sha256 on the client side as well and then create that ref string once we have the have.
If you want to be able to call an external tool to do the encryption, please have a look at it since this may not be so straight forward.

So I have been trying out a few things by trying to avoid setting the size (set it to 0) in the ocispec.Descriptor when writing the encrypted blob and trying to determine the size of the in the Copy() function in helpers.go. I always ran into some problems with the content store returning 0 for the size. I haven't even tried setting the digest in the Descriptor to an empty string... Unless someone has an idea on how to do this I would go for the two-pass option where we encrypt all the data once just to determine size and digest of the encrypted data and in the 2nd pass write the encrypted data using WriteBlob().

I solved the above mentioned issue and the two-pass encryption wasn't necessary.
@justincormack  I just pushed a series of patches converting the code to use AES SIV Stream Cipher (using it with 1MB encrypted chunks).

@justincormack @dmcgowan @lumjjb : openssl cli tool could do the symmetric encryption / decryption along these lines here if you don't mind dependencies on external tools:
$ openssl version
OpenSSL 1.1.0i-fips  14 Aug 2018
$ echo pwd > pwd ; echo hello | openssl enc -e -aes-256-cbc -pass file:pwd | openssl enc -d -aes-256-cbc -pass file:pwd
hello
# openssl enc -ciphers
-aes-128-cbc               -aes-128-cfb               -aes-128-cfb1
-aes-128-cfb8              -aes-128-ctr               -aes-128-ecb
-aes-128-ofb               -aes-192-cbc               -aes-192-cfb
-aes-192-cfb1              -aes-192-cfb8              -aes-192-ctr
-aes-192-ecb               -aes-192-ofb               -aes-256-cbc
-aes-256-cfb               -aes-256-cfb1              -aes-256-cfb8
-aes-256-ctr               -aes-256-ecb               -aes-256-ofb
-aes128                    -aes128-wrap               -aes192
-aes192-wrap               -aes256                    -aes256-wrap
-bf                        -bf-cbc                    -bf-cfb
-bf-ecb                    -bf-ofb                    -blowfish
-camellia-128-cbc          -camellia-128-cfb          -camellia-128-cfb1
-camellia-128-cfb8         -camellia-128-ctr          -camellia-128-ecb
-camellia-128-ofb          -camellia-192-cbc          -camellia-192-cfb
-camellia-192-cfb1         -camellia-192-cfb8         -camellia-192-ctr
-camellia-192-ecb          -camellia-192-ofb          -camellia-256-cbc
-camellia-256-cfb          -camellia-256-cfb1         -camellia-256-cfb8
-camellia-256-ctr          -camellia-256-ecb          -camellia-256-ofb
-camellia128               -camellia192               -camellia256
-cast                      -cast-cbc                  -cast5-cbc
-cast5-cfb                 -cast5-ecb                 -cast5-ofb
-chacha20                  -des                       -des-cbc
-des-cfb                   -des-cfb1                  -des-cfb8
-des-ecb                   -des-ede                   -des-ede-cbc
-des-ede-cfb               -des-ede-ecb               -des-ede-ofb
-des-ede3                  -des-ede3-cbc              -des-ede3-cfb
-des-ede3-cfb1             -des-ede3-cfb8             -des-ede3-ecb
-des-ede3-ofb              -des-ofb                   -des3
-des3-wrap                 -desx                      -desx-cbc
-id-aes128-wrap            -id-aes128-wrap-pad        -id-aes192-wrap
-id-aes192-wrap-pad        -id-aes256-wrap            -id-aes256-wrap-pad
-id-smime-alg-CMS3DESwrap  -idea                      -idea-cbc
-idea-cfb                  -idea-ecb                  -idea-ofb
-rc2                       -rc2-128                   -rc2-40
-rc2-40-cbc                -rc2-64                    -rc2-64-cbc
-rc2-cbc                   -rc2-cfb                   -rc2-ecb
-rc2-ofb                   -rc4                       -rc4-40
-rc5                       -rc5-cbc                   -rc5-cfb
-rc5-ecb                   -rc5-ofb                   -seed
-seed-cbc                  -seed-cfb                  -seed-ecb
-seed-ofb

@dmcgowan @justincormack I send 2 PRs now. The 1st covers the missing annotations field in the gRPC Descriptor message and the 2nd covers the encryption/decryption code (only). I hope we correctly interpreted your requests.

Any reason to keep this PR open? The code seems out of date compared to the much more recent PRs which are in-flight.

I think the only thing here is the pull path code. But I think at this point, with all the changes, we would have to end up rewriting a chunk of it anyway. I'm in favor of closing this and focusing on the other PRs.

Per @lumjjb closing this PR as we have existing PRs for review and potential merge. All the discussion, code commits, and initial opening info describing the capabilities are still available, but the PR won't be in the open list.


Fixed issue GORA-503

Field index 0 is always considered as clean even if it is dirty.

Any comments @renato2099 ?

none @lewismc , would you mind committing this please?

OK I tried to test locally and I get the following
2017-02-16 10:35:10,490 INFO  [StorageServiceShutdownHook] gms.Gossiper (Gossiper.java:stop(1197)) - Announcing shutdown
2017-02-16 10:35:12,494 INFO  [StorageServiceShutdownHook] net.MessagingService (MessagingService.java:shutdown(686)) - Waiting for messaging service to quiesce
2017-02-16 10:35:12,495 INFO  [ACCEPT-localhost/<IP_ADDRESS>] net.MessagingService (MessagingService.java:run(868)) - MessagingService shutting down server thread.
2017-02-16 10:35:12,496 ERROR [StorageServiceShutdownHook] service.CassandraDaemon (CassandraDaemon.java:uncaughtException(187)) - Exception in thread Thread[StorageServiceShutdownHook,5,main]
java.io.IOError: java.io.IOException: Unknown error: 316
	at org.apache.cassandra.net.MessagingService.shutdown(MessagingService.java:701)
	at org.apache.cassandra.service.StorageService$1.runMayThrow(StorageService.java:546)
	at org.apache.cassandra.utils.WrappedRunnable.run(WrappedRunnable.java:28)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Unknown error: 316
	at sun.nio.ch.NativeThread.signal(Native Method)
	at sun.nio.ch.ServerSocketChannelImpl.implCloseSelectableChannel(ServerSocketChannelImpl.java:292)
	at java.nio.channels.spi.AbstractSelectableChannel.implCloseChannel(AbstractSelectableChannel.java:234)
	at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:115)
	at sun.nio.ch.ServerSocketAdaptor.close(ServerSocketAdaptor.java:137)
	at org.apache.cassandra.net.MessagingService$SocketThread.close(MessagingService.java:885)
	at org.apache.cassandra.net.MessagingService.shutdown(MessagingService.java:697)
	... 3 more

Results :

Failed tests:   org.apache.gora.cassandra.store.TestCassandraStore: attempted to delete non-existing file system-compaction_history-jb-531-CompressionInfo.db

I have seen this as well but as the error points out it is the CassandraDeamon not being able to be stopped.
2017-02-16 10:35:12,496 ERROR [StorageServiceShutdownHook] service.CassandraDaemon (CassandraDaemon.java:uncaughtException(187)) - Exception in thread Thread[StorageServiceShutdownHook,5,main]
java.io.IOError: java.io.IOException: Unknown error: 316
Would you mind trying again? or with the same cassandra version supported by Gora currently?

I cannot avoid the error as above. Can you try running the test suite @renato2099 ?

I tried to test localy, but I can't reproduce the error.
@lewismc Did you get this error since merging this fixes?
Thank you.

PING @renato2099

Any comments folks?

I tried locally too and can't reproduce the error.
I found this about your error: https://issues.apache.org/jira/browse/CASSANDRA-8220
Seems related to OSX and JDK8. https://stackoverflow.com/questions/32508530/cassandra-on-mac-stopping-cassandra-shows-error
I guess it can be committed safely :)


It should be res$.request.url === HELLO_URL

The corresponding issue: #32
The search example might be broken as well for the same reason.

I think I'll remove those examples since the most up-to-date is repo cycle-examples


Dynamic and interactive API

Codecov Report
Patch coverage: 1.40% and project coverage change: -5.28 :warning:

Comparison is base (256e35b) 5.96% compared to head (82cfcf9) 0.68%.

:exclamation: Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more.
Additional details and impacted files
@@           Coverage Diff            @@
##            main     #57      +/-   ##
========================================
- Coverage   5.96%   0.68%   -5.28%
========================================
  Files         15      19       +4
  Lines       1476    1023     -453
========================================
- Hits          88       7      -81
+ Misses      1388    1016     -372

Impacted Files
Coverage Δ

src/KuMo.jl
100.00% <ø> (ø)

src/action.jl
0.00% <0.00%> (ø)

src/execute.jl
0.00% <0.00%> (ø)

src/execution/batch_simulation.jl
0.00% <0.00%> (ø)

src/execution/interactive.jl
0.00% <0.00%> (ø)

src/infrastructure.jl
0.00% <0.00%> (ø)

src/job.jl
0.00% <ø> (-66.67%)
:arrow_down:

src/paths.jl
0.00% <0.00%> (ø)

src/request.jl
0.00% <0.00%> (-20.00%)
:arrow_down:

src/scenarii/basic.jl
0.00% <0.00%> (ø)

... and 5 more

... and 1 file with indirect coverage changes

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.


How to connect with AD credentials

When trying to deploy a function with powershell I was not able to connect using AD.  ( I tried with a sql user and that worked but we don't use sql users )
eg.  $cxnString = "Data Source=$serveraddress,1433;Initial Catalog=$dbname;User ID=$env:DBADUser;Password=$env:DBADPassword;Authentication=Active Directory Password"

This produced the error
Microsoft.Azure.WebJobs.Script.Rpc.RpcException : Result: ERROR: New-Object : Exception calling ".ctor" with "1" argument(s): "Keyword not supported: 'authentication'."

So it appears that this is due to powershell core being used for azure functions and dot net core doesn't support AD.   Is this really the case?  That seems like a big problem.   Do you have any working example of how an azure function can connect to a DB using AD credentials?   Thanks.

Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

ID: e4785005-a130-a392-7b4a-c8c6d36bad37
Version Independent ID: 6beb58e0-167c-c08d-46ff-13f72ddbaf31
Content: Use Azure Functions to perform a database clean up task
Content Source: articles/azure-functions/functions-scenario-database-table-cleanup.md
Service: azure-functions
GitHub Login: @ggailey777
Microsoft Alias: glenga

nice

Hi @rscottwatson - Thank you for your feedback! We will review and update as appropriate.

Hey @rscottwatson - Could you clarify the part about the deployment-related implication in this scenario? If you're looking to get your Function App to establish a connection to an Azure SQL database, I believe you can just use managed identity and add the service principal to your SQL DB as a user. Since this topic is outside the scope of this documentation, as the next step, could you open a thread on MSDN forums and tag me here with the URL of the thread? I can look into this further and supply you with the working code on the MSDN thread.
Since this channel is for driving improvements towards MS Docs, we will now proceed to close this thread for now. If there are further questions regarding this matter, please reopen it and we will gladly continue the discussion.


STORM-2509 Close and perform rotation actions for retired writers in …

…AbstractHDFSBolt.

@ryanpersaud Thanks for contributing. We introduced checkstyle and the build is failing with checkstyle violation. Could you fix the violation from your changeset and update the patch?


CFFL

CFFL

Linter check found the following problems:
The following problems have been found:
ERROR: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:18: pip_install_args: pip install should be run with --no-deps.
ERROR: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:24: cython_must_be_in_host: Cython should be in the host section
ERROR: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:30: host_section_needs_exact_pinnings: Packages in host must have exact version pinnings, except python build tools.
ERROR: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:33: host_section_needs_exact_pinnings: Packages in host must have exact version pinnings, except python build tools.
ERROR: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:33: missing_wheel: For pypi packages, wheel should be present in the host section
WARNING: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:36: python_build_tool_in_run: The python build tool pip is in run depends
WARNING: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:64: missing_description: The recipe is missing a description
ERROR: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:64: missing_documentation: The recipe is missing a doc_url or doc_source_url
ERROR: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:64: missing_license_family: The recipe is missing the about/license_family key.
ERROR: /tmp/abs_6dukst2u24/clone/recipe/meta.yaml:64: missing_dev_url: The recipe is missing a dev_url
Errors were found


clone_wp_site.py: Update database config properly

This is a fix for issue #8. It updates the dst app to use the dst database, as intended.
It does this immediately after copying the files so that if any later steps of the script fail, we don't wind up with an app that appears to work because it's pointing to the src database.

Tested and working.
Thanks for this fix. It's really appreciated.


Real-World Comparison of Front-End Frameworks with Benchmarks 2019

Hey @anishkny
I'm working on a 2019 updae of the Real-World Comparison of Front-End Frameworks with Benchmarks article and was wondering if there are any frameworks that should be added to the home page?
Currently there is 17 so if you have anything else in the backlog would be great to include as much as we can.
Thanks, Jacek

Sweet! I am aware of only one FE framework in review (finished, but not added):

Stencil (Repo, Issue #325)

Query for future reference: https://github.com/gothinkster/realworld/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3A"ready+to+be+added"+label%3Afrontend
Do you want to leave this issue open for tracking or okay to close out?

Great, thanks for letting me know. Will do PR when the articles is published.


[shell][v2] Cannot use multithreading to create sidecar commands in Rust

at [v1] create sidecar like this：

let (mut rx, mut child) = Command::new_sidecar(cmd)

now [v2]

window.shell().sidecar("cmd")

allways compile error :

argument requires that window is borrowed for 'static

Hi, could you show a bit more of your code so we can see the context please?

#[tauri::command]
fn greet(window: Window, name: &str) -> String {
    let shell = window.shell();
    let pool = ThreadPool::new().unwrap();
    let run = async move {
        let (rv, child) = shell.sidecar("ffmpeg").unwrap().args(vec!["x"]).spawn().expect("");
    };
    pool.spawn(run).expect("done");
    format!("Hello, {}! You've been greeted from Rust!", name)
}

error[E0597]: `window` does not live long enough
  --> src/main.rs:24:17
   |
23 | fn greet(window: Window, name: &str) -> String {
   |          ------ binding `window` declared here
24 |     let shell = window.shell();
   |                 ^^^^^^--------
   |                 |
   |                 borrowed value does not live long enough
   |                 argument requires that `window` is borrowed for `'static`
...
34 | }
   | - `window` dropped here while still borrowed

Why declare that external access is not allowed?

pub(crate)

impl Command {
    pub(crate) fn new<S: AsRef<OsStr>>(program: S) -> Self {
        let mut command = StdCommand::new(program);

        command.stdout(Stdio::piped());
        command.stdin(Stdio::piped());
        command.stderr(Stdio::piped());
        #[cfg(windows)]
        command.creation_flags(CREATE_NO_WINDOW);

        Self(command)
    }

    pub(crate) fn new_sidecar<S: AsRef<Path>>(program: S) -> crate::Result<Self> {
        Ok(Self::new(relative_command_path(program.as_ref())?))
    }
}

To fix your issue, simply move the window itself into the task/thread:
#[tauri::command]
fn greet(window: Window, name: &str) -> String {
    let pool = ThreadPool::new().unwrap();
    let run = async move {
        let shell = window.shell();
        let (rv, child) = shell.sidecar("ffmpeg").unwrap().args(vec!["x"]).spawn().expect("");
    };
    pool.spawn(run).expect("done");
    format!("Hello, {}! You've been greeted from Rust!", name)
}

If you need the window in the outer function for something else you can just clone it. We've constructed the Window and AppHandle structs in a way that they should be fairly cheap to clone for use cases like this one.

Why declare that external access is not allowed?

I can't tell for sure but probably to have more freedom in changing them later.


Models

This introduces the basic setup for the models

Clean up some leftovers from create-react-app
Adds styleguidist to package.json
Introduces the Rule model

Nice!


fix: fix comapitbility for windows unix gradlew executable for ci

fixed #45

:tada: This PR is included in version 1.0.0-dev.3 :tada:
The release is available on GitHub release
Your semantic-release bot :package::rocket:


Compatible with what versions of Django?

It would be very useful to see what versions of Django was this package tested against.

This package was written for django 1.6. It was meant to be a small "hack" to make django admin more user friendly (for a small project w/ very limited budget). I haven't had the chance or the budget to go back and add tests or checks to make sure it works w/ some of the latest versions of django.
If you use this package in your project .. please fork this and write some unit tests. Pull requests are welcome!
Many Thanks
Mishbah


python36Packages.nipy: 0.4.0 -> 0.4.2

Semi-automatic update generated by https://github.com/ryantm/nixpkgs-update tools. This update was made based on information from https://repology.org/metapackage/python3.6-nipy/versions.
meta.description for python36Packages.nipy is: '"Software for structural and functional neuroimaging analysis"'.

Checks done (click to expand)

built on NixOS
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/.nipy_3dto4d-wrapped passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_3dto4d passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/.nipy_4d_realign-wrapped passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_4d_realign passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/.nipy_4dto3d-wrapped passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_4dto3d passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/.nipy_diagnose-wrapped passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_diagnose passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/.nipy_tsdiffana-wrapped passed the binary check.
/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_tsdiffana passed the binary check.
10 of 10 passed binary check by having a zero exit code.
0 of 10 passed binary check by having the new version present in output.
found 0.4.2 with grep in /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2
directory tree listing: https://gist.github.com/d435b91f71497a0f69b1056a4f4227e5
du listing: https://gist.github.com/264d96ca429f5450ed74ecd288fee323

Outpath report (click to expand)

Outpath difference report
8 total rebuild paths
2 package rebuilds
2 x86_64-linux rebuilds
2 i686-linux rebuilds
2 x86_64-darwin rebuilds
2 aarch64-linux rebuilds
First ten rebuilds by attrpath
python27Packages.nipy
python36Packages.nipy

Instructions to test this update (click to expand)

Either download from Cachix:
nix-store -r /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2 \
  --option binary-caches 'https://cache.nixos.org/ https://r-ryantm.cachix.org/' \
  --option trusted-public-keys '
  r-ryantm.cachix.org-1:gkUbLkouDAyvBdpBX0JOdIiD2/DP1ldF3Z3Y6Gqcc4c=
  cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
  '

(r-ryantm's Cachix cache is only trusted for this store-path realization.)
Or, build yourself:
nix-build -A python36Packages.nipy https://github.com/r-ryantm/nixpkgs/archive/4c687d3e1b18f693ef3ec3fe5fdabc8da537aee5.tar.gz

After you've downloaded or built it, look at the files and if there are any, run the binaries:
ls -la /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2
ls -la /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin

Success on aarch64-linux (full log)
Attempted: python36Packages.nipy
Partial log (click to expand)
strip is /nix/store/p9akxn2sfy4wkhqdqa3li97pc6jaz3r1-binutils-2.30/bin/strip
stripping (with command strip and flags -S) in /nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2/lib  /nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2/bin
patching script interpreter paths in /nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2
checking for references to /build in /nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2...
wrapping `/nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2/bin/nipy_3dto4d'...
wrapping `/nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2/bin/nipy_4d_realign'...
wrapping `/nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2/bin/nipy_4dto3d'...
wrapping `/nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2/bin/nipy_diagnose'...
wrapping `/nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2/bin/nipy_tsdiffana'...
/nix/store/yhkhzgr9qpjc99r3h3qx80d3yicwnq9n-python3.6-nipy-0.4.2

Success on x86_64-linux (full log)
Attempted: python36Packages.nipy
Partial log (click to expand)
shrinking /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/lib/python3.6/site-packages/nipy/algorithms/registration/_registration.cpython-36m-x86_64-linux-gnu.so
strip is /nix/store/vcc4svb8gy29g4pam2zja6llkbcwsyiq-binutils-2.30/bin/strip
stripping (with command strip and flags -S) in /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/lib  /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin
patching script interpreter paths in /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2
checking for references to /build in /nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2...
wrapping `/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_diagnose'...
wrapping `/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_4d_realign'...
wrapping `/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_tsdiffana'...
wrapping `/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_4dto3d'...
wrapping `/nix/store/j7s43868zizh53sp2xh3kvjmbj5zswi5-python3.6-nipy-0.4.2/bin/nipy_3dto4d'...

Timed out, unknown build status on x86_64-darwin (full log)
Attempted: python36Packages.nipy
Partial log (click to expand)
../numpy/lib/tests/test_format.py::test_large_header <- ../../../../nix/store/wd6nr3a4am08clb3jizca9451dhbkr09-python3.6-numpy-1.15.2/lib/python3.6/site-packages/numpy/lib/tests/test_format.py PASSED [ 52%]
../numpy/lib/tests/test_format.py::test_read_array_header_1_0 <- ../../../../nix/store/wd6nr3a4am08clb3jizca9451dhbkr09-python3.6-numpy-1.15.2/lib/python3.6/site-packages/numpy/lib/tests/test_format.py PASSED [ 52%]
../numpy/lib/tests/test_format.py::test_read_array_header_2_0 <- ../../../../nix/store/wd6nr3a4am08clb3jizca9451dhbkr09-python3.6-numpy-1.15.2/lib/python3.6/site-packages/numpy/lib/tests/test_format.py PASSED [ 52%]
../numpy/lib/tests/test_format.py::test_bad_header <- ../../../../nix/store/wd6nr3a4am08clb3jizca9451dhbkr09-python3.6-numpy-1.15.2/lib/python3.6/site-packages/numpy/lib/tests/test_format.py PASSED [ 52%]
building of '/nix/store/a325qb4rsb6w30pp2l9qzxdasry14zi9-python3.6-numpy-1.15.2.drv' timed out after 1800 seconds
cannot build derivation '/nix/store/mbfywgl1sh5532k7i1g0l59zfsnsjffy-python3.6-matplotlib-2.2.3.drv': 3 dependencies couldn't be built
cannot build derivation '/nix/store/frq1cpwrrzw6szj10y1xyh01v7la4jbn-python3.6-nibabel-2.3.0.drv': 1 dependencies couldn't be built
cannot build derivation '/nix/store/5dm1i908szra6mr8bwwbhrv81isbcjx5-python3.6-scipy-1.1.0.drv': 1 dependencies couldn't be built
cannot build derivation '/nix/store/1x99ajkqs8wrsppzg24xn7ys8pqranar-python3.6-nipy-0.4.2.drv': 4 dependencies couldn't be built
error: build of '/nix/store/1x99ajkqs8wrsppzg24xn7ys8pqranar-python3.6-nipy-0.4.2.drv' failed

Timed out, unknown build status on x86_64-darwin (full log)
Attempted: python27Packages.nipy
Partial log (click to expand)
Requirement already satisfied: setuptools in /nix/store/m5pdr0h09xljb83z1hwh3bbj355pn6zb-python2.7-bootstrapped-pip-18.1/lib/python2.7/site-packages (from kiwisolver>=1.0.1->matplotlib==2.2.3) (40.4.3)
Installing collected packages: matplotlib
Successfully installed matplotlib-2.2.3
/private/tmp/nix-build-python2.7-matplotlib-2.2.3.drv-0/matplotlib-2.2.3
post-installation fixup
strip is /nix/store/g5r4apl0za012ffs6ladinwa5w0m1l3k-cctools-binutils-darwin/bin/strip
stripping (with command strip and flags -S) in /nix/store/ff968gxyfiybrylwb22kfs2sq70wdz0f-python2.7-matplotlib-2.2.3/lib
patching script interpreter paths in /nix/store/ff968gxyfiybrylwb22kfs2sq70wdz0f-python2.7-matplotlib-2.2.3
cannot build derivation '/nix/store/884vn9l2w38sdrl7z0srra6yhq7zwpfx-python2.7-nipy-0.4.2.drv': 1 dependencies couldn't be built
error: build of '/nix/store/884vn9l2w38sdrl7z0srra6yhq7zwpfx-python2.7-nipy-0.4.2.drv' failed


chore: moving dependencies to dev dependencies

All types, and especially typescript, should be in devDependencies.
It will prevent package bundlers like aws serverless to pick up a 64MB typescript package into the bundle.
When you think of using this library in node aws lambda for example, which has a 50MB (zipped) and (250MB unzipped) limit to upload package, this makes a big difference :)

Will be releasse this week.


update doc for mailjet support

Thanks for opening a Pull Request!
Here's a handy checklist to ensure your PR goes smoothly.

[ ] I signed Google's Contributor License Agreement
[ ] My code conforms to Google's python style.
[ ] My PR at a minimum doesn't decrease unit-test coverage (if applicable).
[ ] My PR has been functionally tested.
[ ] All of the tests pass
[ ] I have submitted a corresponding PR in the Forseti Terraform module (if applicable).

These guidelines and more can be found in our contributing guidelines.

@jf-marquis-Adeo We would like to get this PR out in v2.25.0. The code freeze for it is tomorrow. Is it possible for you to address two small comments? Alternatively, I can address it for you. Let me know!

hello
i can try but please send me the link i don't find it
Le jeu. 21 nov. 2019 à 20:51, red2k18<EMAIL_ADDRESS>a écrit :

@jf-marquis-Adeo https://github.com/jf-marquis-Adeo We would like to
get this PR out in v2.25.0. The code freeze for it is tomorrow. Is it
possible for you to address two small comments? Alternatively, I can
address it for you. Let me know!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/forseti-security/forseti-security/pull/3379?email_source=notifications&email_token=AJV7DTTZ7J5Y5JJM36YEGW3QU3REJA5CNFSM4JFQH66KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEE3OG2Y#issuecomment-557245291,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AJV7DTUALDMVTMGYDEYKMADQU3REJANCNFSM4JFQH66A
.

--
Cordialement,
Jean-François Marquis
Data Factory
Cloud Architect - SRE
135 Rue Sadi Carnot
59790 RONCHIN
E-mail<EMAIL_ADDRESS>

Hi @jf-marquis-Adeo, here is the link that you want to update. Please let me know if you have any questions. Alternatively, you can reply to the comments in this PR and I will update the documentation accordingly.

hello
update and commit done
documentation is up to date for deploying mailjet connector
have a nice day
best regards
Le ven. 22 nov. 2019 à 15:16, red2k18<EMAIL_ADDRESS>a écrit :

Hi @jf-marquis-Adeo https://github.com/jf-marquis-Adeo, here
https://github.com/jf-marquis-Adeo/forseti-security/blob/patch-2/_docs/_latest/configure/notifier/index.md
is the link that you want to update. Please let me know if you have any
questions. Alternatively, you can reply to the comments in this PR and I
will update the documentation accordingly.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/forseti-security/forseti-security/pull/3379?email_source=notifications&email_token=AJV7DTU4WLOGB7OGGCJIZJTQU7SUHA5CNFSM4JFQH66KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEE5YDUQ#issuecomment-557547986,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AJV7DTTU34MV2DIIFLHWIUDQU7SUHANCNFSM4JFQH66A
.

--
Cordialement,
Jean-François Marquis
Data Factory
Cloud Architect - SRE
135 Rue Sadi Carnot
59790 RONCHIN
E-mail<EMAIL_ADDRESS>

Thank you @jf-marquis-Adeo for your contributions!

Hi @jf-marquis-Adeo, can you let us know if both api_key and mailjet_api_key is needed to authorize requests to Mailjet? If so, how are they different? Besides that do we need api_secret and campaign to authorize requests to Mailjet?

i've correct in the doc
sorry for that
Le ven. 22 nov. 2019 à 16:46, red2k18<EMAIL_ADDRESS>a écrit :

Hi @jf-marquis-Adeo https://github.com/jf-marquis-Adeo, can you let us
know if both api_key and mailjet_api_key is needed to authorize requests
to Mailjet? If so, how are they different? Besides that do we need
api_secret and campaign to authorize requests to Mailjet?
[image: image]
https://user-images.githubusercontent.com/41486977/69439550-356c8480-0d15-11ea-8897-3b589b03c5c5.png
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/forseti-security/forseti-security/pull/3379?email_source=notifications&email_token=AJV7DTXMI7HR7N3SBV36QHLQU75FVA5CNFSM4JFQH66KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEE6A3UQ#issuecomment-557583826,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AJV7DTUCMYCQTXBFNJZBTW3QU75FVANCNFSM4JFQH66A
.

--
Cordialement,
Jean-François Marquis
Data Factory
Cloud Architect - SRE
135 Rue Sadi Carnot
59790 RONCHIN
E-mail<EMAIL_ADDRESS>

Cool, thanks for the correction @jf-marquis-Adeo.


How to deploy a 3 replicas on three differents nodes

Hi,
I am trying to deploy a cluster of 3 replicas of Clickhouse on my Kubernetes Cluster.
My Kubernetes Cluster is composed by 3 nodes (dedicated server) and I would like to deploy a replica in each node to be safe for potential loss of data.
apiVersion: v1
kind: PersistentVolume
metadata:
  name: logs-clickhouse-worker-01-volume
  labels:
    type: local
spec:
  storageClassName: logs-clickhouse-worker
  capacity:
    storage: 2000Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/var/data/k8s-logs-clickhouse-01"
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-eu-de-worker-01
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: logs-clickhouse-worker-02-volume
  labels:
    type: local
spec:
  storageClassName: logs-clickhouse-worker
  capacity:
    storage: 2000Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/var/data/k8s-logs-clickhouse-02"
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-eu-de-worker-02
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: logs-clickhouse-worker-03-volume
  labels:
    type: local
spec:
  storageClassName: logs-clickhouse-worker
  capacity:
    storage: 2000Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/var/data/k8s-logs-clickhouse-03"
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-eu-de-worker-03
---
apiVersion: "clickhouse.altinity.com/v1"
kind: "ClickHouseInstallation"
metadata:
  name: "clickhouse"
spec:
  defaults:
    templates:
      podTemplate: clickhouse-stable
      volumeClaimTemplate: storage-vc-template
      hostTemplate: host-template-custom-ports
  templates:
    hostTemplates:
      - name: host-template-custom-ports
        spec:
          tcpPort: 7000
          httpPort: 7001
          interserverHTTPPort: 7002
    podTemplates:
      - name: clickhouse-stable
        spec:
          containers:
            - name: clickhouse
              image: yandex/clickhouse-server:<IP_ADDRESS>
              volumeMounts:
                - name: storage-vc-template
              resources:
                requests:
                  memory: 16Gi
                  cpu: "8"
                limits:
                  memory: 32Gi
                  cpu: "12"
    volumeClaimTemplates:
      - name: storage-vc-template
        spec:
          storageClassName: "logs-clickhouse-worker"
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 2000Gi
  configuration:
    users:
      test/default: secret
      test/password: test
      test/profile: default
      test/quota: default
      test/networks/ip: "::/0"
    zookeeper:
      nodes:
      - host: logs-zookeeper-client
        port: 2181
    clusters:
      - name: "clickhouse"
        layout:
          shardsCount: 1
          replicasCount: 3

I am trying with this configuration file but :

I can not specify for each replica to use a specific PersistentVolume (Because I want Clickhouse database using a local persistent volume on the same node)
When I apply my config file, a can see a clickhouse operator, my all three volumes get bound but I can not see pods for each broker of database ?

Maybe I am not understanding how it should work, if someone already implemented something similar and can help :)
Thanks

Use node_selector and anti-affinity https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/

@jdalia001 , see podDistribution settings: https://github.com/Altinity/clickhouse-operator/blob/047b5bfcbd16bfe533b32721c52fae930122c68c/docs/chi-examples/99-clickhouseinstallation-max.yaml#L431
For your scenario the following should work:
      podDistribution:
      - scope: ClickHouseInstallation
        type: ClickHouseAntiAffinity


AssertionError for mask

I get the AssertionError,I have no idea to deal with it ? Can anyone help me? THX!!!!

Does it work if you set the maximum number in your parameters to 0.9?


2 problems reported

Recently I  found 2 problems:
1、Extra Sapce typing between #else and " if " can cause error;
2、calculation contain antitrigonometric functions could easily go wrong if you don't notice that the value of an antitrigonometric might be very big. Still I think it's not advisible to use #rad and #deg to controll the vaule, instead they should only be used to control display. My suggestion is, deg should be used as a unit, while the default is 1,i.e. radian.

Hi @idealkindom!
Thank you for your feedback! I will take a closer look. About #end if, this is the way it is defined in the Calcpad language. These are not two separate literals. The whole #end if with just one space is a keyword. And as for any other keywords, if you do not write it exactly as specified, it is not recognized by the parser.

Yes I guess so,it's the same for space between both #else and if, #end and if.
Pls consider the antitrigonometric calculation problem.Thks a lot.

Hi, about inverse trigonometric functions, can you please provide more specific details of example:
What is your input, what do you get, and what is expected? Do you get different results if you try the same functions anywhere else, say in Excel or Mathcad?
Is it possible to be simply the floating point math limitations? Just like other similar software, Calcpad internally warps the native processor math functions. They are very fast, but their range and precision are limited by the parameters of the machine environment. On the other hand, processors internally use some kind of CORDIC or BKM algorithms:
https://en.wikipedia.org/wiki/CORDIC
https://www.computer.org/csdl/proceedings-article/afips/1959/50540257/12OmNqN6QXX
https://en.wikipedia.org/wiki/BKM_algorithm
https://www.researchgate.net/publication/220329442_BKM_A_New_Hardware_Algorithm_for_Complex_Elementary_Functions
The other option is to use arbitrary precision arithmetic, like this one:
https://en.wikipedia.org/wiki/GNU_Multiple_Precision_Arithmetic_Library
However, since it is software-emulated, it is hundreds of times slower than using the processor functions. But for most real-world engineering problems, the float64 range and precision should be more than enough.

Hi, about inverse trigonometric functions, can you please provide more specific details of example:
What is your input, what do you get, and what is expected? Do you get different results if you try the same functions anywhere else, say in Excel or Mathcad?
Is it possible to be simply the floating point math limitations? Just like other similar software, Calcpad internally warps the native processor math functions. They are very fast, but their range and precision are limited by the parameters of the machine environment. On the other hand, processors internally use some kind of CORDIC or BKM algorithms:
https://en.wikipedia.org/wiki/CORDIC https://www.computer.org/csdl/proceedings-article/afips/1959/50540257/12OmNqN6QXX https://en.wikipedia.org/wiki/BKM_algorithm https://www.researchgate.net/publication/220329442_BKM_A_New_Hardware_Algorithm_for_Complex_Elementary_Functions
The other option is to use arbitrary precision arithmetic, like this one: https://en.wikipedia.org/wiki/GNU_Multiple_Precision_Arithmetic_Library
However, since it is software-emulated, it is hundreds of times slower than using the processor functions. For most real-world engineering problems, the float64 range and precision should be more than enough.

OK.let's see an example. L=Rasin(d/R),when d/R=1/2, you get a result asL=20R, instead of L=2*R. radian is a self consistant unit while degree not, since one circle is 360 degree. I used Maxima a lot for many years, it use radian as default unit for both trigonometric and anti-trigonometric. That's why I suggest the trigonometric and anti-trigonometric function should be in radian as default, while degree must be used as a unit. How do you think??

Hi! I cannot get anything like this on my computer. Here is my code:
d = 1
R = 2*d
#deg
'Degrees
L = R*asin(d/R)
L/R
asin(1/2)'°

'Convert to radians
L = R*asin(d/R)*deg/rad
'or
L = R*asin(d/R)*π/180

#rad
'Radians
L = R*asin(d/R)
L/R
asin(1/2)

What I get is that asin(1/2) = 30° in degrees. How do you get 20?

It is expected that, if you have set to work in degrees, you will get the result in degrees, so you will have to use the respective formula (L = R*asin(d/R)*π/180) or simply change the target units. I think that the most appropriate default units are different for the different engineering fields. So, it is best for everyone to set them by himself. For me as a structural engineer, it is best to use degrees in 95% of cases.
In the latest version, if you change the default units from the UI and close the program, they will be saved and will be loaded each time you start the program after that. However, my recommendation is not to rely on the default settings, but to specify explicitly the units at the beginning of each worksheet (#deg, #rad, or #gra). In this way, if you open it in the future with different settings, you will not get different results.

Sorry, that's my typing error. I also got 30.
At 2023-09-11 19:52:38, "Ned Ganchovski" @.***> wrote:
Hi! I cannot get anything like this on my computer. Here is my code:
d = 1
R = 2d
#deg
'Degrees
L = Rasin(d/R)
L/R
asin(1/2)'°
'Convert to radians
L = R*asin(d/R)deg/rad
'or
L = Rasin(d/R)*π/180
#rad
'Radians
L = R*asin(d/R)
L/R
asin(1/2)
What I get is that asin(1/2) = 30° in degrees. How do you get 20?
It is expected that, if you have set to work in degrees, you will get the result in degrees, so you will have to use the respective formula (L = R*asin(d/R)*π/180) or simply change the target units. I think that the most appropriate default units are different for the different engineering fields. So, it is best for everyone to set them by himself. For me as a structural engineer, it is best to use degrees in 95% of cases.
In the latest version, if you change the default units from the UI and close the program, they will be saved and will be loaded each time you start the program after that. However, my recommendation is not to rely on the default settings, but to specify explicitly the units at the beginning of each worksheet (#deg, #rad, or #gra). In this way, if you open it in the future with different settings, you will not get different results.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.Message ID: @.***>

OK, I see. So, we can now both agree that the result is correct for the selected units. You can set whatever defaults you prefer, but in all cases, you have to keep in mind what units you are using.
It is also strange to multiply angles by lengths directly. You'd rather obtain the angle from asin, but after that use it with cos or tan to calculate another length, instead of the angle itself, say:
α = asin(a/c)
b = c*cos(α)

I think there is nothing to be fixed.


Pass current props to NavLink.props.isActive()

Description of Problem
In my app I have some navigation UI that looks like this:

There are routes something like this:
# Examples of routes
/locations
/location/{id}
/organizations
/organization/{id}
/users
/users/{id}
etc...

I would like for these globally visible links to appear active both at the route that literally matches their given link, but also at the route representing the singular entity of the corresponding kind.
For example, "Location" is active both at /locations and at /location/{id}
Desired API
This would be trivially implemented by giving the isActive function access to the other NavLink props. Then, isActive could test against the to prop, or anything else aribitrarily.
For example:
<NavLink to="/location" isActive={(match,location,{to}) => location.pathname.includes(to))}

It's impossible to generalize this idea without access to the props, since the necessary information with which to generalize is NavLink.props.to.
Example of Problem
For example, in order to implement this with the existing API, you must manually specify the regex in every NavLink:
// notice that this isActive function can't be written just once for all of these cases
<NavLink to="/locations" isActive={(match,location) => /location/.test(location.pathname)}
<NavLink to="/organizations" isActive={(match,location) => /organization/.test(location.pathname)}
<NavLink to="/users" isActive={(match,location) => /user/.test(location.pathname)}

Actionable Idea
It looks like an easy PR that would not break any existing behavior. What are your thoughts?
In the NavLink component you can see that isActive receives match, and location.
      const isActive = !!(getIsActive ? getIsActive(match, location) : match) <----

      return (
        <Link
          to={to}
          className={isActive ? [ className, activeClassName ].filter(i => i).join(' ') : className}
          style={isActive ? { ...style, ...activeStyle } : style}
          aria-current={isActive && ariaCurrent}
          {...rest}
        />

I'm imagining the change looks like this:
      const isActive = !!(getIsActive ? getIsActive(match, location, props) : match)

@timdorr I see you closed this issue almost instantly. Is that because this has come up before?

+1 for this feature, seems very light touch and potentially useful.  A simpler implementation that is even more of a light touch is to just pass the to prop as a third parameter to isActive.

You had no content in this issue when you opened it.

This will probably get pushback because you can do this yourself already:
export const LooseNavLink = props => (
  <NavLink {...props} isActive={(match, location) => location.pathname.startsWith(props.to.pathname)}/>
)

In general, the consensus is to avoid adding things to the APIs if they are already possible without too much difficulty. We're trying not to grow the API surface, because it's very hard to roll back changes and it makes future improvements harder to do.
I know this is just one arg on one prop on one component, but it's meant as a philosophy for development so that we don't suffer a death by a thousand cuts. Same thing happened with v1-v3, so we want to avoid that.

For reference, this PR was about essentially the same feature and was it ultimately decided against implementing it #4499

@pshrmn one difference is that I only want to write isActive once for all of my navigation. That's only possible if I can access what's different among NavLinks from inside isActive (props). the solution described it #4499 still forces you to repeat input individually for each case:
const isActive = to => (match,location) => location.pathname.includes(to))
<NavLink to="/users" isActive={isActive('user')} // still have to repeat the 'user' info every time
<NavLink to="/location" isActive={isActive('location')}

@timdorr's higher order solution is the only way to accomplish my aim, and it's reasonable. There happens to be a high impedance to writing HoC's in my codebase, but I appreciate that's not true for everyone.
@timdorr if isActive takes all props, it's a broad stroke that allows many arbitrary possibilities for determining active state. that feels high value and low impact, but maybe I'm underestimating the experience you had with earlier iterations of react-router.

@joefraley if your navigation urls will be /users, /users/:id, NavLink will have active state on both of them. Maybe it's easier to change urls?

I think this is a pretty succinct answer: https://github.com/ReactTraining/react-router/pull/4499#issuecomment-284970451 So, I think we're definitely not going to implement this ourselves.

@timdorr well it's your project, but the answer linked there has the cost described above. any such function must be refined individually for each NavLink by hand, because the isActive function does not receive the information it needs to be written once generally.
@VladShcherbin that is the desired behavior in this case. /user(s)/anything/no/matter/what should appear active.

@timdorr : This is why I would prefer of you implemented this change:
https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md

A bind call or arrow function in a JSX prop will create a brand new function on every single render. This is bad for performance, as it will result in the garbage collector being invoked way more than is necessary. It may also cause unnecessary re-renders if a brand new function is passed as a prop to a component that uses reference equality check on the prop to determine if it should update.


Unknown file type

I can't upload sh files with the tool, but I can do with the Google web application.
How I can setup it? Thanks in advance!
root@homeserver:~# ./gdrive files upload /my/ --recursive
Error: Failed to create file tree: Unknown file type: /my/a/b in c/test.sh

Me too. please advise. Were you able to figure this out?


New algos

Solved issue #182
In Language- python

[ ] Updated README Link
[ yes] Added code with comments

Thanks for contributing!

It would be great if you could add a link to a question at the top of the code.

Sure I'll be adding links to all the questions in respective folders.

It would be great if you could add a link to a question at the top of the code.

I have added txt file and mentioned the links.
Please check

Can you please add the question link as comments at the top of the solutions itself for easy navigation?

Can you please add the question link as comments at the top of the solutions itself for easy navigation?

All done mate.


Periodic "ping" from long tasks in non-interactive mode

See original issue on GitLab
In GitLab by [Gitlab user @nanonyme] on Nov 11, 2018, 20:05
Currently if you're building something very slow like LLVM it gets hard to follow CI since it can't show the nice progress parts of Buildstream in non-interactive mode. It would be nice to have a periodic ping (eg Running build-commands) periodically over a long build, eg every 10 minutes, to be argued upon, so you know something is still happening (and which element it is that's building).
Since this behaviour is just noise in interactive mode, the ping should only ever be printed in non-interactive mode.

In GitLab by [Gitlab user @nanonyme] on Nov 11, 2018, 20:06
changed the description

In GitLab by [Gitlab user @toscalix] on Nov 12, 2018, 10:49
Raised by  [Gitlab user @jjardon] we went over this proposal during the open Monday meeting. No viable solution to implement this was found although it was agreed that leaving the user for some time wondering about progress is far from ideal.

In GitLab by [Gitlab user @nanonyme] on Nov 12, 2018, 11:04
Well, some time might well be hours with some elements

In GitLab by [Gitlab user @nanonyme] on Oct 7, 2020, 19:33
If no one has ideas on how to implement this, closing.


Don't call keys on strings in deep_intersect

This simple fix for #204 works for me.

Coverage remained the same at 74.59% when pulling de0558a35f10812f0b90b6cb8f2f265d51e8ce95 on alexjfisher:issue_204 into a5b3eda7f6ac3fd0d527786c0290c174ac2d81ab on biemond:master.

Coverage remained the same at 74.59% when pulling de0558a35f10812f0b90b6cb8f2f265d51e8ce95 on alexjfisher:issue_204 into a5b3eda7f6ac3fd0d527786c0290c174ac2d81ab on biemond:master.


Gen7 Random Battle: Kangaskhan with power-up punch

Kangaskhan cannot learn power-up punch in Gen7. However Kangaskhan with power-up punch still shows up in Gen7 random battle.

PuP Kangaskhan is just unreleased due to the current absence of Pokebank.
We are fine with unreleased and even some illegal movesets (due to incompatibilities) in Random Battle.


Remove mutex from OSTComposer::revokeStakeRequest and OSTComposer::rejectStakeRequest

We can safely remove mutex from OSTComposer::revokeStakeRequest and OSTComposer::rejectStakeRequest as the following statements in function OSTComposer::removeStakeRequest (called within above-mentioned functions) protects against reentrancy:
        delete stakeRequestHashes[staker][address(stakeRequest.gateway)];
        delete stakeRequests[_stakeRequestHash];

This will be handled in this ticket.https://github.com/openst/mosaic-contracts/issues/763


⚠ CIOOS Pacific ERDDAP dataset DFO_MEDS_BUOYS has degraded performance

In 0a0165d, CIOOS Pacific ERDDAP dataset DFO_MEDS_BUOYS (https://data.cioospacific.ca/erddap/tabledap/DFO_MEDS_BUOYS.htmlTable?STN_ID&time>=now-1week&distinct()) experienced degraded performance:

HTTP code: 503
Response time: 23097 ms

Resolved: CIOOS Pacific ERDDAP dataset DFO_MEDS_BUOYS performance has improved in 08987f0 after 5 minutes.


Fix 3 data races in TestPageCache

This commit fixes 3 data races in TestPageCache:

Pass "i" from the outer for-loop as a parameter to the goroutine
Move l1.Lock to protect reading of o1
Move l2.Lock to protect reading of o2

Thank you Cameron!  It appears that Bjørn was working on the same issue and committed a similar fix in 0c01fef.  But thank you for your contributions as always!  :-)

Ah, sorry -- I didn't see this.


testing: use laxar-mocks instead of laxar-testing

For the spec tests of the widget use laxar-mocks instead of laxar-testing.

Implemented on master (v2.3.0).


Any plans to generate plain javascript files?

I'm trying to migrate a big project from prisma 1 to prisma 2.
Pal.js could help a lot, but I can't migrate the project to typescript right now. Its to much change

Yes current I working on JavaScript with two syntax es modules and normal Javascript

Awesome! Thanks @AhmedElywa

Now we have support for JavaScript from v 1.3.2. You can use it by adding javascript property to the backend object in the pal.js file.
module.exports = {
  backend: {
    generator: 'nexus-schema',
    onDelete: true,
    javaScript: true, // like this
  },
}