text:
title:  "PostgreSQL JDBC Driver 42.0.0 Released"
date:   2017-02-20 00:00:00 +0300
categories:
  - new_release
version: 42.0.0
---
**Notable changes**

### Added
- Replication protocol API was added: [replication API documentation](https://jdbc.postgresql.org//documentation/head/replication.html). [PR 550](https://github.com/pgjdbc/pgjdbc/pull/550)
- java.util.logging is now used for logging: [logging documentation](https://jdbc.postgresql.org//documentation/head/logging.html). [PR 722](https://github.com/pgjdbc/pgjdbc/pull/722)
- Add support for PreparedStatement.setCharacterStream(int, Reader). [ee4c4265](https://github.com/pgjdbc/pgjdbc/commit/ee4c4265aebc1c73a1d1fabac5ba259d1fbfd1e4)

### Changed
- Version bumped to 42.0.0 to avoid version clash with PostgreSQL version and follow a better sematic versioning. [46634923](https://github.com/pgjdbc/pgjdbc/commit/466349236622c6b03bb9cd8d7f517c3ce0586751)
- Ensure executeBatch() can be used with pgbouncer. Previously pgjdbc could use server-prepared statements for batch execution even with prepareThreshold=0. [Issue 742](https://github.com/pgjdbc/pgjdbc/issues/742)
- Error position is displayed when SQL has unterminated literals, comments, etc. [Issue 688](https://github.com/pgjdbc/pgjdbc/issues/688)
- Strict handling of accepted values in getBoolean and setObject(BOOLEAN), now it follows PostgreSQL accepted values, only 1 and 0 for numeric types are acepted (previusly !=0 was true). [PR 732](https://github.com/pgjdbc/pgjdbc/pull/732)
- Return correct versions and name of the driver. [PR 668](https://github.com/pgjdbc/pgjdbc/pull/668)

### Removed
- Support for PostgreSQL versions below 8.2 was dropped. [PR 661](https://github.com/pgjdbc/pgjdbc/pull/661)

### Deprecated
- Deprecated PGPoolingDataSource, instead of this class you should use a fully featured connection pool like HikariCP, vibur-dbcp, commons-dbcp, c3p0, etc. [PR 739](https://github.com/pgjdbc/pgjdbc/pull/739)

### Regresions
- Data truncated in setCharacterStream. Fixed in 42.1.0
- No suitable driver found for jdbc:postgresql when using a DataSource implementation. Fixed in 42.1.0

You may have noticed the change in the versioning of the driver, you can [read the FAQ](documentation/faq.html#versioning) for more information.

**Commits by author**

AlexElin (6):

* refactor: use varargs [PR#681](https://github.com/pgjdbc/pgjdbc/pull/681) [50b7fe0f](https://github.com/pgjdbc/pgjdbc/commit/50b7fe0fe901ee24160615bebd4b86603b960b86)
* refactor: make HostChooser implement Iterable [PR#645](https://github.com/pgjdbc/pgjdbc/pull/645) [3d37db78](https://github.com/pgjdbc/pgjdbc/commit/3d37db78ae4a82cab8809bac86e3b55537e91d95)
* refactor: migrate to Junit4 [PR#682](https://github.com/pgjdbc/pgjdbc/pull/682) [f4a067cc](https://github.com/pgjdbc/pgjdbc/commit/f4a067cc5d32dde2698a062d6f9855d50fa3127d)
* refactor: remove deprecated Utils' methods [PR#678](https://github.com/pgjdbc/pgjdbc/pull/678) [0275d40f](https://github.com/pgjdbc/pgjdbc/commit/0275d40f6f2b8b30faef8aed31761e1ff7a0ed90)
* refactor: migrate tests to junit4 [PR#685](https://github.com/pgjdbc/pgjdbc/pull/685) [faab4998](https://github.com/pgjdbc/pgjdbc/commit/faab499853c56f67cb70fb242f75b918452f2a6f)
* refactor: remove checks for jdk version 1.4 (tests) [PR#737](https://github.com/pgjdbc/pgjdbc/pull/737) [ee51dfce](https://github.com/pgjdbc/pgjdbc/commit/ee51dfce99dcd922b66373cd0c69c83bf339dbdb)

Eric McCormack (1):

* fix: accept server version with more than 3 parts [PR#741](https://github.com/pgjdbc/pgjdbc/pull/741) [8437f6c1](https://github.com/pgjdbc/pgjdbc/commit/8437f6c1c76c3560331d75c4332ab50959d57228)

Jordan Lewis (1):

* feat: do not use pg_depend against PostgreSQL 9.0+ [PR#689](https://github.com/pgjdbc/pgjdbc/pull/689) [62e25fba](https://github.com/pgjdbc/pgjdbc/commit/62e25fba70002d7639472c5a1dcd9d1de5b7f872)

Jorge Solorzano (22):

* refactor: remove support for postgresql < 8.2 [PR#661](https://github.com/pgjdbc/pgjdbc/pull/661) [14e64be7](https://github.com/pgjdbc/pgjdbc/commit/14e64be7dc43feaef210e82fcd9b6089e54e5e4f)
* fix: add query to support postgresql 8.2 without t.typarray [PR#699](https://github.com/pgjdbc/pgjdbc/pull/699) [cb3995b5](https://github.com/pgjdbc/pgjdbc/commit/cb3995b5a0311a2f5f7737fdfe83457680305efb)
* test: add CI tests against PostgreSQL 8.3 [PR#710](https://github.com/pgjdbc/pgjdbc/pull/710) [436365b0](https://github.com/pgjdbc/pgjdbc/commit/436365b095d0a276b876433fd00d9bbac72708cc)
* fix: robust castToBoolean for setObject in PreparedStatement [PR#714](https://github.com/pgjdbc/pgjdbc/pull/714) [edc2a14a](https://github.com/pgjdbc/pgjdbc/commit/edc2a14af8837911a48bb322363bb8b70d2b173d)
* refactor: remove unused V2ReplicationProtocol.java [PR#718](https://github.com/pgjdbc/pgjdbc/pull/718) [7881e41e](https://github.com/pgjdbc/pgjdbc/commit/7881e41ea93f3bd6a3f58e85cb4329f77b667db0)
* test: ignore tests that don't apply to Pg8.2 and Pg8.3 [PR#703](https://github.com/pgjdbc/pgjdbc/pull/703) [3bc0951e](https://github.com/pgjdbc/pgjdbc/commit/3bc0951e84ad3b7af746e2dece04b4ea816605e2)
* style: reorder checkstyle in travis [PR#721](https://github.com/pgjdbc/pgjdbc/pull/721) [ba812fb4](https://github.com/pgjdbc/pgjdbc/commit/ba812fb404852b73a304f7ccb068a2030b8604b3)
* refactor: remove charset property not used [PR#709](https://github.com/pgjdbc/pgjdbc/pull/709) [f6fd5a5a](https://github.com/pgjdbc/pgjdbc/commit/f6fd5a5abea1efe9b987825b7874ffd962772197)
* fix: huntbugs on PgDatabaseMetaData, String concatenation in a loop [PR#693](https://github.com/pgjdbc/pgjdbc/pull/693) [3a00ef94](https://github.com/pgjdbc/pgjdbc/commit/3a00ef9436c6b04c472330802a8adb22adaa9b63)
* refactor: fix getDriverVersion, getDriverName and getJDBCMajor/MinorVersion methods [PR#668](https://github.com/pgjdbc/pgjdbc/pull/668) [aa974341](https://github.com/pgjdbc/pgjdbc/commit/aa9743417f18a10a1a7c7a4bc25ad2862155ddc8)
* docs: reword supported versions, include datasources section, compare versions [PR#673](https://github.com/pgjdbc/pgjdbc/pull/673) [b2cdd057](https://github.com/pgjdbc/pgjdbc/commit/b2cdd057664a00928d97290ed7435fcf57f3a360)
* test: fix test replication on PG_HEAD [PR#734](https://github.com/pgjdbc/pgjdbc/pull/734) [3b406a18](https://github.com/pgjdbc/pgjdbc/commit/3b406a18a7c1751aa477e8378ee8cad0a60efd1e)
* refactor: deprecated PGPoolingDataSource [PR#739](https://github.com/pgjdbc/pgjdbc/pull/739) [55e2cd16](https://github.com/pgjdbc/pgjdbc/commit/55e2cd16cc7e30b1370e777d71556aa625bace9f)
* fix: strict handling of getBoolean and setObject with postgres accepted values [PR#732](https://github.com/pgjdbc/pgjdbc/pull/732) [4942f7d1](https://github.com/pgjdbc/pgjdbc/commit/4942f7d1cc812feeeca331878334a3d4058615e4)
* docs: move docs from www/documentation/head [PR#744](https://github.com/pgjdbc/pgjdbc/pull/744) [70e23c45](https://github.com/pgjdbc/pgjdbc/commit/70e23c45c904ebded42ab1b5efc10d66f34ab68c)
* test: fix replication test in Pg10 [PR#746](https://github.com/pgjdbc/pgjdbc/pull/746) [63ed2129](https://github.com/pgjdbc/pgjdbc/commit/63ed2129f7aa4375d009f19c64dc2404e725aabb)
* feat: use java.util.logging [PR#722](https://github.com/pgjdbc/pgjdbc/pull/722) [43e6505e](https://github.com/pgjdbc/pgjdbc/commit/43e6505e3aa16e6acdf08f02f2dd1e3cf131ac3e)
* perf: short circuit Oid.BOOL in getBoolean [PR#745](https://github.com/pgjdbc/pgjdbc/pull/745) [e69e4a1d](https://github.com/pgjdbc/pgjdbc/commit/e69e4a1d5502319bc810e0e4529611ba52ea386c)
* fix: add isLoggable around parameterized logger [PR#752](https://github.com/pgjdbc/pgjdbc/pull/752) [8b50cfe5](https://github.com/pgjdbc/pgjdbc/commit/8b50cfe58c47d19adc0c241488a24037f4a4416f)
* docs: move www repository to pgjdbc/docs [d4e99198](https://github.com/pgjdbc/pgjdbc/commit/d4e99198336bf41fc7f3fe9c0746036d5477601c)
* add syntax highlight to documentation [8c035ade](https://github.com/pgjdbc/pgjdbc/commit/8c035adeb820c8a19e372f0b76a70e511b657cad)
* add more style [9c510e65](https://github.com/pgjdbc/pgjdbc/commit/9c510e65f573354a47b7e94553870231e8dc2935)

Pavel Raiskup (3):

* fix: sync with latest Fedora [PR#637](https://github.com/pgjdbc/pgjdbc/pull/637) [a29ad80b](https://github.com/pgjdbc/pgjdbc/commit/a29ad80bcfdd65d11257f6339eb6bf2512612eed)
* packaging: rpm: update srpm generator [5c3c9239](https://github.com/pgjdbc/pgjdbc/commit/5c3c92392d66608ed7d9d1f96eb5b380e2667cd9)
* packaging: rpm_ci: use curl -L to download rawhide logs [c8125cff](https://github.com/pgjdbc/pgjdbc/commit/c8125cff3ae719675983185d49cd49e6c028d7b7)

Philippe Marschall (3):

* refactor: clean up PgDatabaseMetaData [PR#692](https://github.com/pgjdbc/pgjdbc/pull/692) [d32b077e](https://github.com/pgjdbc/pgjdbc/commit/d32b077e20b2afae9c4c80f704f7c8a26a8b8e11)
* refactor: delete Keyword enum [PR#697](https://github.com/pgjdbc/pgjdbc/pull/697) [677e3c4c](https://github.com/pgjdbc/pgjdbc/commit/677e3c4cf2533ce283a2d444cee6da8a94a4ca82)
* feat: support microsecond resolution for JSR-310 types [PR#691](https://github.com/pgjdbc/pgjdbc/pull/691) [6b3a1efb](https://github.com/pgjdbc/pgjdbc/commit/6b3a1efb36709e570caf0dc71a42a53e89f5a5e0)

Roman Ivanov (2):

* config: move version of checkstyle to property [PR#723](https://github.com/pgjdbc/pgjdbc/pull/723) [9ef7d6f1](https://github.com/pgjdbc/pgjdbc/commit/9ef7d6f1f274a75a14b380c57f4c140b47b25888)
* chore: upgrade checkstyle to 7.4, make checkstyle version configurable via property [PR#725](https://github.com/pgjdbc/pgjdbc/pull/725) [e1a25782](https://github.com/pgjdbc/pgjdbc/commit/e1a2578247684667a51bc34ee53449e4457755df)

Steve Ungerer (1):

* fix: ensure executeBatch() does not use server-side prepared statements when prepareThreshold=0 [PR#690](https://github.com/pgjdbc/pgjdbc/pull/690) [aca26a07](https://github.com/pgjdbc/pgjdbc/commit/aca26a07026e9b289a209799ab28131a33b296dd)

Trygve Laugstøl (1):

* feat: connect the socket only if the socket factory created an unconnected socket [PR#587](https://github.com/pgjdbc/pgjdbc/pull/587) [f75572be](https://github.com/pgjdbc/pgjdbc/commit/f75572beb8b59a7a73f6e1d12d692b483dc04c85)

Vladimir Gordiychuk (4):

* bug: fix not enscaped special symbol that fail build [PR#686](https://github.com/pgjdbc/pgjdbc/pull/686) [b4604cd7](https://github.com/pgjdbc/pgjdbc/commit/b4604cd7de0c297c8b56608642055ed6eb3645e0)
* feat: add replication protocol API [PR#550](https://github.com/pgjdbc/pgjdbc/pull/550) [f48c6bb7](https://github.com/pgjdbc/pgjdbc/commit/f48c6bb7e726479bbf4be4ef95e4c138db5cac96)
* test: fix drop replication slot on 9.4 for tests [PR#696](https://github.com/pgjdbc/pgjdbc/pull/696) [c1c48bd7](https://github.com/pgjdbc/pgjdbc/commit/c1c48bd705f04e8d1b1cb8b998dda0a6893f891d)
* chore: Gather backtrace from core dump on CI [PR#736](https://github.com/pgjdbc/pgjdbc/pull/736) [da5e4ef1](https://github.com/pgjdbc/pgjdbc/commit/da5e4ef1cbc4a5c9f41e9aee8d183560ba679541)

Vladimir Sitnikov (12):

* fedora: add BuildRequires: classloader-leak-test-framework [64b6750c](https://github.com/pgjdbc/pgjdbc/commit/64b6750cbd49ca8a9f0596bed1876132b64c34d4)
* tests: remove Class.forName(..driver..) from test code [c99507b5](https://github.com/pgjdbc/pgjdbc/commit/c99507b5661804b5b1b2340ba747fe553dc37a68)
* test: add CI tests against PostgreSQL 8.2 [PR#659](https://github.com/pgjdbc/pgjdbc/pull/659) [63ee60e2](https://github.com/pgjdbc/pgjdbc/commit/63ee60e2ad93441ff55dab08213cc09815b614e0)
* feat: display error position when SQL has unterminated literals, comments, etc [PR#688](https://github.com/pgjdbc/pgjdbc/pull/688) [8a95d991](https://github.com/pgjdbc/pgjdbc/commit/8a95d991e2032cf0406be8e54e70cfafaad794b5)
* feat: add support for PreparedStatement.setCharacterStream(int, Reader) [PR#671](https://github.com/pgjdbc/pgjdbc/pull/671) [ee4c4265](https://github.com/pgjdbc/pgjdbc/commit/ee4c4265aebc1c73a1d1fabac5ba259d1fbfd1e4)
* chore: update next version to 42.0.0-SNAPSHOT [46634923](https://github.com/pgjdbc/pgjdbc/commit/466349236622c6b03bb9cd8d7f517c3ce0586751)
* refactor: add CallableQueryKey#equals override [401c51a1](https://github.com/pgjdbc/pgjdbc/commit/401c51a15d80d2f24cddbe5cd4b2ca7509a0a765)
* doc: correct wording in readme regarding PostgreSQL versions used in pgjdbc regression testing [60391d75](https://github.com/pgjdbc/pgjdbc/commit/60391d7559d6ee4e579ec2e36d7aa8199a99189d)
* fix: revert ExpressionProperties to Properties in BaseDataSource [989b4d23](https://github.com/pgjdbc/pgjdbc/commit/989b4d235ec6776f31284d15618195cf7f8a7a79)
* fix: correct edge cases for setCharacterStream(int, Reader) [f1190d8c](https://github.com/pgjdbc/pgjdbc/commit/f1190d8c2eb1c506616e2298aabad211ba3bdb0a)
* test: add SharedTimerClassLoaderLeakTest to the regular test suite [b3553276](https://github.com/pgjdbc/pgjdbc/commit/b3553276ad48cbb47cd4849d3a08b7ee61be8ace)
* refactor: wrap more log statements with isLoggable check [07d225ec](https://github.com/pgjdbc/pgjdbc/commit/07d225ec1028d945533d5023702b3865aee54ad6)

bd-infor (1):

* docs: clarify handling of loglevel [PR#711](https://github.com/pgjdbc/pgjdbc/pull/711) [6334bac0](https://github.com/pgjdbc/pgjdbc/commit/6334bac036efaa4a9f2a445f1cbdcc310f4c6263)

### Contributors to this release

We thank the following people for their contributions to this release.

[AlexElin](https://github.com/AlexElin)
[bd-infor](https://github.com/bd-infor)
[Eric McCormack](https://github.com/ericmack)
[Jordan Lewis](https://github.com/jordanlewis)
[Jorge Solorzano](https://github.com/jorsol)
[Pavel Raiskup](https://github.com/praiskup)
[Philippe Marschall](https://github.com/marschall)
[Roman Ivanov](https://github.com/romani)
[Steve Ungerer](https://github.com/scubasau)
[Trygve Laugstøl](https://github.com/trygvis)
[Vladimir Gordiychuk](https://github.com/Gordiychuk)
[Vladimir Sitnikov](https://github.com/vlsi)


text:
package parsing.deserializers;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import parsing.inject.IWrapperFactory;
import parsing.wrappers.ChampionWrapper;
import parsing.wrappers.ItemWrapper;
import riotapi.staticdata.champion.ChampionDto;
import riotapi.staticdata.champion.ChampionListDto;
import riotapi.staticdata.item.ItemDto;
import riotapi.staticdata.item.ItemListDto;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;

public class ItemDeserializer implements JsonDeserializer<ItemWrapper>{

    private final Map<String, Map<Type, Object>> staticCache;
    private final Type itemType;
    private final Provider<IWrapperFactory> provider;
    private final String region;

    @Inject
    ItemDeserializer(
            Map<String, Map<Type, Object>> staticCache,
            TypeToken<ItemListDto> itemType,
            Provider<IWrapperFactory> provider,
            @Named("region") String region){
        this.staticCache = staticCache;
        this.itemType = itemType.getType();
        this.provider = provider;
        this.region = region;
    }

    @Override
    public ItemWrapper deserialize(JsonElement arg0, Type arg1,
            JsonDeserializationContext arg2) throws JsonParseException {

        ItemListDto itemList = (ItemListDto) staticCache.get(region).get(itemType);

        HashMap<String, ItemDto> itemMap = itemList.get_data();
        long itemId = Long.parseLong(arg0.getAsString());
        IWrapperFactory factory = provider.get();
        ItemDto item = itemMap.get("" + itemId);
        ItemWrapper wrapper = factory.getItemWrapper(item);

        return wrapper;
    }

}

redpajama_set_name: RedPajamaGithub


text:
package org.apache.flink.streaming.runtime.io;

import org.apache.flink.annotation.Internal;
import org.apache.flink.runtime.checkpoint.CheckpointException;
import org.apache.flink.runtime.checkpoint.CheckpointFailureReason;
import org.apache.flink.runtime.checkpoint.channel.InputChannelInfo;
import org.apache.flink.runtime.io.network.api.CancelCheckpointMarker;
import org.apache.flink.runtime.io.network.api.CheckpointBarrier;
import org.apache.flink.runtime.jobgraph.tasks.AbstractInvokable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayDeque;

/**
 * The {@link CheckpointBarrierTracker} keeps track of what checkpoint barriers have been received
 * from which input channels. Once it has observed all checkpoint barriers for a checkpoint ID, it
 * notifies its listener of a completed checkpoint.
 *
 *
Unlike the {@link CheckpointBarrierAligner}, the BarrierTracker does not block the input
 * channels that have sent barriers, so it cannot be used to gain "exactly-once" processing
 * guarantees. It can, however, be used to gain "at least once" processing guarantees.
 *
 *
NOTE: This implementation strictly assumes that newer checkpoints have higher checkpoint IDs.
 */
@Internal
public class CheckpointBarrierTracker extends CheckpointBarrierHandler {

    private static final Logger LOG = LoggerFactory.getLogger(CheckpointBarrierTracker.class);

    /**
     * The tracker tracks a maximum number of checkpoints, for which some, but not all barriers have
     * yet arrived.
     */
    private static final int MAX_CHECKPOINTS_TO_TRACK = 50;

    // ------------------------------------------------------------------------

    /**
     * The number of channels. Once that many barriers have been received for a checkpoint, the
     * checkpoint is considered complete.
     */
    private final int totalNumberOfInputChannels;

    /**
     * All checkpoints for which some (but not all) barriers have been received, and that are not
     * yet known to be subsumed by newer checkpoints.
     */
    private final ArrayDeque<CheckpointBarrierCount> pendingCheckpoints;

    /** The highest checkpoint ID encountered so far. */
    private long latestPendingCheckpointID = -1;

    public CheckpointBarrierTracker(
            int totalNumberOfInputChannels, AbstractInvokable toNotifyOnCheckpoint) {
        super(toNotifyOnCheckpoint);
        this.totalNumberOfInputChannels = totalNumberOfInputChannels;
        this.pendingCheckpoints = new ArrayDeque<>();
    }

    public void processBarrier(CheckpointBarrier receivedBarrier, InputChannelInfo channelInfo)
            throws IOException {
        final long barrierId = receivedBarrier.getId();

        // fast path for single channel trackers
        if (totalNumberOfInputChannels == 1) {
            markAlignmentStartAndEnd(receivedBarrier.getTimestamp());
            notifyCheckpoint(receivedBarrier);
            return;
        }

        // general path for multiple input channels
        if (LOG.isDebugEnabled()) {
            LOG.debug("Received barrier for checkpoint {} from channel {}", barrierId, channelInfo);
        }

        // find the checkpoint barrier in the queue of pending barriers
        CheckpointBarrierCount barrierCount = null;
        int pos = 0;

        for (CheckpointBarrierCount next : pendingCheckpoints) {
            if (next.checkpointId == barrierId) {
                barrierCount = next;
                break;
            }
            pos++;
        }

        if (barrierCount != null) {
            // add one to the count to that barrier and check for completion
            int numBarriersNew = barrierCount.incrementBarrierCount();
            if (numBarriersNew == totalNumberOfInputChannels) {
                // checkpoint can be triggered (or is aborted and all barriers have been seen)
                // first, remove this checkpoint and all all prior pending
                // checkpoints (which are now subsumed)
                for (int i = 0; i <= pos; i++) {
                    pendingCheckpoints.pollFirst();
                }

                // notify the listener
                if (!barrierCount.isAborted()) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Received all barriers for checkpoint {}", barrierId);
                    }
                    markAlignmentEnd();
                    notifyCheckpoint(receivedBarrier);
                }
            }
        } else {
            // first barrier for that checkpoint ID
            // add it only if it is newer than the latest checkpoint.
            // if it is not newer than the latest checkpoint ID, then there cannot be a
            // successful checkpoint for that ID anyways
            if (barrierId > latestPendingCheckpointID) {
                markAlignmentStart(receivedBarrier.getTimestamp());
                latestPendingCheckpointID = barrierId;
                pendingCheckpoints.addLast(new CheckpointBarrierCount(barrierId));

                // make sure we do not track too many checkpoints
                if (pendingCheckpoints.size() > MAX_CHECKPOINTS_TO_TRACK) {
                    pendingCheckpoints.pollFirst();
                }
            }
        }
    }

    @Override
    public void processBarrierAnnouncement(
            CheckpointBarrier announcedBarrier, int sequenceNumber, InputChannelInfo channelInfo)
            throws IOException {
        // Ignore
    }

    @Override
    public void processCancellationBarrier(CancelCheckpointMarker cancelBarrier)
            throws IOException {
        final long checkpointId = cancelBarrier.getCheckpointId();

        if (LOG.isDebugEnabled()) {
            LOG.debug("Received cancellation barrier for checkpoint {}", checkpointId);
        }

        // fast path for single channel trackers
        if (totalNumberOfInputChannels == 1) {
            notifyAbortOnCancellationBarrier(checkpointId);
            return;
        }

        // -- general path for multiple input channels --

        // find the checkpoint barrier in the queue of pending barriers
        // while doing this we "abort" all checkpoints before that one
        CheckpointBarrierCount cbc;
        while ((cbc = pendingCheckpoints.peekFirst()) != null
                && cbc.checkpointId() < checkpointId) {
            pendingCheckpoints.removeFirst();

            if (cbc.markAborted()) {
                // abort the subsumed checkpoints if not already done
                notifyAbortOnCancellationBarrier(cbc.checkpointId());
            }
        }

        if (cbc != null && cbc.checkpointId() == checkpointId) {
            // make sure the checkpoint is remembered as aborted
            if (cbc.markAborted()) {
                // this was the first time the checkpoint was aborted - notify
                notifyAbortOnCancellationBarrier(checkpointId);
            }

            // we still count the barriers to be able to remove the entry once all barriers have
            // been seen
            if (cbc.incrementBarrierCount() == totalNumberOfInputChannels) {
                // we can remove this entry
                pendingCheckpoints.removeFirst();
            }
        } else if (checkpointId > latestPendingCheckpointID) {
            notifyAbortOnCancellationBarrier(checkpointId);

            latestPendingCheckpointID = checkpointId;

            CheckpointBarrierCount abortedMarker = new CheckpointBarrierCount(checkpointId);
            abortedMarker.markAborted();
            pendingCheckpoints.addFirst(abortedMarker);

            // we have removed all other pending checkpoint barrier counts --> no need to check that
            // we don't exceed the maximum checkpoints to track
        } else {
            // trailing cancellation barrier which was already cancelled
        }
    }

    @Override
    public void processEndOfPartition() throws IOException {
        while (!pendingCheckpoints.isEmpty()) {
            CheckpointBarrierCount barrierCount = pendingCheckpoints.removeFirst();
            if (barrierCount.markAborted()) {
                notifyAbort(
                        barrierCount.checkpointId(),
                        new CheckpointException(
                                CheckpointFailureReason.CHECKPOINT_DECLINED_INPUT_END_OF_STREAM));
            }
        }
    }

    public long getLatestCheckpointId() {
        return pendingCheckpoints.isEmpty() ? -1 : pendingCheckpoints.peekLast().checkpointId();
    }

    public boolean isCheckpointPending() {
        return !pendingCheckpoints.isEmpty();
    }

    /** Simple class for a checkpoint ID with a barrier counter. */
    private static final class CheckpointBarrierCount {

        private final long checkpointId;

        private int barrierCount;

        private boolean aborted;

        CheckpointBarrierCount(long checkpointId) {
            this.checkpointId = checkpointId;
            this.barrierCount = 1;
        }

        public long checkpointId() {
            return checkpointId;
        }

        public int incrementBarrierCount() {
            return ++barrierCount;
        }

        public boolean isAborted() {
            return aborted;
        }

        public boolean markAborted() {
            boolean firstAbort = !this.aborted;
            this.aborted = true;
            return firstAbort;
        }

        @Override
        public String toString() {
            return isAborted()
                    ? String.format("checkpointID=%d - ABORTED", checkpointId)
                    : String.format("checkpointID=%d, count=%d", checkpointId, barrierCount);
        }
    }
}


text:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * A concrete renderer for HTML_QuickForm, makes an array of form contents
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.01 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_01.txt If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category    HTML
 * @package     HTML_QuickForm
 * @author      Alexey Borzov <avb@php.net>
 * @author      Adam Daniel <adaniel1@eesus.jnj.com>
 * @author      Bertrand Mansion <bmansion@mamasam.com>
 * @author      Thomas Schulz <ths@4bconsult.de>
 * @copyright   2001-2011 The PHP Group
 * @license     http://www.php.net/license/3_01.txt PHP License 3.01
 * @version     CVS: $Id: Array.php 317587 2011-10-01 07:55:53Z avb $
 * @link        http://pear.php.net/package/HTML_QuickForm
 */

/**
 * An abstract base class for QuickForm renderers
 */
require_once 'HTML/QuickForm/Renderer.php';

/**
 * A concrete renderer for HTML_QuickForm, makes an array of form contents
 *
 * Based on old HTML_QuickForm::toArray() code.
 *
 * The form array structure is the following:
 *

 * array(
 *   'frozen'           => 'whether the form is frozen',
 *   'javascript'       => 'javascript for client-side validation',
 *   'attributes'       => 'attributes for <form> tag',
 *   'requirednote      => 'note about the required elements',
 *   // if we set the option to collect hidden elements
 *   'hidden'           => 'collected html of all hidden elements',
 *   // if there were some validation errors:
 *   'errors' => array(
 *     '1st element name' => 'Error for the 1st element',
 *     ...
 *     'nth element name' => 'Error for the nth element'
 *   ),
 *   // if there are no headers in the form:
 *   'elements' => array(
 *     element_1,
 *     ...
 *     element_N
 *   )
 *   // if there are headers in the form:
 *   'sections' => array(
 *     array(
 *       'header'   => 'Header text for the first header',
 *       'name'     => 'Header name for the first header',
 *       'elements' => array(
 *          element_1,
 *          ...
 *          element_K1
 *       )
 *     ),
 *     ...
 *     array(
 *       'header'   => 'Header text for the Mth header',
 *       'name'     => 'Header name for the Mth header',
 *       'elements' => array(
 *          element_1,
 *          ...
 *          element_KM
 *       )
 *     )
 *   )
 * );
 *

 *
 * where element_i is an array of the form:
 *

 * array(
 *   'name'      => 'element name',
 *   'value'     => 'element value',
 *   'type'      => 'type of the element',
 *   'frozen'    => 'whether element is frozen',
 *   'label'     => 'label for the element',
 *   'required'  => 'whether element is required',
 *   'error'     => 'error associated with the element',
 *   'style'     => 'some information about element style (e.g. for Smarty)',
 *   // if element is not a group
 *   'html'      => 'HTML for the element'
 *   // if element is a group
 *   'separator' => 'separator for group elements',
 *   'elements'  => array(
 *     element_1,
 *     ...
 *     element_N
 *   )
 * );
 *

 *
 * @category    HTML
 * @package     HTML_QuickForm
 * @author      Alexey Borzov <avb@php.net>
 * @author      Adam Daniel <adaniel1@eesus.jnj.com>
 * @author      Bertrand Mansion <bmansion@mamasam.com>
 * @author      Thomas Schulz <ths@4bconsult.de>
 * @version     Release: 3.2.13
 * @since       3.0
 */
class HTML_QuickForm_Renderer_Array extends HTML_QuickForm_Renderer
{
   /**#@+
    * @access private
    */
   /**
    * An array being generated
    * @var array
    */
    var $_ary;

   /**
    * Number of sections in the form (i.e. number of headers in it)
    * @var integer
    */
    var $_sectionCount;

   /**
    * Current section number
    * @var integer
    */
    var $_currentSection;

   /**
    * Array representing current group
    * @var array
    */
    var $_currentGroup = null;

   /**
    * Additional style information for different elements
    * @var array
    */
    var $_elementStyles = array();

   /**
    * true: collect all hidden elements into string; false: process them as usual form elements
    * @var bool
    */
    var $_collectHidden = false;

   /**
    * true:  render an array of labels to many labels, $key 0 named 'label', the rest "label_$key"
    * false: leave labels as defined
    * @var bool
    */
    var $_staticLabels = false;
   /**#@-*/

   /**
    * Constructor
    *
    * @param  bool    true: collect all hidden elements into string; false: process them as usual form elements
    * @param  bool    true: render an array of labels to many labels, $key 0 to 'label' and the oterh to "label_$key"
    * @access public
    */
    function HTML_QuickForm_Renderer_Array($collectHidden = false, $staticLabels = false)
    {
        $this->HTML_QuickForm_Renderer();
        $this->_collectHidden = $collectHidden;
        $this->_staticLabels  = $staticLabels;
    } // end constructor

   /**
    * Returns the resultant array
    *
    * @access public
    * @return array
    */
    function toArray()
    {
        return $this->_ary;
    }

    function startForm(&$form)
    {
        $this->_ary = array(
            'frozen'            => $form->isFrozen(),
            'javascript'        => $form->getValidationScript(),
            'attributes'        => $form->getAttributes(true),
            'requirednote'      => $form->getRequiredNote(),
            'errors'            => array()
        );
        if ($this->_collectHidden) {
            $this->_ary['hidden'] = '';
        }
        $this->_elementIdx     = 1;
        $this->_currentSection = null;
        $this->_sectionCount   = 0;
    } // end func startForm

    function renderHeader(&$header)
    {
        $this->_ary['sections'][$this->_sectionCount] = array(
            'header' => $header->toHtml(),
            'name'   => $header->getName()
        );
        $this->_currentSection = $this->_sectionCount++;
    } // end func renderHeader

    function renderElement(&$element, $required, $error)
    {
        $elAry = $this->_elementToArray($element, $required, $error);
        if (!empty($error)) {
            $this->_ary['errors'][$elAry['name']] = $error;
        }
        $this->_storeArray($elAry);
    } // end func renderElement

    function renderHidden(&$element)
    {
        if ($this->_collectHidden) {
            $this->_ary['hidden'] .= $element->toHtml() . "\n";
        } else {
            $this->renderElement($element, false, null);
        }
    } // end func renderHidden

    function startGroup(&$group, $required, $error)
    {
        $this->_currentGroup = $this->_elementToArray($group, $required, $error);
        if (!empty($error)) {
            $this->_ary['errors'][$this->_currentGroup['name']] = $error;
        }
    } // end func startGroup

    function finishGroup(&$group)
    {
        $this->_storeArray($this->_currentGroup);
        $this->_currentGroup = null;
    } // end func finishGroup

   /**
    * Creates an array representing an element
    *
    * @access private
    * @param  HTML_QuickForm_element    element being processed
    * @param  bool                      Whether an element is required
    * @param  string                    Error associated with the element
    * @return array
    */
    function _elementToArray(&$element, $required, $error)
    {
        $ret = array(
            'name'      => $element->getName(),
            'value'     => $element->getValue(),
            'type'      => $element->getType(),
            'frozen'    => $element->isFrozen(),
            'required'  => $required,
            'error'     => $error
        );
        // render label(s)
        $labels = $element->getLabel();
        if (is_array($labels) && $this->_staticLabels) {
            foreach($labels as $key => $label) {
                $key = is_int($key)? $key + 1: $key;
                if (1 === $key) {
                    $ret['label'] = $label;
                } else {
                    $ret['label_' . $key] = $label;
                }
            }
        } else {
            $ret['label'] = $labels;
        }

        // set the style for the element
        if (isset($this->_elementStyles[$ret['name']])) {
            $ret['style'] = $this->_elementStyles[$ret['name']];
        }
        if ('group' == $ret['type']) {
            $ret['separator'] = $element->_separator;
            $ret['elements']  = array();
        } else {
            $ret['html']      = $element->toHtml();
        }
        return $ret;
    }

   /**
    * Stores an array representation of an element in the form array
    *
    * @access private
    * @param array  Array representation of an element
    * @return void
    */
    function _storeArray($elAry)
    {
        // where should we put this element...
        if (is_array($this->_currentGroup) && ('group' != $elAry['type'])) {
            $this->_currentGroup['elements'][] = $elAry;
        } elseif (isset($this->_currentSection)) {
            $this->_ary['sections'][$this->_currentSection]['elements'][] = $elAry;
        } else {
            $this->_ary['elements'][] = $elAry;
        }
    }

   /**
    * Sets a style to use for element rendering
    *
    * @param mixed      element name or array ('element name' => 'style name')
    * @param string     style name if $elementName is not an array
    * @access public
    * @return void
    */
    function setElementStyle($elementName, $styleName = null)
    {
        if (is_array($elementName)) {
            $this->_elementStyles = array_merge($this->_elementStyles, $elementName);
        } else {
            $this->_elementStyles[$elementName] = $styleName;
        }
    }
}
?>


text:
<?php

/**
 * Configuration for sfAjaxDebugPlugin
 *
 * @package    sfAjaxDebugPlugin
 * @author     Andreas Ferber <af+symfony@chaos-agency.de>
 * @version    SVN: $Id$
 */

if (sfConfig::get('sf_web_debug') && sfConfig::get('sf_ajax_debug')) {
    if (!in_array('sfAjaxDebug', sfConfig::get('sf_enabled_modules', array()))) {
        $this->dispatcher->notify(new sfEvent($this, 'application.log', array('sfAjaxDebug module not enabled', 'priority' => sfLogger::DEBUG)));
        sfConfig::set('sf_ajax_debug', false);
    }
    elseif (sfConfig::get('sf_ajax_debug_routes')) {
        $this->dispatcher->connect('routing.load_configuration', array('sfAjaxDebugRouting', 'listenToRoutingLoadConfigurationEvent'));
    }
}

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */


text:
package me.reed.sample;

import android.Manifest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import java.util.List;

import me.reed.permissionaccess.PermissionAccess;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                PermissionAccess.getInstance().requestPermission(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION)
                        .setRationale("测试")
                        .setCallback(new PermissionAccess.PermissionCallback() {
                            @Override
                            public void onPermissionError(List<String> permissions) {
                                for (int i = 0; i < permissions.size(); i++) {
                                    Log.i("未通过的权限", permissions.get(i));
                                }
                            }

                            @Override
                            public void onPermissionSuccess() {
                                Log.i("权限结果", "通过");
                            }
                        }).start(MainActivity.this);
            }
        });
    }
}


text:
/**
 * Contains tuple implementations related to the Transactional Collection layout.
 */
package org.gradoop.flink.model.impl.layouts.transactional.tuples;


text:
/**
 * Data mask pattern reference
 * @type {Object}
 */
exports.Patterns = {
  PATTERN000: 0,
  PATTERN001: 1,
  PATTERN010: 2,
  PATTERN011: 3,
  PATTERN100: 4,
  PATTERN101: 5,
  PATTERN110: 6,
  PATTERN111: 7
}

/**
 * Weighted penalty scores for the undesirable features
 * @type {Object}
 */
var PenaltyScores = {
  N1: 3,
  N2: 3,
  N3: 40,
  N4: 10
}

/**
* Find adjacent modules in row/column with the same color
* and assign a penalty value.
*
* Points: N1 + i
* i is the amount by which the number of adjacent modules of the same color exceeds 5
*/
exports.getPenaltyN1 = function getPenaltyN1 (data) {
  var size = data.size
  var points = 0
  var sameCountCol = 0
  var sameCountRow = 0
  var lastCol = null
  var lastRow = null

  for (var row = 0; row < size; row++) {
    sameCountCol = sameCountRow = 0
    lastCol = lastRow = null

    for (var col = 0; col < size; col++) {
      var module = data.get(row, col)
      if (module === lastCol) {
        sameCountCol++
      } else {
        if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5)
        lastCol = module
        sameCountCol = 1
      }

      module = data.get(col, row)
      if (module === lastRow) {
        sameCountRow++
      } else {
        if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5)
        lastRow = module
        sameCountRow = 1
      }
    }

    if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5)
    if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5)
  }

  return points
}

/**
 * Find 2x2 blocks with the same color and assign a penalty value
 *
 * Points: N2 * (m - 1) * (n - 1)
 */
exports.getPenaltyN2 = function getPenaltyN2 (data) {
  var size = data.size
  var points = 0

  for (var row = 0; row < size - 1; row++) {
    for (var col = 0; col < size - 1; col++) {
      var last = data.get(row, col) +
        data.get(row, col + 1) +
        data.get(row + 1, col) +
        data.get(row + 1, col + 1)

      if (last === 4 || last === 0) points++
    }
  }

  return points * PenaltyScores.N2
}

/**
 * Find 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column,
 * preceded or followed by light area 4 modules wide
 *
 * Points: N3 * number of pattern found
 */
exports.getPenaltyN3 = function getPenaltyN3 (data) {
  var size = data.size
  var points = 0
  var bitsCol = 0
  var bitsRow = 0

  for (var row = 0; row < size; row++) {
    bitsCol = bitsRow = 0
    for (var col = 0; col < size; col++) {
      bitsCol = ((bitsCol << 1) & 0x7FF) | data.get(row, col)
      if (col >= 10 && (bitsCol === 0x5D0 || bitsCol === 0x05D)) points++

      bitsRow = ((bitsRow << 1) & 0x7FF) | data.get(col, row)
      if (col >= 10 && (bitsRow === 0x5D0 || bitsRow === 0x05D)) points++
    }
  }

  return points * PenaltyScores.N3
}

/**
 * Calculate proportion of dark modules in entire symbol
 *
 * Points: N4 * k
 *
 * k is the rating of the deviation of the proportion of dark modules
 * in the symbol from 50% in steps of 5%
 */
exports.getPenaltyN4 = function getPenaltyN4 (data) {
  var darkCount = 0
  var modulesCount = data.data.length

  for (var i = 0; i < modulesCount; i++) darkCount += data.data[i]

  var k = Math.abs(Math.ceil((darkCount * 100 / modulesCount) / 5) - 10)

  return k * PenaltyScores.N4
}

/**
 * Return mask value at given position
 *
 * @param  {Number} maskPattern Pattern reference value
 * @param  {Number} i           Row
 * @param  {Number} j           Column
 * @return {Boolean}            Mask value
 */
function getMaskAt (maskPattern, i, j) {
  switch (maskPattern) {
    case exports.Patterns.PATTERN000: return (i + j) % 2 === 0
    case exports.Patterns.PATTERN001: return i % 2 === 0
    case exports.Patterns.PATTERN010: return j % 3 === 0
    case exports.Patterns.PATTERN011: return (i + j) % 3 === 0
    case exports.Patterns.PATTERN100: return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0
    case exports.Patterns.PATTERN101: return (i * j) % 2 + (i * j) % 3 === 0
    case exports.Patterns.PATTERN110: return ((i * j) % 2 + (i * j) % 3) % 2 === 0
    case exports.Patterns.PATTERN111: return ((i * j) % 3 + (i + j) % 2) % 2 === 0

    default: throw new Error('bad maskPattern:' + maskPattern)
  }
}

/**
 * Apply a mask pattern to a BitMatrix
 *
 * @param  {Number}    pattern Pattern reference number
 * @param  {BitMatrix} data    BitMatrix data
 */
exports.applyMask = function applyMask (pattern, data) {
  var size = data.size

  for (var col = 0; col < size; col++) {
    for (var row = 0; row < size; row++) {
      if (data.isReserved(row, col)) continue
      data.xor(row, col, getMaskAt(pattern, row, col))
    }
  }
}

/**
 * Returns the best mask pattern for data
 *
 * @param  {BitMatrix} data
 * @return {Number} Mask pattern reference number
 */
exports.getBestMask = function getBestMask (data, setupFormatFunc) {
  var numPatterns = Object.keys(exports.Patterns).length
  var bestPattern = 0
  var lowerPenalty = Infinity

  for (var p = 0; p < numPatterns; p++) {
    setupFormatFunc(p)
    exports.applyMask(p, data)

    // Calculate penalty
    var penalty =
      exports.getPenaltyN1(data) +
      exports.getPenaltyN2(data) +
      exports.getPenaltyN3(data) +
      exports.getPenaltyN4(data)

    // Undo previously applied mask
    exports.applyMask(p, data)

    if (penalty < lowerPenalty) {
      lowerPenalty = penalty
      bestPattern = p
    }
  }

  return bestPattern
}


text:
#include <iostream>

#include <cryptolens/core.hpp>
#include <cryptolens/Error.hpp>
#include <cryptolens/Configuration_Windows.hpp>
#include <cryptolens/MachineCodeComputer_static.hpp>

namespace cryptolens = ::cryptolens_io::v20190401;
using Cryptolens = cryptolens::basic_Cryptolens<cryptolens::Configuration_Windows<cryptolens::MachineCodeComputer_static>>;

/*
* This example uses the basic_SKM class to make a request to the WebAPI
* and then checks some properties of the license keys.
*/
int main()
{
	cryptolens::Error e;
	Cryptolens cryptolens_handle(e);

	// Setting up the signature verifier with credentials from "Security Settings"
	// on serialkeymanager.com
	cryptolens_handle.signature_verifier.set_modulus_base64(e, "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==");
	cryptolens_handle.signature_verifier.set_exponent_base64(e, "AQAB");

	cryptolens_handle.machine_code_computer.set_machine_code(e, "289jf2afs3");

	cryptolens::optional<cryptolens::LicenseKey> license_key =
		cryptolens_handle.activate(
			e,
			// SKM Access Token
			"WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0=",
			// Product id
			3646,
			// License Key
			"MPDWY-PQAOW-FKSCH-SGAAU"
		);

	if (e) {
		using namespace cryptolens::errors;

		if (e.get_subsystem() == Subsystem::Main) {
			// Handle errors from the SKM API
			std::cout << "SKM error: " << e.get_reason() << std::endl;
		}
		else if (e.get_subsystem() == Subsystem::RequestHandler) {
			size_t code = e.get_extra();
			std::cout << "Error connecting to the server: error code: " << code << std::endl;
		}
		else {
			std::cout << "Unhandled error: " << e.get_subsystem() << " " << e.get_reason() << " " << e.get_extra() << std::endl;
		}
		return 1;
	}

	std::cout << "License key for product with id: " << license_key->get_product_id() << std::endl;

	// Use LicenseKeyChecker to check properties of the license key
	if (license_key->check().has_expired(1234567)) {
		std::cout << "Your subscription has expired." << std::endl;
		return 1;
	}

	if (license_key->check().has_feature(1)) { std::cout << "Welcome! Pro version enabled!" << std::endl; }
	else { std::cout << "Welcome!" << std::endl; }

	std::string EXIT; std::getline(std::cin, EXIT);
}


text:
package k8s

import (
	"encoding/json"
	"fmt"
	"testing"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/pkg/annotation"
	"github.com/cilium/cilium/pkg/checker"
	"github.com/cilium/cilium/pkg/identity"
	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/core/v1"
	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
	slim_networkingv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/networking/v1"
	"github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/util/intstr"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/policy"
	"github.com/cilium/cilium/pkg/policy/api"
	"github.com/cilium/cilium/pkg/testutils"

	. "gopkg.in/check.v1"
	"k8s.io/apimachinery/pkg/types"
)

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) {
	TestingT(t)
}

type K8sSuite struct{}

var _ = Suite(&K8sSuite{})

var (
	labelsA = labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("id", "a", labels.LabelSourceK8s),
	}

	labelSelectorA = slim_metav1.LabelSelector{
		MatchLabels: map[string]string{
			"id": "a",
		},
	}

	labelsB = labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("id1", "b", labels.LabelSourceK8s),
		labels.NewLabel("id2", "c", labels.LabelSourceK8s),
	}

	labelsC = labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("id", "c", labels.LabelSourceK8s),
	}

	labelSelectorC = slim_metav1.LabelSelector{
		MatchLabels: map[string]string{
			"id": "c",
		},
	}

	ctxAToB = policy.SearchContext{
		From:  labelsA,
		To:    labelsB,
		Trace: policy.TRACE_VERBOSE,
	}

	ctxAToC = policy.SearchContext{
		From:  labelsA,
		To:    labelsC,
		Trace: policy.TRACE_VERBOSE,
	}

	port80 = slim_networkingv1.NetworkPolicyPort{
		Port: &intstr.IntOrString{
			Type:   intstr.Int,
			IntVal: 80,
		},
	}

	dummySelectorCacheUser = &DummySelectorCacheUser{}
)

type DummySelectorCacheUser struct{}

func testNewPolicyRepository() *policy.Repository {
	repo := policy.NewPolicyRepository(nil, nil)
	repo.GetSelectorCache().SetLocalIdentityNotifier(testutils.NewDummyIdentityNotifier())
	return repo
}

func (d *DummySelectorCacheUser) IdentitySelectionUpdated(selector policy.CachedSelector, added, deleted []identity.NumericIdentity) {
}

func (s *K8sSuite) TestParseNetworkPolicyIngress(c *C) {
	netPolicy := &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: slim_metav1.LabelSelector{
				MatchLabels: map[string]string{
					"foo1": "bar1",
					"foo2": "bar2",
				},
			},
			Ingress: []slim_networkingv1.NetworkPolicyIngressRule{
				{
					From: []slim_networkingv1.NetworkPolicyPeer{
						{
							PodSelector: &slim_metav1.LabelSelector{
								MatchLabels: map[string]string{
									"foo3": "bar3",
									"foo4": "bar4",
								},
							},
						},
					},
					Ports: []slim_networkingv1.NetworkPolicyPort{
						{
							Port: &intstr.IntOrString{
								Type:   intstr.Int,
								IntVal: 80,
							},
						},
					},
				},
			},
		},
	}

	_, err := ParseNetworkPolicy(netPolicy)
	c.Assert(err, IsNil)

	fromEndpoints := labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("foo3", "bar3", labels.LabelSourceK8s),
		labels.NewLabel("foo4", "bar4", labels.LabelSourceK8s),
	}

	ctx := policy.SearchContext{
		From: fromEndpoints,
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s),
			labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}

	rules, err := ParseNetworkPolicy(netPolicy)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	repo := testNewPolicyRepository()

	repo.AddList(rules)
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied)

	epSelector := api.NewESFromLabels(fromEndpoints...)
	cachedEPSelector, _ := repo.GetSelectorCache().AddIdentitySelector(dummySelectorCacheUser, epSelector)
	defer func() { repo.GetSelectorCache().RemoveSelector(cachedEPSelector, dummySelectorCacheUser) }()

	ingressL4Policy, err := repo.ResolveL4IngressPolicy(&ctx)
	c.Assert(ingressL4Policy, Not(IsNil))
	c.Assert(err, IsNil)
	c.Assert(ingressL4Policy, checker.Equals, policy.L4PolicyMap{
		"80/TCP": {
			Port: 80, Protocol: api.ProtoTCP, U8Proto: 6,
			L7Parser:           policy.ParserTypeNone,
			L7RulesPerSelector: policy.L7DataMap{cachedEPSelector: nil},
			Ingress:            true,
			DerivedFromRules: []labels.LabelArray{
				labels.ParseLabelArray(
					"k8s:"+k8sConst.PolicyLabelName,
					"k8s:"+k8sConst.PolicyLabelUID,
					"k8s:"+k8sConst.PolicyLabelNamespace+"=default",
					"k8s:"+k8sConst.PolicyLabelDerivedFrom+"="+resourceTypeNetworkPolicy,
				),
			},
		},
	})
	ingressL4Policy.Detach(repo.GetSelectorCache())

	ctx.To = labels.LabelArray{
		labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s),
	}

	// ctx.To needs to have all labels from the policy in order to be accepted
	c.Assert(repo.AllowsIngressRLocked(&ctx), Not(Equals), api.Allowed)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel("foo3", "bar3", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s),
			labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// ctx.From also needs to have all labels from the policy in order to be accepted
	c.Assert(repo.AllowsIngressRLocked(&ctx), Not(Equals), api.Allowed)
}

func (s *K8sSuite) TestParseNetworkPolicyMultipleSelectors(c *C) {

	// Rule with multiple selectors in egress and ingress
	ex1 := []byte(`{
"kind":"NetworkPolicy",
"apiVersion":"extensions/networkingv1",
"metadata":{
  "name":"ingress-multiple-selectors"
},
"spec":{
  "podSelector":{
    "matchLabels":{
      "role":"backend"
    }
  },
  "egress":[
    {
      "ports":[
        {
          "protocol":"TCP",
          "port":5432
        }
      ],
      "to":[
        {
          "podSelector":{
            "matchLabels":{
              "app":"db1"
            }
          }
        },
        {
          "podSelector":{
            "matchLabels":{
              "app":"db2"
            }
          }
        }
      ]
    }
  ],
  "ingress":[
    {
      "from":[
        {
          "podSelector":{
            "matchLabels":{
              "role":"frontend"
            }
          },
          "namespaceSelector":{
            "matchLabels":{
              "project":"myproject"
            }
          }
        },
        {
          "podSelector":{
            "matchLabels":{
              "app":"inventory"
            }
          }
        }
      ]
    }
  ]
}
}`)

	np := slim_networkingv1.NetworkPolicy{}
	err := json.Unmarshal(ex1, &np)
	c.Assert(err, IsNil)

	rules, err := ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	repo := testNewPolicyRepository()
	repo.AddList(rules)

	endpointLabels := labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("role", "backend", labels.LabelSourceK8s),
	}

	// Ingress context
	ctx := policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		To:    endpointLabels,
		Trace: policy.TRACE_VERBOSE,
	}

	// should be DENIED because ctx.From is missing the namespace selector
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied)

	ctx.From = labels.LabelArray{
		labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "project"), "myproject", labels.LabelSourceK8s),
	}

	// should be ALLOWED with the namespace label properly set
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	ctx.From = labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("app", "inventory", labels.LabelSourceK8s),
	}

	// should be ALLOWED since all rules in From must match
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	// Egress context
	ctx = policy.SearchContext{
		From: endpointLabels,
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("app", "db1", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}

	// should be DENIED because DPorts are missing in context
	c.Assert(repo.AllowsEgressRLocked(&ctx), Equals, api.Denied)

	ctx.DPorts = []*models.Port{{Port: 5432, Protocol: models.PortProtocolTCP}}

	// should be ALLOWED with DPorts set correctly
	c.Assert(repo.AllowsEgressRLocked(&ctx), Equals, api.Allowed)

	ctx.To = labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("app", "db2", labels.LabelSourceK8s),
	}

	// should be ALLOWED for db2 as well
	c.Assert(repo.AllowsEgressRLocked(&ctx), Equals, api.Allowed)
}

func (s *K8sSuite) TestParseNetworkPolicyNoSelectors(c *C) {

	// Ingress with neither pod nor namespace selector set.
	ex1 := []byte(`{
"kind": "NetworkPolicy",
"apiVersion": "extensions/networkingv1",
"metadata": {
  "name": "ingress-cidr-test",
  "namespace": "myns",
  "uid": "11bba160-ddca-11e8-b697-0800273b04ff"
},
"spec": {
  "podSelector": {
    "matchLabels": {
      "role": "backend"
    }
  },
  "ingress": [
    {
      "from": [
        {
          "ipBlock": {
            "cidr": "10.0.0.0/8",
	          "except": [
	            "10.96.0.0/12"
	          ]
          }
        }
      ]
    }
  ]
}
}`)

	fromEndpoints := labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s),
		labels.NewLabel("role", "backend", labels.LabelSourceK8s),
	}

	epSelector := api.NewESFromLabels(fromEndpoints...)
	np := slim_networkingv1.NetworkPolicy{}
	err := json.Unmarshal(ex1, &np)
	c.Assert(err, IsNil)

	expectedRule := api.NewRule().
		WithEndpointSelector(epSelector).
		WithIngressRules([]api.IngressRule{
			{
				FromCIDRSet: []api.CIDRRule{
					{
						Cidr: api.CIDR("10.0.0.0/8"),
						ExceptCIDRs: []api.CIDR{
							"10.96.0.0/12",
						},
					},
				},
			},
		}).
		WithEgressRules([]api.EgressRule{}).
		WithLabels(labels.ParseLabelArray(
			"k8s:"+k8sConst.PolicyLabelName+"=ingress-cidr-test",
			"k8s:"+k8sConst.PolicyLabelUID+"=11bba160-ddca-11e8-b697-0800273b04ff",
			"k8s:"+k8sConst.PolicyLabelNamespace+"=myns",
			"k8s:"+k8sConst.PolicyLabelDerivedFrom+"="+resourceTypeNetworkPolicy,
		))

	expectedRule.Sanitize()

	expectedRules := api.Rules{
		expectedRule,
	}

	rules, err := ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(rules, NotNil)
	c.Assert(rules, checker.DeepEquals, expectedRules)
}

func (s *K8sSuite) TestParseNetworkPolicyEgress(c *C) {

	netPolicy := &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: slim_metav1.LabelSelector{
				MatchLabels: map[string]string{
					"foo1": "bar1",
					"foo2": "bar2",
				},
			},
			Egress: []slim_networkingv1.NetworkPolicyEgressRule{
				{
					To: []slim_networkingv1.NetworkPolicyPeer{
						{
							PodSelector: &slim_metav1.LabelSelector{
								MatchLabels: map[string]string{
									"foo3": "bar3",
									"foo4": "bar4",
								},
							},
						},
					},
					Ports: []slim_networkingv1.NetworkPolicyPort{
						{
							Port: &intstr.IntOrString{
								Type:   intstr.Int,
								IntVal: 80,
							},
						},
					},
				},
			},
		},
	}

	rules, err := ParseNetworkPolicy(netPolicy)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	fromEndpoints := labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s),
		labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s),
	}

	toEndpoints := labels.LabelArray{
		labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
		labels.NewLabel("foo3", "bar3", labels.LabelSourceK8s),
		labels.NewLabel("foo4", "bar4", labels.LabelSourceK8s),
	}

	ctx := policy.SearchContext{
		From:  fromEndpoints,
		To:    toEndpoints,
		Trace: policy.TRACE_VERBOSE,
	}

	repo := testNewPolicyRepository()
	repo.AddList(rules)
	// Because search context did not contain port-specific policy, deny is
	// expected.
	c.Assert(repo.AllowsEgressRLocked(&ctx), Equals, api.Denied)

	epSelector := api.NewESFromLabels(toEndpoints...)
	cachedEPSelector, _ := repo.GetSelectorCache().AddIdentitySelector(dummySelectorCacheUser, epSelector)
	defer func() { repo.GetSelectorCache().RemoveSelector(cachedEPSelector, dummySelectorCacheUser) }()

	egressL4Policy, err := repo.ResolveL4EgressPolicy(&ctx)
	c.Assert(egressL4Policy, Not(IsNil))
	c.Assert(err, IsNil)
	c.Assert(egressL4Policy, checker.DeepEquals, policy.L4PolicyMap{
		"80/TCP": {
			Port: 80, Protocol: api.ProtoTCP, U8Proto: 6,
			L7Parser:           policy.ParserTypeNone,
			L7RulesPerSelector: policy.L7DataMap{cachedEPSelector: nil},
			Ingress:            false,
			DerivedFromRules: []labels.LabelArray{
				labels.ParseLabelArray(
					"k8s:"+k8sConst.PolicyLabelName,
					"k8s:"+k8sConst.PolicyLabelUID,
					"k8s:"+k8sConst.PolicyLabelNamespace+"=default",
					"k8s:"+k8sConst.PolicyLabelDerivedFrom+"="+resourceTypeNetworkPolicy,
				),
			},
		},
	})
	egressL4Policy.Detach(repo.GetSelectorCache())

	ctx.From = labels.LabelArray{
		labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s),
	}

	// ctx.From needs to have all labels from the policy in order to be accepted
	c.Assert(repo.AllowsEgressRLocked(&ctx), Not(Equals), api.Allowed)

	ctx = policy.SearchContext{
		To: labels.LabelArray{
			labels.NewLabel("foo3", "bar3", labels.LabelSourceK8s),
		},
		From: labels.LabelArray{
			labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s),
			labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}

	// ctx.To also needs to have all labels from the policy in order to be accepted.
	c.Assert(repo.AllowsEgressRLocked(&ctx), Not(Equals), api.Allowed)
}

func parseAndAddRules(c *C, p *slim_networkingv1.NetworkPolicy) *policy.Repository {
	repo := testNewPolicyRepository()
	rules, err := ParseNetworkPolicy(p)
	c.Assert(err, IsNil)
	rev := repo.GetRevision()
	_, id := repo.AddList(rules)
	c.Assert(id, Equals, rev+1)

	return repo
}

func (s *K8sSuite) TestParseNetworkPolicyEgressAllowAll(c *C) {
	repo := parseAndAddRules(c, &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: labelSelectorA,
			Egress: []slim_networkingv1.NetworkPolicyEgressRule{
				{
					To: []slim_networkingv1.NetworkPolicyPeer{},
				},
			},
		},
	})

	c.Assert(repo.AllowsEgressRLocked(&ctxAToB), Equals, api.Allowed)
	c.Assert(repo.AllowsEgressRLocked(&ctxAToC), Equals, api.Allowed)

	ctxAToC80 := ctxAToC
	ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}}
	c.Assert(repo.AllowsEgressRLocked(&ctxAToC80), Equals, api.Allowed)

	ctxAToC90 := ctxAToC
	ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}}
	c.Assert(repo.AllowsEgressRLocked(&ctxAToC90), Equals, api.Allowed)
}

func (s *K8sSuite) TestParseNetworkPolicyEgressL4AllowAll(c *C) {
	repo := parseAndAddRules(c, &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: labelSelectorA,
			Egress: []slim_networkingv1.NetworkPolicyEgressRule{
				{
					Ports: []slim_networkingv1.NetworkPolicyPort{port80},
					To:    []slim_networkingv1.NetworkPolicyPeer{},
				},
			},
		},
	})

	ctxAToC80 := ctxAToC
	ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}}
	c.Assert(repo.AllowsEgressRLocked(&ctxAToC80), Equals, api.Allowed)

	ctxAToC90 := ctxAToC
	ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}}
	c.Assert(repo.AllowsEgressRLocked(&ctxAToC90), Equals, api.Denied)
}

func (s *K8sSuite) TestParseNetworkPolicyIngressAllowAll(c *C) {
	repo := parseAndAddRules(c, &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: labelSelectorC,
			Ingress: []slim_networkingv1.NetworkPolicyIngressRule{
				{
					From: []slim_networkingv1.NetworkPolicyPeer{},
				},
			},
		},
	})

	c.Assert(repo.AllowsIngressRLocked(&ctxAToB), Equals, api.Denied)
	c.Assert(repo.AllowsIngressRLocked(&ctxAToC), Equals, api.Allowed)

	ctxAToC80 := ctxAToC
	ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}}
	c.Assert(repo.AllowsIngressRLocked(&ctxAToC80), Equals, api.Allowed)

	ctxAToC90 := ctxAToC
	ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}}
	c.Assert(repo.AllowsIngressRLocked(&ctxAToC90), Equals, api.Allowed)
}

func (s *K8sSuite) TestParseNetworkPolicyIngressL4AllowAll(c *C) {
	repo := parseAndAddRules(c, &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: labelSelectorC,
			Ingress: []slim_networkingv1.NetworkPolicyIngressRule{
				{
					Ports: []slim_networkingv1.NetworkPolicyPort{port80},
					From:  []slim_networkingv1.NetworkPolicyPeer{},
				},
			},
		},
	})

	c.Assert(repo.AllowsIngressRLocked(&ctxAToB), Equals, api.Denied)

	ctxAToC80 := ctxAToC
	ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}}
	c.Assert(repo.AllowsIngressRLocked(&ctxAToC80), Equals, api.Allowed)

	ctxAToC90 := ctxAToC
	ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}}
	c.Assert(repo.AllowsIngressRLocked(&ctxAToC90), Equals, api.Denied)
}

func (s *K8sSuite) TestParseNetworkPolicyNamedPort(c *C) {
	netPolicy := &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			Ingress: []slim_networkingv1.NetworkPolicyIngressRule{
				{
					Ports: []slim_networkingv1.NetworkPolicyPort{
						{
							Port: &intstr.IntOrString{
								Type:   intstr.String,
								StrVal: "port-80",
							},
						},
					},
				},
			},
		},
	}

	rules, err := ParseNetworkPolicy(netPolicy)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)
}

func (s *K8sSuite) TestParseNetworkPolicyUnknownProto(c *C) {
	unknownProtocol := slim_corev1.Protocol("unknown")
	netPolicy := &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			Ingress: []slim_networkingv1.NetworkPolicyIngressRule{
				{
					Ports: []slim_networkingv1.NetworkPolicyPort{
						{
							Port: &intstr.IntOrString{
								Type:   intstr.String,
								StrVal: "port-80",
							},
							Protocol: &unknownProtocol,
						},
					},
				},
			},
		},
	}

	rules, err := ParseNetworkPolicy(netPolicy)
	c.Assert(err, Not(IsNil))
	c.Assert(len(rules), Equals, 0)
}

func (s *K8sSuite) TestParseNetworkPolicyEmptyFrom(c *C) {
	// From missing, all sources should be allowed
	netPolicy1 := &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: slim_metav1.LabelSelector{
				MatchLabels: map[string]string{
					"foo1": "bar1",
				},
			},
			Ingress: []slim_networkingv1.NetworkPolicyIngressRule{
				{},
			},
		},
	}

	rules, err := ParseNetworkPolicy(netPolicy1)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	ctx := policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("foo0", "bar0", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}

	repo := testNewPolicyRepository()
	repo.AddList(rules)
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	// Empty From rules, all sources should be allowed
	netPolicy2 := &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: slim_metav1.LabelSelector{
				MatchLabels: map[string]string{
					"foo1": "bar1",
				},
			},
			Ingress: []slim_networkingv1.NetworkPolicyIngressRule{
				{
					From:  []slim_networkingv1.NetworkPolicyPeer{},
					Ports: []slim_networkingv1.NetworkPolicyPort{},
				},
			},
		},
	}

	rules, err = ParseNetworkPolicy(netPolicy2)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)
	repo = testNewPolicyRepository()
	repo.AddList(rules)
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)
}

func (s *K8sSuite) TestParseNetworkPolicyDenyAll(c *C) {
	// From missing, all sources should be allowed
	netPolicy1 := &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: slim_metav1.LabelSelector{
				MatchLabels: map[string]string{},
			},
		},
	}

	rules, err := ParseNetworkPolicy(netPolicy1)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	ctx := policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("foo0", "bar0", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}

	repo := testNewPolicyRepository()
	repo.AddList(rules)
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied)
}

func (s *K8sSuite) TestParseNetworkPolicyNoIngress(c *C) {
	netPolicy := &slim_networkingv1.NetworkPolicy{
		Spec: slim_networkingv1.NetworkPolicySpec{
			PodSelector: slim_metav1.LabelSelector{
				MatchLabels: map[string]string{
					"foo1": "bar1",
					"foo2": "bar2",
				},
			},
		},
	}

	rules, err := ParseNetworkPolicy(netPolicy)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)
}

func (s *K8sSuite) TestNetworkPolicyExamples(c *C) {
	// Example 1a: Only allow traffic from frontend pods on TCP port 6379 to
	// backend pods in the same namespace `myns`
	ex1 := []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/v1beta1",
  "metadata": {
    "name": "allow-frontend",
    "namespace": "myns"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "role": "backend"
      }
    },
    "ingress": [
      {
        "from": [
          {
            "podSelector": {
              "matchLabels": {
                "role": "frontend"
              }
            }
          }
        ],
        "ports": [
          {
            "protocol": "TCP",
            "port": 6379
          }
        ]
      }
    ]
  }
}`)
	np := slim_networkingv1.NetworkPolicy{}
	err := json.Unmarshal(ex1, &np)
	c.Assert(err, IsNil)

	_, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)

	// Example 1b: Only allow traffic from frontend pods to backend pods
	// in the same namespace `myns`
	ex1 = []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/networkingv1",
  "metadata": {
    "name": "allow-frontend",
    "namespace": "myns"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "role": "backend"
      }
    },
    "ingress": [
      {
        "from": [
          {
            "podSelector": {
              "matchLabels": {
                "role": "frontend"
              }
            }
          }
        ]
      },{
        "ports": [
          {
            "protocol": "TCP",
            "port": 6379
          }
        ]
      }
    ]
  }
}`)
	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex1, &np)
	c.Assert(err, IsNil)

	rules, err := ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	repo := testNewPolicyRepository()
	repo.AddList(rules)
	ctx := policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s),
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel("role", "backend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Doesn't share the same namespace
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel("role", "backend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Doesn't share the same namespace
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s),
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s),
			labels.NewLabel("role", "backend", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Port:     6379,
				Protocol: models.PortProtocolTCP,
			},
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT sense the traffic needs to come from `frontend` AND
	// port 6379 and belong to the same namespace `myns`.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	// Example 2a: Allow TCP 443 from any source in Bob's namespaces.
	ex2 := []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/v1beta1",
  "metadata": {
    "name": "allow-tcp-443"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "role": "frontend"
      }
    },
    "ingress": [
      {
        "ports": [
          {
            "protocol": "TCP",
            "port": 443
          }
        ],
        "from": [
          {
            "namespaceSelector": {
              "matchLabels": {
                "user": "bob"
              }
            }
          }
        ]
      }
    ]
  }
}`)

	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex2, &np)
	c.Assert(err, IsNil)

	_, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)

	// Example 2b: Allow from any source in Bob's namespaces.
	ex2 = []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/networkingv1",
  "metadata": {
    "name": "allow-tcp-443"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "role": "frontend"
      }
    },
    "ingress": [
      {
        "ports": [
          {
            "protocol": "TCP",
            "port": 443
          }
        ],
        "from": [
          {
            "namespaceSelector": {
              "matchLabels": {
                "user": "bob"
              }
            }
          }
        ]
      }
    ]
  }
}`)

	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex2, &np)
	c.Assert(err, IsNil)

	rules, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	repo = testNewPolicyRepository()
	repo.AddList(rules)
	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}

	// Should be DENY sense the traffic needs to come from
	// namespace `user=bob` AND port 443.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied)

	l4Policy, err := repo.ResolveL4IngressPolicy(&ctx)
	c.Assert(l4Policy, Not(IsNil))
	c.Assert(err, IsNil)
	l4Policy.Detach(repo.GetSelectorCache())

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Port:     443,
				Protocol: models.PortProtocolTCP,
			},
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT sense the traffic comes from Bob's namespaces
	// (even if it's a different namespace than `default`) AND port 443.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	// Example 3: Allow all traffic to all pods in this namespace.
	ex3 := []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/v1beta1",
  "metadata": {
    "name": "allow-all"
  },
  "spec": {
    "podSelector": null,
    "ingress": [
      {
      }
    ]
  }
}`)

	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex3, &np)
	c.Assert(err, IsNil)

	rules, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	repo = testNewPolicyRepository()
	repo.AddList(rules)
	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "backend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT since it's going to `default` namespace
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "backend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT since it's coming from `default` and going to `default` ns
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s),
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "backend", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Port:     443,
				Protocol: models.PortProtocolTCP,
			},
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT since it's coming from `default` and going to `default` namespace.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	// Example 4a: Example 4 is similar to example 2 but we will add both network
	// policies to see if the rules are additive for the same podSelector.
	ex4 := []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/v1beta1",
  "metadata": {
    "name": "allow-tcp-8080"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "role": "frontend"
      }
    },
    "ingress": [
      {
        "ports": [
          {
            "protocol": "UDP",
            "port": 8080
          }
        ],
        "from": [
          {
            "namespaceSelector": {
              "matchLabels": {
                "user": "bob"
              }
            }
          }
        ]
      }
    ]
  }
}`)

	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex4, &np)
	c.Assert(err, IsNil)

	rules, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	// Example 4b: Example 4 is similar to example 2 but we will add both network
	// policies to see if the rules are additive for the same podSelector.
	ex4 = []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/networkingv1",
  "metadata": {
    "name": "allow-tcp-8080"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "role": "frontend"
      }
    },
    "ingress": [
      {
        "ports": [
          {
            "protocol": "UDP",
            "port": 8080
          }
        ]
      },{
        "from": [
          {
            "namespaceSelector": {
              "matchLabels": {
                "user": "bob"
              }
            }
          }
        ]
      }
    ]
  }
}`)

	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex4, &np)
	c.Assert(err, IsNil)

	rules, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)

	repo = testNewPolicyRepository()
	// add example 4
	repo.AddList(rules)

	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex2, &np)
	c.Assert(err, IsNil)

	rules, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	// add example 2
	repo.AddList(rules)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Protocol: models.PortProtocolUDP,
				Port:     8080,
			},
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT sense traffic comes from Bob's namespaces AND port 8080 as specified in `ex4`.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Port:     443,
				Protocol: models.PortProtocolTCP,
			},
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT sense traffic comes from Bob's namespaces AND port 443 as specified in `ex2`.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "alice", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Protocol: models.PortProtocolUDP,
				Port:     8080,
			},
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, slim_metav1.NamespaceDefault, labels.LabelSourceK8s),
			labels.NewLabel("role", "frontend", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT despite coming from Alice's namespaces since it's port 8080 as specified in `ex4`.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	// Example 5: Some policies with match expressions.
	ex5 := []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/v1beta1",
  "metadata": {
    "name": "allow-tcp-8080",
    "namespace": "expressions"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "component": "redis"
      },
      "matchExpressions": [
        {
          "key": "tier",
          "operator": "In",
          "values": [
            "cache"
          ]
        },
        {
          "key": "environment",
          "operator": "NotIn",
          "values": [
            "dev"
          ]
        }
      ]
    },
    "ingress": [
      {
        "ports": [
          {
            "protocol": "UDP",
            "port": 8080
          }
        ],
        "from": [
          {
            "namespaceSelector": {
              "matchLabels": {
                "component": "redis"
              },
              "matchExpressions": [
                {
                  "key": "tier",
                  "operator": "In",
                  "values": [
                    "cache"
                  ]
                },
                {
                  "key": "environment",
                  "operator": "NotIn",
                  "values": [
                    "dev"
                  ]
                }
              ]
            }
          }
        ]
      }
    ]
  }
}`)

	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex5, &np)
	c.Assert(err, IsNil)

	rules, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(len(rules), Equals, 1)
	repo.AddList(rules)

	// A reminder: from the kubernetes network policy spec:
	// namespaceSelector:
	//  Selects Namespaces using cluster scoped-labels.  This
	//  matches all pods in all namespaces selected by this label selector.
	//  This field follows standard label selector semantics.
	//  If omitted, this selector selects no namespaces.
	//  If present but empty, this selector selects all namespaces.
	ctx = policy.SearchContext{
		From: labels.LabelArray{
			// doesn't matter the namespace.
			labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s),
			// component==redis is in the policy
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "component"), "redis", labels.LabelSourceK8s),
			// tier==cache is in the policy
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "tier"), "cache", labels.LabelSourceK8s),
			// environment is not in `dev` which is in the policy
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "environment"), "production", labels.LabelSourceK8s),
			// doesn't matter, there isn't any matchExpression denying traffic from any zone.
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "zone"), "eu-1", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Port:     8080,
				Protocol: models.PortProtocolUDP,
			},
		},
		To: labels.LabelArray{
			// Namespace needs to be in `expressions` since the policy is being enforced for that namespace.
			labels.NewLabel(k8sConst.PodNamespaceLabel, "expressions", labels.LabelSourceK8s),
			// component==redis is in the policy.
			labels.NewLabel("component", "redis", labels.LabelSourceK8s),
			// tier==cache is in the policy
			labels.NewLabel("tier", "cache", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT since the SearchContext is being covered by the rules.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)

	ctx.To = labels.LabelArray{
		// Namespace needs to be in `expressions` since the policy is being enforced for that namespace.
		labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s),
		// component==redis is in the policy.
		labels.NewLabel("component", "redis", labels.LabelSourceK8s),
		// tier==cache is in the policy
		labels.NewLabel("tier", "cache", labels.LabelSourceK8s),
	}
	// Should be DENY since the namespace doesn't belong to the policy.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "component"), "redis", labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "tier"), "cache", labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "environment"), "dev", labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "zone"), "eu-1", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Port:     8080,
				Protocol: models.PortProtocolUDP,
			},
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, "expressions", labels.LabelSourceK8s),
			labels.NewLabel("component", "redis", labels.LabelSourceK8s),
			labels.NewLabel("tier", "cache", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be DENY since the environment is from dev.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied)

	ctx = policy.SearchContext{
		From: labels.LabelArray{
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "component"), "redis", labels.LabelSourceK8s),
			labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "tier"), "cache", labels.LabelSourceK8s),
		},
		DPorts: []*models.Port{
			{
				Port:     8080,
				Protocol: models.PortProtocolUDP,
			},
		},
		To: labels.LabelArray{
			labels.NewLabel(k8sConst.PodNamespaceLabel, "expressions", labels.LabelSourceK8s),
			labels.NewLabel("component", "redis", labels.LabelSourceK8s),
			labels.NewLabel("tier", "cache", labels.LabelSourceK8s),
		},
		Trace: policy.TRACE_VERBOSE,
	}
	// Should be ACCEPT since the environment is from dev.
	c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed)
}

func (s *K8sSuite) TestCIDRPolicyExamples(c *C) {
	ex1 := []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/networkingv1",
  "metadata": {
    "name": "ingress-cidr-test",
    "namespace": "myns"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "role": "backend"
      }
    },
    "ingress": [
      {
        "from": [
          {
            "namespaceSelector": {
              "matchLabels": {
                "user": "bob"
              }
            }
          }
        ]
      }, {
        "from": [
          {
            "ipBlock": {
              "cidr": "10.0.0.0/8",
	          "except": [
	            "10.96.0.0/12"
	          ]
            }
          }
        ]
      }
    ]
  }
}`)
	np := slim_networkingv1.NetworkPolicy{}
	err := json.Unmarshal(ex1, &np)
	c.Assert(err, IsNil)

	rules, err := ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(rules, NotNil)
	c.Assert(len(rules), Equals, 1)
	c.Assert(len(rules[0].Ingress), Equals, 2)

	ex2 := []byte(`{
  "kind": "NetworkPolicy",
  "apiVersion": "extensions/networkingv1",
  "metadata": {
    "name": "ingress-cidr-test",
    "namespace": "myns"
  },
  "spec": {
    "podSelector": {
      "matchLabels": {
        "role": "backend"
      }
    },
    "egress": [
      {
        "to": [
          {
            "ipBlock": {
              "cidr": "10.0.0.0/8",
	          "except": [
	            "10.96.0.0/12", "10.255.255.254/32"
	          ]
            }
          },
	      {
            "ipBlock": {
              "cidr": "11.0.0.0/8",
	          "except": [
	            "11.96.0.0/12", "11.255.255.254/32"
	          ]
            }
          }
        ]
      }
    ]
  }
}`)

	np = slim_networkingv1.NetworkPolicy{}
	err = json.Unmarshal(ex2, &np)
	c.Assert(err, IsNil)

	rules, err = ParseNetworkPolicy(&np)
	c.Assert(err, IsNil)
	c.Assert(rules, NotNil)
	c.Assert(len(rules), Equals, 1)
	c.Assert(rules[0].Egress[0].ToCIDRSet[0].Cidr, Equals, api.CIDR("10.0.0.0/8"))

	expectedCIDRs := []api.CIDR{"10.96.0.0/12", "10.255.255.254/32"}
	for k, v := range rules[0].Egress[0].ToCIDRSet[0].ExceptCIDRs {
		c.Assert(v, Equals, expectedCIDRs[k])
	}

	expectedCIDRs = []api.CIDR{"11.96.0.0/12", "11.255.255.254/32"}
	for k, v := range rules[0].Egress[1].ToCIDRSet[0].ExceptCIDRs {
		c.Assert(v, Equals, expectedCIDRs[k])
	}

	c.Assert(len(rules[0].Egress), Equals, 2)

}

func getSelectorPointer(sel api.EndpointSelector) *api.EndpointSelector {
	return &sel
}

func Test_parseNetworkPolicyPeer(t *testing.T) {
	type args struct {
		namespace string
		peer      *slim_networkingv1.NetworkPolicyPeer
	}
	tests := []struct {
		name string
		args args
		want *api.EndpointSelector
	}{
		{
			name: "peer-with-pod-selector",
			args: args{
				namespace: "foo-namespace",
				peer: &slim_networkingv1.NetworkPolicyPeer{
					PodSelector: &slim_metav1.LabelSelector{
						MatchLabels: map[string]string{
							"foo": "bar",
						},
						MatchExpressions: []slim_metav1.LabelSelectorRequirement{
							{
								Key:      "foo",
								Operator: slim_metav1.LabelSelectorOpIn,
								Values:   []string{"bar", "baz"},
							},
						},
					},
				},
			},
			want: getSelectorPointer(
				api.NewESFromMatchRequirements(
					map[string]string{
						"k8s.foo":                         "bar",
						"k8s.io.kubernetes.pod.namespace": "foo-namespace",
					},
					[]slim_metav1.LabelSelectorRequirement{
						{
							Key:      "k8s.foo",
							Operator: slim_metav1.LabelSelectorOpIn,
							Values:   []string{"bar", "baz"},
						},
					},
				),
			),
		},
		{
			name: "peer-nil",
			args: args{
				namespace: "foo-namespace",
			},
			want: nil,
		},
		{
			name: "peer-with-pod-selector-and-ns-selector",
			args: args{
				namespace: "foo-namespace",
				peer: &slim_networkingv1.NetworkPolicyPeer{
					PodSelector: &slim_metav1.LabelSelector{
						MatchLabels: map[string]string{
							"foo": "bar",
						},
						MatchExpressions: []slim_metav1.LabelSelectorRequirement{
							{
								Key:      "foo",
								Operator: slim_metav1.LabelSelectorOpIn,
								Values:   []string{"bar", "baz"},
							},
						},
					},
					NamespaceSelector: &slim_metav1.LabelSelector{
						MatchLabels: map[string]string{
							"ns-foo": "ns-bar",
						},
						MatchExpressions: []slim_metav1.LabelSelectorRequirement{
							{
								Key:      "ns-foo-expression",
								Operator: slim_metav1.LabelSelectorOpExists,
							},
						},
					},
				},
			},
			want: getSelectorPointer(
				api.NewESFromMatchRequirements(
					map[string]string{
						"k8s.foo": "bar",
						"k8s.io.cilium.k8s.namespace.labels.ns-foo": "ns-bar",
					},
					[]slim_metav1.LabelSelectorRequirement{
						{
							Key:      "k8s.io.cilium.k8s.namespace.labels.ns-foo-expression",
							Operator: slim_metav1.LabelSelectorOpExists,
						},
						{
							Key:      "k8s.foo",
							Operator: slim_metav1.LabelSelectorOpIn,
							Values:   []string{"bar", "baz"},
						},
					},
				),
			),
		},
		{
			name: "peer-with-ns-selector",
			args: args{
				namespace: "foo-namespace",
				peer: &slim_networkingv1.NetworkPolicyPeer{
					NamespaceSelector: &slim_metav1.LabelSelector{
						MatchLabels: map[string]string{
							"ns-foo": "ns-bar",
						},
						MatchExpressions: []slim_metav1.LabelSelectorRequirement{
							{
								Key:      "ns-foo-expression",
								Operator: slim_metav1.LabelSelectorOpExists,
							},
						},
					},
				},
			},
			want: getSelectorPointer(
				api.NewESFromMatchRequirements(
					map[string]string{
						"k8s.io.cilium.k8s.namespace.labels.ns-foo": "ns-bar",
					},
					[]slim_metav1.LabelSelectorRequirement{
						{
							Key:      "k8s.io.cilium.k8s.namespace.labels.ns-foo-expression",
							Operator: slim_metav1.LabelSelectorOpExists,
						},
					},
				),
			),
		},
		{
			name: "peer-with-allow-all-ns-selector",
			args: args{
				namespace: "foo-namespace",
				peer: &slim_networkingv1.NetworkPolicyPeer{
					NamespaceSelector: &slim_metav1.LabelSelector{},
				},
			},
			want: getSelectorPointer(
				api.NewESFromMatchRequirements(
					map[string]string{},
					[]slim_metav1.LabelSelectorRequirement{
						{
							Key:      fmt.Sprintf("%s.%s", labels.LabelSourceK8s, k8sConst.PodNamespaceLabel),
							Operator: slim_metav1.LabelSelectorOpExists,
						},
					},
				),
			),
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := parseNetworkPolicyPeer(tt.args.namespace, tt.args.peer)
			args := []interface{}{got, tt.want}
			names := []string{"obtained", "expected"}
			if equal, err := checker.DeepEquals.Check(args, names); !equal {
				t.Errorf("Failed to parseNetworkPolicyPeer():\n%s", err)
			}
		})
	}
}

func (s *K8sSuite) TestGetPolicyLabelsv1(c *C) {
	uuid := "1bba160-ddca-11e8-b697-0800273b04ff"
	tests := []struct {
		np          *slim_networkingv1.NetworkPolicy // input network policy
		name        string                           // expected extracted name
		namespace   string                           // expected extracted namespace
		uuid        string                           // expected extracted uuid
		derivedFrom string                           // expected extracted derived
	}{
		{
			np:          &slim_networkingv1.NetworkPolicy{},
			name:        "",
			namespace:   slim_metav1.NamespaceDefault,
			uuid:        "",
			derivedFrom: resourceTypeNetworkPolicy,
		},
		{
			np: &slim_networkingv1.NetworkPolicy{
				ObjectMeta: slim_metav1.ObjectMeta{
					Annotations: map[string]string{
						annotation.Name: "foo",
					},
				},
			},
			name:        "foo",
			uuid:        "",
			namespace:   slim_metav1.NamespaceDefault,
			derivedFrom: resourceTypeNetworkPolicy,
		},
		{
			np: &slim_networkingv1.NetworkPolicy{
				ObjectMeta: slim_metav1.ObjectMeta{
					Name:      "foo",
					Namespace: "bar",
					UID:       types.UID(uuid),
				},
			},
			name:        "foo",
			namespace:   "bar",
			uuid:        uuid,
			derivedFrom: resourceTypeNetworkPolicy,
		},
	}

	assertLabel := func(lbl labels.Label, key, value string) {
		c.Assert(lbl.Key, Equals, key)
		c.Assert(lbl.Value, Equals, value)
		c.Assert(lbl.Source, Equals, labels.LabelSourceK8s)
	}

	for _, tt := range tests {
		lbls := GetPolicyLabelsv1(tt.np)

		c.Assert(lbls, NotNil)
		c.Assert(len(lbls), Equals, 4, Commentf(
			"Incorrect number of labels: Expected DerivedFrom, Name, Namespace and UID labels."))
		assertLabel(lbls[0], "io.cilium.k8s.policy.derived-from", tt.derivedFrom)
		assertLabel(lbls[1], "io.cilium.k8s.policy.name", tt.name)
		assertLabel(lbls[2], "io.cilium.k8s.policy.namespace", tt.namespace)
		assertLabel(lbls[3], "io.cilium.k8s.policy.uid", tt.uuid)
	}
}

func (s *K8sSuite) TestIPBlockToCIDRRule(c *C) {
	blocks := []*slim_networkingv1.IPBlock{
		{},
		{CIDR: "192.168.1.1/24"},
		{CIDR: "192.168.1.1/24", Except: []string{}},
		{CIDR: "192.168.1.1/24", Except: []string{"192.168.1.1/28"}},
		{
			CIDR: "192.168.1.1/24",
			Except: []string{
				"192.168.1.1/30",
				"192.168.1.1/26",
				"192.168.1.1/28",
			},
		},
	}

	for _, block := range blocks {
		cidrRule := ipBlockToCIDRRule(block)

		exceptCIDRs := make([]api.CIDR, len(block.Except))
		for i, v := range block.Except {
			exceptCIDRs[i] = api.CIDR(v)
		}

		c.Assert(cidrRule.Generated, Equals, false)
		c.Assert(cidrRule.Cidr, Equals, api.CIDR(block.CIDR))

		if block.Except == nil || len(block.Except) == 0 {
			c.Assert(cidrRule.ExceptCIDRs, IsNil)
		} else {
			c.Assert(cidrRule.ExceptCIDRs, checker.DeepEquals, exceptCIDRs)
		}
	}
}